| /******************************************************************************* |
| * Copyright (c) 2000, 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler; |
| |
| import java.io.*; |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.core.compiler.IProblem; |
| import org.eclipse.jdt.internal.compiler.ast.*; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.codegen.*; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.impl.Constant; |
| import org.eclipse.jdt.internal.compiler.impl.StringConstant; |
| import org.eclipse.jdt.internal.compiler.lookup.*; |
| import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; |
| import org.eclipse.jdt.internal.compiler.util.Messages; |
| |
| /** |
| * 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 TypeConstants, TypeIds { |
| public static final int INITIAL_CONTENTS_SIZE = 400; |
| public static final int INITIAL_HEADER_SIZE = 1500; |
| public static final int INNER_CLASSES_SIZE = 5; |
| |
| /** |
| * 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(Messages.bind(Messages.output_isFile, f.getAbsolutePath())); |
| throw new IOException(Messages.output_isFileNotDirectory); |
| } |
| } else { |
| // we have to create that directory |
| if (!f.mkdirs()) { |
| System.out.println(Messages.bind(Messages.output_dirName, f.getAbsolutePath())); |
| throw new IOException(Messages.output_notValidAll); |
| } |
| } |
| 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(Messages.bind(Messages.output_fileName, f.getName())); |
| throw new IOException(Messages.output_notValid); |
| } |
| } |
| token = tokenizer.nextToken(); |
| } |
| // token contains the last one |
| return outDir.append(token).toString(); |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Request the creation of a ClassFile compatible representation of a problematic type |
| * |
| * @param typeDeclaration org.eclipse.jdt.internal.compiler.ast.TypeDeclaration |
| * @param unitResult org.eclipse.jdt.internal.compiler.CompilationUnitResult |
| */ |
| public static void createProblemType( |
| TypeDeclaration typeDeclaration, |
| CompilationResult unitResult) { |
| SourceTypeBinding typeBinding = typeDeclaration.binding; |
| ClassFile classFile = new ClassFile(typeBinding, null, true); |
| |
| // TODO (olivier) handle cases where a field cannot be generated (name too long) |
| // TODO (olivier) handle too many methods |
| // inner attributes |
| if (typeBinding.isMemberType()) |
| classFile.recordEnclosingTypeAttributes(typeBinding); |
| |
| // add its fields |
| FieldBinding[] fields = typeBinding.fields; |
| if ((fields != null) && (fields != NoFields)) { |
| for (int i = 0, max = fields.length; i < max; i++) { |
| if (fields[i].constant() == null) { |
| FieldReference.getConstantFor(fields[i], null, false, null); |
| } |
| } |
| classFile.addFieldInfos(); |
| } else { |
| // we have to set the number of fields to be equals to 0 |
| classFile.contents[classFile.contentsOffset++] = 0; |
| classFile.contents[classFile.contentsOffset++] = 0; |
| } |
| // leave some space for the methodCount |
| classFile.setForMethodInfos(); |
| // add its user defined methods |
| MethodBinding[] methods = typeBinding.methods; |
| AbstractMethodDeclaration[] methodDeclarations = typeDeclaration.methods; |
| int maxMethodDecl = methodDeclarations == null ? 0 : methodDeclarations.length; |
| int problemsLength; |
| IProblem[] problems = unitResult.getErrors(); |
| if (problems == null) { |
| problems = new IProblem[0]; |
| } |
| IProblem[] problemsCopy = new IProblem[problemsLength = problems.length]; |
| System.arraycopy(problems, 0, problemsCopy, 0, problemsLength); |
| if (methods != null) { |
| if (typeBinding.isInterface()) { |
| // we cannot create problem methods for an interface. So we have to generate a clinit |
| // which should contain all the problem |
| classFile.addProblemClinit(problemsCopy); |
| for (int i = 0, max = methods.length; i < max; i++) { |
| MethodBinding methodBinding; |
| if ((methodBinding = methods[i]) != null) { |
| // find the corresponding method declaration |
| for (int j = 0; j < maxMethodDecl; j++) { |
| if ((methodDeclarations[j] != null) |
| && (methodDeclarations[j].binding == methods[i])) { |
| if (!methodBinding.isConstructor()) { |
| classFile.addAbstractMethod(methodDeclarations[j], methodBinding); |
| } |
| break; |
| } |
| } |
| } |
| } |
| } else { |
| for (int i = 0, max = methods.length; i < max; i++) { |
| MethodBinding methodBinding; |
| if ((methodBinding = methods[i]) != null) { |
| // find the corresponding method declaration |
| for (int j = 0; j < maxMethodDecl; j++) { |
| if ((methodDeclarations[j] != null) |
| && (methodDeclarations[j].binding == methods[i])) { |
| AbstractMethodDeclaration methodDecl; |
| if ((methodDecl = methodDeclarations[j]).isConstructor()) { |
| classFile.addProblemConstructor(methodDecl, methodBinding, problemsCopy); |
| } else { |
| classFile.addProblemMethod(methodDecl, methodBinding, problemsCopy); |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |
| // add abstract methods |
| classFile.addDefaultAbstractMethods(); |
| } |
| // propagate generation of (problem) member types |
| if (typeDeclaration.memberTypes != null) { |
| for (int i = 0, max = typeDeclaration.memberTypes.length; i < max; i++) { |
| TypeDeclaration memberType = typeDeclaration.memberTypes[i]; |
| if (memberType.binding != null) { |
| classFile.recordNestedMemberAttribute(memberType.binding); |
| ClassFile.createProblemType(memberType, unitResult); |
| } |
| } |
| } |
| classFile.addAttributes(); |
| unitResult.record(typeBinding.constantPoolName(), classFile); |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Search the line number corresponding to a specific position |
| */ |
| public static final int searchLineNumber( |
| int[] startLineIndexes, |
| int position) { |
| // this code is completely useless, but it is the same implementation than |
| // org.eclipse.jdt.internal.compiler.problem.ProblemHandler.searchLineNumber(int[], int) |
| // if (startLineIndexes == null) |
| // return 1; |
| int length = startLineIndexes.length; |
| if (length == 0) |
| return 1; |
| int g = 0, d = length - 1; |
| int m = 0; |
| while (g <= d) { |
| m = (g + d) / 2; |
| if (position < startLineIndexes[m]) { |
| d = m - 1; |
| } else |
| if (position > startLineIndexes[m]) { |
| g = m + 1; |
| } else { |
| return m + 1; |
| } |
| } |
| if (position < startLineIndexes[m]) { |
| return m + 1; |
| } |
| return m + 2; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * 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(); |
| } |
| } |
| public CodeStream codeStream; |
| public ConstantPool constantPool; |
| public int constantPoolOffset; |
| // the header contains all the bytes till the end of the constant pool |
| public byte[] contents; |
| public int contentsOffset; |
| protected boolean creatingProblemType; |
| public ClassFile enclosingClassFile; |
| public byte[] header; |
| // that collection contains all the remaining bytes of the .class file |
| public int headerOffset; |
| public ReferenceBinding[] innerClassesBindings; |
| public int methodCount; |
| public int methodCountOffset; |
| public int numberOfInnerClasses; |
| public boolean ownSharedArrays = false; // flag set when header/contents are set to shared arrays |
| // used to generate private access methods |
| public int produceDebugAttributes; |
| public SourceTypeBinding referenceBinding; |
| public long targetJDK; |
| |
| /** |
| * INTERNAL USE-ONLY |
| * This methods creates a new instance of the receiver. |
| */ |
| public ClassFile() { |
| // default constructor for subclasses |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * This methods creates a new instance of the receiver. |
| * |
| * @param aType org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding |
| * @param enclosingClassFile org.eclipse.jdt.internal.compiler.ClassFile |
| * @param creatingProblemType <CODE>boolean</CODE> |
| */ |
| public ClassFile( |
| SourceTypeBinding aType, |
| ClassFile enclosingClassFile, |
| boolean creatingProblemType) { |
| |
| this.referenceBinding = aType; |
| initByteArrays(); |
| |
| // generate the magic numbers inside the header |
| header[headerOffset++] = (byte) (0xCAFEBABEL >> 24); |
| header[headerOffset++] = (byte) (0xCAFEBABEL >> 16); |
| header[headerOffset++] = (byte) (0xCAFEBABEL >> 8); |
| header[headerOffset++] = (byte) (0xCAFEBABEL >> 0); |
| |
| final CompilerOptions options = aType.scope.compilerOptions(); |
| this.targetJDK = options.targetJDK; |
| header[headerOffset++] = (byte) (this.targetJDK >> 8); // minor high |
| header[headerOffset++] = (byte) (this.targetJDK >> 0); // minor low |
| header[headerOffset++] = (byte) (this.targetJDK >> 24); // major high |
| header[headerOffset++] = (byte) (this.targetJDK >> 16); // major low |
| |
| constantPoolOffset = headerOffset; |
| headerOffset += 2; |
| constantPool = new ConstantPool(this); |
| |
| // Modifier manipulations for classfile |
| int accessFlags = aType.getAccessFlags(); |
| if (aType.isPrivate()) { // rewrite private to non-public |
| accessFlags &= ~ClassFileConstants.AccPublic; |
| } |
| if (aType.isProtected()) { // rewrite protected into public |
| accessFlags |= ClassFileConstants.AccPublic; |
| } |
| // clear all bits that are illegal for a class or an interface |
| accessFlags |
| &= ~( |
| ClassFileConstants.AccStrictfp |
| | ClassFileConstants.AccProtected |
| | ClassFileConstants.AccPrivate |
| | ClassFileConstants.AccStatic |
| | ClassFileConstants.AccSynchronized |
| | ClassFileConstants.AccNative); |
| |
| // set the AccSuper flag (has to be done after clearing AccSynchronized - since same value) |
| if (!aType.isInterface()) { // class or enum |
| accessFlags |= ClassFileConstants.AccSuper; |
| } |
| |
| this.enclosingClassFile = enclosingClassFile; |
| // innerclasses get their names computed at code gen time |
| |
| // now we continue to generate the bytes inside the contents array |
| contents[contentsOffset++] = (byte) (accessFlags >> 8); |
| contents[contentsOffset++] = (byte) accessFlags; |
| int classNameIndex = constantPool.literalIndexForType(aType.constantPoolName()); |
| contents[contentsOffset++] = (byte) (classNameIndex >> 8); |
| contents[contentsOffset++] = (byte) classNameIndex; |
| int superclassNameIndex; |
| if (aType.isInterface()) { |
| superclassNameIndex = constantPool.literalIndexForType(ConstantPool.JavaLangObjectConstantPoolName); |
| } else { |
| superclassNameIndex = |
| (aType.superclass == null ? 0 : constantPool.literalIndexForType(aType.superclass.constantPoolName())); |
| } |
| contents[contentsOffset++] = (byte) (superclassNameIndex >> 8); |
| contents[contentsOffset++] = (byte) superclassNameIndex; |
| ReferenceBinding[] superInterfacesBinding = aType.superInterfaces(); |
| int interfacesCount = superInterfacesBinding.length; |
| contents[contentsOffset++] = (byte) (interfacesCount >> 8); |
| contents[contentsOffset++] = (byte) interfacesCount; |
| for (int i = 0; i < interfacesCount; i++) { |
| int interfaceIndex = constantPool.literalIndexForType(superInterfacesBinding[i].constantPoolName()); |
| contents[contentsOffset++] = (byte) (interfaceIndex >> 8); |
| contents[contentsOffset++] = (byte) interfaceIndex; |
| } |
| produceDebugAttributes = options.produceDebugAttributes; |
| innerClassesBindings = new ReferenceBinding[INNER_CLASSES_SIZE]; |
| this.creatingProblemType = creatingProblemType; |
| codeStream = new CodeStream(this, this.targetJDK); |
| |
| // retrieve the enclosing one guaranteed to be the one matching the propagated flow info |
| // 1FF9ZBU: LFCOM:ALL - Local variable attributes busted (Sanity check) |
| ClassFile outermostClassFile = this.outerMostEnclosingClassFile(); |
| if (this == outermostClassFile) { |
| codeStream.maxFieldCount = aType.scope.referenceType().maxFieldCount; |
| } else { |
| codeStream.maxFieldCount = outermostClassFile.codeStream.maxFieldCount; |
| } |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Generate the byte for a problem method info that correspond to a boggus method. |
| * |
| * @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration |
| * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding |
| */ |
| public void addAbstractMethod( |
| AbstractMethodDeclaration method, |
| MethodBinding methodBinding) { |
| |
| // force the modifiers to be public and abstract |
| methodBinding.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract; |
| |
| this.generateMethodInfoHeader(methodBinding); |
| int methodAttributeOffset = this.contentsOffset; |
| int attributeNumber = this.generateMethodInfoAttribute(methodBinding); |
| this.completeMethodInfo(methodAttributeOffset, attributeNumber); |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * This methods generate all the attributes for the receiver. |
| * For a class they could be: |
| * - source file attribute |
| * - inner classes attribute |
| * - deprecated attribute |
| */ |
| public void addAttributes() { |
| // update the method count |
| contents[methodCountOffset++] = (byte) (methodCount >> 8); |
| contents[methodCountOffset] = (byte) methodCount; |
| |
| int attributeNumber = 0; |
| // leave two bytes for the number of attributes and store the current offset |
| int attributeOffset = contentsOffset; |
| contentsOffset += 2; |
| |
| // source attribute |
| if ((produceDebugAttributes & CompilerOptions.Source) != 0) { |
| String fullFileName = |
| new String(referenceBinding.scope.referenceCompilationUnit().getFileName()); |
| fullFileName = fullFileName.replace('\\', '/'); |
| int lastIndex = fullFileName.lastIndexOf('/'); |
| if (lastIndex != -1) { |
| fullFileName = fullFileName.substring(lastIndex + 1, fullFileName.length()); |
| } |
| // check that there is enough space to write all the bytes for the field info corresponding |
| // to the @fieldBinding |
| if (contentsOffset + 8 >= contents.length) { |
| resizeContents(8); |
| } |
| int sourceAttributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.SourceName); |
| contents[contentsOffset++] = (byte) (sourceAttributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) sourceAttributeNameIndex; |
| // The length of a source file attribute is 2. This is a fixed-length |
| // attribute |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 2; |
| // write the source file name |
| int fileNameIndex = constantPool.literalIndex(fullFileName.toCharArray()); |
| contents[contentsOffset++] = (byte) (fileNameIndex >> 8); |
| contents[contentsOffset++] = (byte) fileNameIndex; |
| attributeNumber++; |
| } |
| // Deprecated attribute |
| if (referenceBinding.isDeprecated()) { |
| // check that there is enough space to write all the bytes for the field info corresponding |
| // to the @fieldBinding |
| if (contentsOffset + 6 >= contents.length) { |
| resizeContents(6); |
| } |
| int deprecatedAttributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.DeprecatedName); |
| contents[contentsOffset++] = (byte) (deprecatedAttributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) deprecatedAttributeNameIndex; |
| // the length of a deprecated attribute is equals to 0 |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| attributeNumber++; |
| } |
| // Inner class attribute |
| if (numberOfInnerClasses != 0) { |
| // Generate the inner class attribute |
| int exSize = 8 * numberOfInnerClasses + 8; |
| if (exSize + contentsOffset >= this.contents.length) { |
| resizeContents(exSize); |
| } |
| // Now we now the size of the attribute and the number of entries |
| // attribute name |
| int attributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.InnerClassName); |
| contents[contentsOffset++] = (byte) (attributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) attributeNameIndex; |
| int value = (numberOfInnerClasses << 3) + 2; |
| contents[contentsOffset++] = (byte) (value >> 24); |
| contents[contentsOffset++] = (byte) (value >> 16); |
| contents[contentsOffset++] = (byte) (value >> 8); |
| contents[contentsOffset++] = (byte) value; |
| contents[contentsOffset++] = (byte) (numberOfInnerClasses >> 8); |
| contents[contentsOffset++] = (byte) numberOfInnerClasses; |
| for (int i = 0; i < numberOfInnerClasses; i++) { |
| ReferenceBinding innerClass = innerClassesBindings[i]; |
| int accessFlags = innerClass.getAccessFlags(); |
| int innerClassIndex = constantPool.literalIndexForType(innerClass.constantPoolName()); |
| // 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.literalIndexForType(innerClass.enclosingType().constantPoolName()); |
| 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 |= ClassFileConstants.AccPrivate; |
| } else if (innerClass.isLocalType() && !innerClass.isMemberType()) { |
| accessFlags |= ClassFileConstants.AccPrivate; |
| } else if (innerClass.isMemberType() && innerClass.isInterface()) { |
| accessFlags |= ClassFileConstants.AccStatic; // implicitely static |
| } |
| contents[contentsOffset++] = (byte) (accessFlags >> 8); |
| contents[contentsOffset++] = (byte) accessFlags; |
| } |
| attributeNumber++; |
| } |
| // add signature attribute |
| char[] genericSignature = referenceBinding.genericSignature(); |
| if (genericSignature != null) { |
| // check that there is enough space to write all the bytes for the field info corresponding |
| // to the @fieldBinding |
| if (contentsOffset + 8 >= contents.length) { |
| resizeContents(8); |
| } |
| int signatureAttributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.SignatureName); |
| contents[contentsOffset++] = (byte) (signatureAttributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) signatureAttributeNameIndex; |
| // the length of a signature attribute is equals to 2 |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 2; |
| int signatureIndex = |
| constantPool.literalIndex(genericSignature); |
| contents[contentsOffset++] = (byte) (signatureIndex >> 8); |
| contents[contentsOffset++] = (byte) signatureIndex; |
| attributeNumber++; |
| } |
| if (targetJDK >= ClassFileConstants.JDK1_5 |
| && this.referenceBinding.isNestedType() |
| && !this.referenceBinding.isMemberType()) { |
| // add enclosing method attribute (1.5 mode only) |
| if (contentsOffset + 10 >= contents.length) { |
| resizeContents(10); |
| } |
| int enclosingMethodAttributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.EnclosingMethodName); |
| contents[contentsOffset++] = (byte) (enclosingMethodAttributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) enclosingMethodAttributeNameIndex; |
| // the length of a signature attribute is equals to 2 |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 4; |
| |
| int enclosingTypeIndex = constantPool.literalIndexForType(this.referenceBinding.enclosingType().constantPoolName()); |
| contents[contentsOffset++] = (byte) (enclosingTypeIndex >> 8); |
| contents[contentsOffset++] = (byte) enclosingTypeIndex; |
| byte methodIndexByte1 = 0; |
| byte methodIndexByte2 = 0; |
| if (this.referenceBinding instanceof LocalTypeBinding) { |
| MethodBinding methodBinding = ((LocalTypeBinding) this.referenceBinding).enclosingMethod; |
| if (methodBinding != null) { |
| int enclosingMethodIndex = constantPool.literalIndexForNameAndType(methodBinding.selector, methodBinding.signature()); |
| methodIndexByte1 = (byte) (enclosingMethodIndex >> 8); |
| methodIndexByte2 = (byte) enclosingMethodIndex; |
| } |
| } |
| contents[contentsOffset++] = methodIndexByte1; |
| contents[contentsOffset++] = methodIndexByte2; |
| attributeNumber++; |
| } |
| if (this.targetJDK >= ClassFileConstants.JDK1_5 && !this.creatingProblemType) { |
| TypeDeclaration typeDeclaration = referenceBinding.scope.referenceContext; |
| if (typeDeclaration != null) { |
| final Annotation[] annotations = typeDeclaration.annotations; |
| if (annotations != null) { |
| attributeNumber += generateRuntimeAnnotations(annotations); |
| } |
| } |
| } |
| // update the number of attributes |
| if (attributeOffset + 2 >= this.contents.length) { |
| resizeContents(2); |
| } |
| contents[attributeOffset++] = (byte) (attributeNumber >> 8); |
| contents[attributeOffset] = (byte) attributeNumber; |
| |
| // resynchronize all offsets of the classfile |
| header = constantPool.poolContent; |
| headerOffset = constantPool.currentOffset; |
| int constantPoolCount = constantPool.currentIndex; |
| header[constantPoolOffset++] = (byte) (constantPoolCount >> 8); |
| header[constantPoolOffset] = (byte) constantPoolCount; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * This methods generate all the default abstract method infos that correpond to |
| * the abstract methods inherited from superinterfaces. |
| */ |
| public void addDefaultAbstractMethods() { // default abstract methods |
| MethodBinding[] defaultAbstractMethods = |
| referenceBinding.getDefaultAbstractMethods(); |
| for (int i = 0, max = defaultAbstractMethods.length; i < max; i++) { |
| generateMethodInfoHeader(defaultAbstractMethods[i]); |
| int methodAttributeOffset = contentsOffset; |
| int attributeNumber = generateMethodInfoAttribute(defaultAbstractMethods[i]); |
| completeMethodInfo(methodAttributeOffset, attributeNumber); |
| } |
| } |
| |
| private int addFieldAttributes(FieldBinding fieldBinding, int fieldAttributeOffset) { |
| int attributesNumber = 0; |
| // 4.7.2 only static constant fields get a ConstantAttribute |
| // Generate the constantValueAttribute |
| if (fieldBinding.isConstantValue()){ |
| if (contentsOffset + 8 >= contents.length) { |
| resizeContents(8); |
| } |
| // Now we generate the constant attribute corresponding to the fieldBinding |
| int constantValueNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.ConstantValueName); |
| contents[contentsOffset++] = (byte) (constantValueNameIndex >> 8); |
| contents[contentsOffset++] = (byte) constantValueNameIndex; |
| // The attribute length = 2 in case of a constantValue attribute |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 2; |
| attributesNumber++; |
| // Need to add the constant_value_index |
| Constant fieldConstant = fieldBinding.constant(); |
| switch (fieldConstant.typeID()) { |
| case T_boolean : |
| int booleanValueIndex = |
| constantPool.literalIndex(fieldConstant.booleanValue() ? 1 : 0); |
| contents[contentsOffset++] = (byte) (booleanValueIndex >> 8); |
| contents[contentsOffset++] = (byte) booleanValueIndex; |
| break; |
| case T_byte : |
| case T_char : |
| case T_int : |
| case T_short : |
| int integerValueIndex = |
| constantPool.literalIndex(fieldConstant.intValue()); |
| contents[contentsOffset++] = (byte) (integerValueIndex >> 8); |
| contents[contentsOffset++] = (byte) integerValueIndex; |
| break; |
| case T_float : |
| int floatValueIndex = |
| constantPool.literalIndex(fieldConstant.floatValue()); |
| contents[contentsOffset++] = (byte) (floatValueIndex >> 8); |
| contents[contentsOffset++] = (byte) floatValueIndex; |
| break; |
| case T_double : |
| int doubleValueIndex = |
| constantPool.literalIndex(fieldConstant.doubleValue()); |
| contents[contentsOffset++] = (byte) (doubleValueIndex >> 8); |
| contents[contentsOffset++] = (byte) doubleValueIndex; |
| break; |
| case T_long : |
| int longValueIndex = |
| constantPool.literalIndex(fieldConstant.longValue()); |
| contents[contentsOffset++] = (byte) (longValueIndex >> 8); |
| contents[contentsOffset++] = (byte) longValueIndex; |
| break; |
| case T_JavaLangString : |
| int stringValueIndex = |
| constantPool.literalIndex( |
| ((StringConstant) fieldConstant).stringValue()); |
| if (stringValueIndex == -1) { |
| if (!creatingProblemType) { |
| // report an error and abort: will lead to a problem type classfile creation |
| TypeDeclaration typeDeclaration = referenceBinding.scope.referenceContext; |
| FieldDeclaration[] fieldDecls = typeDeclaration.fields; |
| for (int i = 0, max = fieldDecls.length; i < max; i++) { |
| if (fieldDecls[i].binding == fieldBinding) { |
| // problem should abort |
| typeDeclaration.scope.problemReporter().stringConstantIsExceedingUtf8Limit( |
| fieldDecls[i]); |
| } |
| } |
| } else { |
| // already inside a problem type creation : no constant for this field |
| contentsOffset = fieldAttributeOffset; |
| } |
| } else { |
| contents[contentsOffset++] = (byte) (stringValueIndex >> 8); |
| contents[contentsOffset++] = (byte) stringValueIndex; |
| } |
| } |
| } |
| if (this.targetJDK < ClassFileConstants.JDK1_5 && fieldBinding.isSynthetic()) { |
| if (contentsOffset + 6 >= contents.length) { |
| resizeContents(6); |
| } |
| int syntheticAttributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.SyntheticName); |
| contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; |
| // the length of a synthetic attribute is equals to 0 |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| attributesNumber++; |
| } |
| if (fieldBinding.isDeprecated()) { |
| if (contentsOffset + 6 >= contents.length) { |
| resizeContents(6); |
| } |
| int deprecatedAttributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.DeprecatedName); |
| contents[contentsOffset++] = (byte) (deprecatedAttributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) deprecatedAttributeNameIndex; |
| // the length of a deprecated attribute is equals to 0 |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| attributesNumber++; |
| } |
| // add signature attribute |
| char[] genericSignature = fieldBinding.genericSignature(); |
| if (genericSignature != null) { |
| // check that there is enough space to write all the bytes for the field info corresponding |
| // to the @fieldBinding |
| if (contentsOffset + 8 >= contents.length) { |
| resizeContents(8); |
| } |
| int signatureAttributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.SignatureName); |
| contents[contentsOffset++] = (byte) (signatureAttributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) signatureAttributeNameIndex; |
| // the length of a signature attribute is equals to 2 |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 2; |
| int signatureIndex = |
| constantPool.literalIndex(genericSignature); |
| contents[contentsOffset++] = (byte) (signatureIndex >> 8); |
| contents[contentsOffset++] = (byte) signatureIndex; |
| attributesNumber++; |
| } |
| if (this.targetJDK >= ClassFileConstants.JDK1_5 && !this.creatingProblemType) { |
| FieldDeclaration fieldDeclaration = fieldBinding.sourceField(); |
| if (fieldDeclaration != null) { |
| Annotation[] annotations = fieldDeclaration.annotations; |
| if (annotations != null) { |
| attributesNumber += generateRuntimeAnnotations(annotations); |
| } |
| } |
| } |
| return attributesNumber; |
| } |
| /** |
| * INTERNAL USE-ONLY |
| * This methods generates the bytes for the given field binding |
| * @param fieldBinding the given field binding |
| */ |
| private void addFieldInfo(FieldBinding fieldBinding) { |
| // check that there is enough space to write all the bytes for the field info corresponding |
| // to the @fieldBinding |
| if (contentsOffset + 8 >= contents.length) { |
| resizeContents(8); |
| } |
| // Now we can generate all entries into the byte array |
| // First the accessFlags |
| int accessFlags = fieldBinding.getAccessFlags(); |
| if (targetJDK < ClassFileConstants.JDK1_5) { |
| // pre 1.5, synthetic was an attribute, not a modifier |
| accessFlags &= ~ClassFileConstants.AccSynthetic; |
| } |
| contents[contentsOffset++] = (byte) (accessFlags >> 8); |
| contents[contentsOffset++] = (byte) accessFlags; |
| // Then the nameIndex |
| int nameIndex = constantPool.literalIndex(fieldBinding.name); |
| contents[contentsOffset++] = (byte) (nameIndex >> 8); |
| contents[contentsOffset++] = (byte) nameIndex; |
| // Then the descriptorIndex |
| int descriptorIndex = constantPool.literalIndex(fieldBinding.type.signature()); |
| contents[contentsOffset++] = (byte) (descriptorIndex >> 8); |
| contents[contentsOffset++] = (byte) descriptorIndex; |
| int fieldAttributeOffset = contentsOffset; |
| int attributeNumber = 0; |
| // leave some space for the number of attributes |
| contentsOffset += 2; |
| attributeNumber += addFieldAttributes(fieldBinding, fieldAttributeOffset); |
| 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) |
| */ |
| /** |
| * INTERNAL USE-ONLY |
| * This methods generate all the fields infos for the receiver. |
| * This includes: |
| * - a field info for each defined field of that class |
| * - a field info for each synthetic field (e.g. this$0) |
| */ |
| public void addFieldInfos() { |
| SourceTypeBinding currentBinding = referenceBinding; |
| FieldBinding[] syntheticFields = currentBinding.syntheticFields(); |
| int fieldCount = |
| currentBinding.fieldCount() |
| + (syntheticFields == null ? 0 : syntheticFields.length); |
| |
| // write the number of fields |
| if (fieldCount > 0xFFFF) { |
| referenceBinding.scope.problemReporter().tooManyFields(referenceBinding.scope.referenceType()); |
| } |
| contents[contentsOffset++] = (byte) (fieldCount >> 8); |
| contents[contentsOffset++] = (byte) fieldCount; |
| |
| FieldBinding[] fieldBindings = currentBinding.fields(); |
| for (int i = 0, max = fieldBindings.length; i < max; i++) { |
| addFieldInfo(fieldBindings[i]); |
| } |
| if (syntheticFields != null) { |
| for (int i = 0, max = syntheticFields.length; i < max; i++) { |
| addFieldInfo(syntheticFields[i]); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * This methods stores the bindings for each inner class. They will be used to know which entries |
| * have to be generated for the inner classes attributes. |
| * @param refBinding org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding |
| */ |
| private void addInnerClasses(ReferenceBinding refBinding) { |
| // check first if that reference binding is there |
| for (int i = 0; i < numberOfInnerClasses; i++) { |
| if (innerClassesBindings[i] == refBinding) |
| return; |
| } |
| int length = innerClassesBindings.length; |
| if (numberOfInnerClasses == length) { |
| System.arraycopy( |
| innerClassesBindings, |
| 0, |
| innerClassesBindings = new ReferenceBinding[length * 2], |
| 0, |
| length); |
| } |
| innerClassesBindings[numberOfInnerClasses++] = refBinding; |
| } |
| |
| private void addMissingAbstractProblemMethod(MethodDeclaration methodDeclaration, MethodBinding methodBinding, IProblem problem, CompilationResult compilationResult) { |
| // always clear the strictfp/native/abstract bit for a problem method |
| generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(ClassFileConstants.AccStrictfp | ClassFileConstants.AccNative | ClassFileConstants.AccAbstract)); |
| int methodAttributeOffset = contentsOffset; |
| int attributeNumber = generateMethodInfoAttribute(methodBinding); |
| |
| // Code attribute |
| attributeNumber++; |
| |
| int codeAttributeOffset = contentsOffset; |
| generateCodeAttributeHeader(); |
| StringBuffer buffer = new StringBuffer(25); |
| buffer.append("\t" + problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$ |
| buffer.insert(0, Messages.compilation_unresolvedProblem); |
| String problemString = buffer.toString(); |
| |
| codeStream.init(this); |
| codeStream.preserveUnusedLocals = true; |
| codeStream.initializeMaxLocals(methodBinding); |
| |
| // return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "") |
| codeStream.generateCodeAttributeForProblemMethod(problemString); |
| |
| completeCodeAttributeForMissingAbstractProblemMethod( |
| methodBinding, |
| codeAttributeOffset, |
| compilationResult.getLineSeparatorPositions(), |
| problem.getSourceLineNumber()); |
| |
| completeMethodInfo(methodAttributeOffset, attributeNumber); |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Generate the byte for a problem clinit method info that correspond to a boggus method. |
| * |
| * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[] |
| */ |
| public void addProblemClinit(IProblem[] problems) { |
| generateMethodInfoHeaderForClinit(); |
| // leave two spaces for the number of attributes |
| contentsOffset -= 2; |
| int attributeOffset = contentsOffset; |
| contentsOffset += 2; |
| int attributeNumber = 0; |
| |
| int codeAttributeOffset = contentsOffset; |
| generateCodeAttributeHeader(); |
| codeStream.resetForProblemClinit(this); |
| String problemString = "" ; //$NON-NLS-1$ |
| int problemLine = 0; |
| if (problems != null) { |
| int max = problems.length; |
| StringBuffer buffer = new StringBuffer(25); |
| int count = 0; |
| for (int i = 0; i < max; i++) { |
| IProblem problem = problems[i]; |
| if ((problem != null) && (problem.isError())) { |
| buffer.append("\t" +problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$ |
| count++; |
| if (problemLine == 0) { |
| problemLine = problem.getSourceLineNumber(); |
| } |
| problems[i] = null; |
| } |
| } // insert the top line afterwards, once knowing how many problems we have to consider |
| if (count > 1) { |
| buffer.insert(0, Messages.compilation_unresolvedProblems); |
| } else { |
| buffer.insert(0, Messages.compilation_unresolvedProblem); |
| } |
| problemString = buffer.toString(); |
| } |
| |
| // return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "") |
| codeStream.generateCodeAttributeForProblemMethod(problemString); |
| attributeNumber++; // code attribute |
| completeCodeAttributeForClinit( |
| codeAttributeOffset, |
| problemLine); |
| contents[attributeOffset++] = (byte) (attributeNumber >> 8); |
| contents[attributeOffset] = (byte) attributeNumber; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Generate the byte for a problem method info that correspond to a boggus constructor. |
| * |
| * @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration |
| * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding |
| * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[] |
| */ |
| public void addProblemConstructor( |
| AbstractMethodDeclaration method, |
| MethodBinding methodBinding, |
| IProblem[] problems) { |
| |
| // always clear the strictfp/native/abstract bit for a problem method |
| generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(ClassFileConstants.AccStrictfp | ClassFileConstants.AccNative | ClassFileConstants.AccAbstract)); |
| int methodAttributeOffset = contentsOffset; |
| int attributeNumber = generateMethodInfoAttribute(methodBinding, true); |
| |
| // Code attribute |
| attributeNumber++; |
| int codeAttributeOffset = contentsOffset; |
| generateCodeAttributeHeader(); |
| codeStream.reset(method, this); |
| String problemString = "" ; //$NON-NLS-1$ |
| int problemLine = 0; |
| if (problems != null) { |
| int max = problems.length; |
| StringBuffer buffer = new StringBuffer(25); |
| int count = 0; |
| for (int i = 0; i < max; i++) { |
| IProblem problem = problems[i]; |
| if ((problem != null) && (problem.isError())) { |
| buffer.append("\t" +problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$ |
| count++; |
| if (problemLine == 0) { |
| problemLine = problem.getSourceLineNumber(); |
| } |
| } |
| } // insert the top line afterwards, once knowing how many problems we have to consider |
| if (count > 1) { |
| buffer.insert(0, Messages.compilation_unresolvedProblems); |
| } else { |
| buffer.insert(0, Messages.compilation_unresolvedProblem); |
| } |
| problemString = buffer.toString(); |
| } |
| |
| // return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "") |
| codeStream.generateCodeAttributeForProblemMethod(problemString); |
| completeCodeAttributeForProblemMethod( |
| method, |
| methodBinding, |
| codeAttributeOffset, |
| ((SourceTypeBinding) methodBinding.declaringClass) |
| .scope |
| .referenceCompilationUnit() |
| .compilationResult |
| .getLineSeparatorPositions(), |
| problemLine); |
| completeMethodInfo(methodAttributeOffset, attributeNumber); |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Generate the byte for a problem method info that correspond to a boggus constructor. |
| * Reset the position inside the contents byte array to the savedOffset. |
| * |
| * @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration |
| * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding |
| * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[] |
| * @param savedOffset <CODE>int</CODE> |
| */ |
| public void addProblemConstructor( |
| AbstractMethodDeclaration method, |
| MethodBinding methodBinding, |
| IProblem[] problems, |
| int savedOffset) { |
| // we need to move back the contentsOffset to the value at the beginning of the method |
| contentsOffset = savedOffset; |
| methodCount--; // we need to remove the method that causes the problem |
| addProblemConstructor(method, methodBinding, problems); |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Generate the byte for a problem method info that correspond to a boggus method. |
| * |
| * @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration |
| * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding |
| * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[] |
| */ |
| public void addProblemMethod( |
| AbstractMethodDeclaration method, |
| MethodBinding methodBinding, |
| IProblem[] problems) { |
| if (methodBinding.isAbstract() && methodBinding.declaringClass.isInterface()) { |
| method.abort(ProblemSeverities.AbortType, null); |
| } |
| // always clear the strictfp/native/abstract bit for a problem method |
| generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(ClassFileConstants.AccStrictfp | ClassFileConstants.AccNative | ClassFileConstants.AccAbstract)); |
| int methodAttributeOffset = contentsOffset; |
| int attributeNumber = generateMethodInfoAttribute(methodBinding, true); |
| |
| // Code attribute |
| attributeNumber++; |
| |
| int codeAttributeOffset = contentsOffset; |
| generateCodeAttributeHeader(); |
| codeStream.reset(method, this); |
| String problemString = "" ; //$NON-NLS-1$ |
| int problemLine = 0; |
| if (problems != null) { |
| int max = problems.length; |
| StringBuffer buffer = new StringBuffer(25); |
| int count = 0; |
| for (int i = 0; i < max; i++) { |
| IProblem problem = problems[i]; |
| if ((problem != null) |
| && (problem.isError()) |
| && (problem.getSourceStart() >= method.declarationSourceStart) |
| && (problem.getSourceEnd() <= method.declarationSourceEnd)) { |
| buffer.append("\t" +problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$ |
| count++; |
| if (problemLine == 0) { |
| problemLine = problem.getSourceLineNumber(); |
| } |
| problems[i] = null; |
| } |
| } // insert the top line afterwards, once knowing how many problems we have to consider |
| if (count > 1) { |
| buffer.insert(0, Messages.compilation_unresolvedProblems); |
| } else { |
| buffer.insert(0, Messages.compilation_unresolvedProblem); |
| } |
| problemString = buffer.toString(); |
| } |
| |
| // return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "") |
| codeStream.generateCodeAttributeForProblemMethod(problemString); |
| completeCodeAttributeForProblemMethod( |
| method, |
| methodBinding, |
| codeAttributeOffset, |
| ((SourceTypeBinding) methodBinding.declaringClass) |
| .scope |
| .referenceCompilationUnit() |
| .compilationResult |
| .getLineSeparatorPositions(), |
| problemLine); |
| completeMethodInfo(methodAttributeOffset, attributeNumber); |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Generate the byte for a problem method info that correspond to a boggus method. |
| * Reset the position inside the contents byte array to the savedOffset. |
| * |
| * @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration |
| * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding |
| * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[] |
| * @param savedOffset <CODE>int</CODE> |
| */ |
| public void addProblemMethod( |
| AbstractMethodDeclaration method, |
| MethodBinding methodBinding, |
| IProblem[] problems, |
| int savedOffset) { |
| // we need to move back the contentsOffset to the value at the beginning of the method |
| contentsOffset = savedOffset; |
| methodCount--; // we need to remove the method that causes the problem |
| addProblemMethod(method, methodBinding, problems); |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Generate the byte for all the special method infos. |
| * They are: |
| * - synthetic access methods |
| * - default abstract methods |
| */ |
| public void addSpecialMethods() { |
| |
| // add all methods (default abstract methods and synthetic) |
| |
| // default abstract methods |
| generateMissingAbstractMethods(referenceBinding.scope.referenceType().missingAbstractMethods, referenceBinding.scope.referenceCompilationUnit().compilationResult); |
| |
| MethodBinding[] defaultAbstractMethods = this.referenceBinding.getDefaultAbstractMethods(); |
| for (int i = 0, max = defaultAbstractMethods.length; i < max; i++) { |
| generateMethodInfoHeader(defaultAbstractMethods[i]); |
| int methodAttributeOffset = contentsOffset; |
| int attributeNumber = generateMethodInfoAttribute(defaultAbstractMethods[i]); |
| completeMethodInfo(methodAttributeOffset, attributeNumber); |
| } |
| // add synthetic methods infos |
| SyntheticMethodBinding[] syntheticMethods = this.referenceBinding.syntheticMethods(); |
| if (syntheticMethods != null) { |
| for (int i = 0, max = syntheticMethods.length; i < max; i++) { |
| SyntheticMethodBinding syntheticMethod = syntheticMethods[i]; |
| switch (syntheticMethod.kind) { |
| case SyntheticMethodBinding.FieldReadAccess : |
| // generate a method info to emulate an reading access to |
| // a non-accessible field |
| addSyntheticFieldReadAccessMethod(syntheticMethod); |
| break; |
| case SyntheticMethodBinding.FieldWriteAccess : |
| // generate a method info to emulate an writing access to |
| // a non-accessible field |
| addSyntheticFieldWriteAccessMethod(syntheticMethod); |
| break; |
| case SyntheticMethodBinding.MethodAccess : |
| case SyntheticMethodBinding.SuperMethodAccess : |
| case SyntheticMethodBinding.BridgeMethod : |
| // generate a method info to emulate an access to a non-accessible method / super-method or bridge method |
| addSyntheticMethodAccessMethod(syntheticMethod); |
| break; |
| case SyntheticMethodBinding.ConstructorAccess : |
| // generate a method info to emulate an access to a non-accessible constructor |
| addSyntheticConstructorAccessMethod(syntheticMethod); |
| break; |
| case SyntheticMethodBinding.EnumValues : |
| // generate a method info to define <enum>#values() |
| addSyntheticEnumValuesMethod(syntheticMethod); |
| break; |
| case SyntheticMethodBinding.EnumValueOf : |
| // generate a method info to define <enum>#valueOf(String) |
| addSyntheticEnumValueOfMethod(syntheticMethod); |
| break; |
| case SyntheticMethodBinding.SwitchTable : |
| // generate a method info to define the switch table synthetic method |
| addSyntheticSwitchTable(syntheticMethod); |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Generate the bytes for a synthetic method that provides an access to a private constructor. |
| * |
| * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding |
| */ |
| public void addSyntheticConstructorAccessMethod(SyntheticMethodBinding methodBinding) { |
| generateMethodInfoHeader(methodBinding); |
| int methodAttributeOffset = this.contentsOffset; |
| // this will add exception attribute, synthetic attribute, deprecated attribute,... |
| int attributeNumber = generateMethodInfoAttribute(methodBinding); |
| // Code attribute |
| int codeAttributeOffset = contentsOffset; |
| attributeNumber++; // add code attribute |
| generateCodeAttributeHeader(); |
| codeStream.init(this); |
| codeStream.generateSyntheticBodyForConstructorAccess(methodBinding); |
| completeCodeAttributeForSyntheticMethod( |
| methodBinding, |
| codeAttributeOffset, |
| ((SourceTypeBinding) methodBinding.declaringClass) |
| .scope |
| .referenceCompilationUnit() |
| .compilationResult |
| .getLineSeparatorPositions()); |
| // update the number of attributes |
| contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); |
| contents[methodAttributeOffset] = (byte) attributeNumber; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Generate the bytes for a synthetic method that implements Enum#valueOf(String) for a given enum type |
| * |
| * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding |
| */ |
| public void addSyntheticEnumValueOfMethod(SyntheticMethodBinding methodBinding) { |
| generateMethodInfoHeader(methodBinding); |
| int methodAttributeOffset = this.contentsOffset; |
| // this will add exception attribute, synthetic attribute, deprecated attribute,... |
| int attributeNumber = generateMethodInfoAttribute(methodBinding); |
| // Code attribute |
| int codeAttributeOffset = contentsOffset; |
| attributeNumber++; // add code attribute |
| generateCodeAttributeHeader(); |
| codeStream.init(this); |
| codeStream.generateSyntheticBodyForEnumValueOf(methodBinding); |
| completeCodeAttributeForSyntheticMethod( |
| methodBinding, |
| codeAttributeOffset, |
| ((SourceTypeBinding) methodBinding.declaringClass) |
| .scope |
| .referenceCompilationUnit() |
| .compilationResult |
| .getLineSeparatorPositions()); |
| // update the number of attributes |
| contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); |
| contents[methodAttributeOffset] = (byte) attributeNumber; |
| } |
| |
| public void addSyntheticSwitchTable(SyntheticMethodBinding methodBinding) { |
| generateMethodInfoHeader(methodBinding); |
| int methodAttributeOffset = this.contentsOffset; |
| // this will add exception attribute, synthetic attribute, deprecated attribute,... |
| int attributeNumber = generateMethodInfoAttribute(methodBinding); |
| // Code attribute |
| int codeAttributeOffset = contentsOffset; |
| attributeNumber++; // add code attribute |
| generateCodeAttributeHeader(); |
| codeStream.init(this); |
| codeStream.generateSyntheticBodyForSwitchTable(methodBinding); |
| completeCodeAttributeForSyntheticMethod( |
| true, |
| methodBinding, |
| codeAttributeOffset, |
| ((SourceTypeBinding) methodBinding.declaringClass) |
| .scope |
| .referenceCompilationUnit() |
| .compilationResult |
| .getLineSeparatorPositions()); |
| // update the number of attributes |
| contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); |
| contents[methodAttributeOffset] = (byte) attributeNumber; |
| } |
| /** |
| * INTERNAL USE-ONLY |
| * Generate the bytes for a synthetic method that implements Enum#values() for a given enum type |
| * |
| * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding |
| */ |
| public void addSyntheticEnumValuesMethod(SyntheticMethodBinding methodBinding) { |
| generateMethodInfoHeader(methodBinding); |
| int methodAttributeOffset = this.contentsOffset; |
| // this will add exception attribute, synthetic attribute, deprecated attribute,... |
| int attributeNumber = generateMethodInfoAttribute(methodBinding); |
| // Code attribute |
| int codeAttributeOffset = contentsOffset; |
| attributeNumber++; // add code attribute |
| generateCodeAttributeHeader(); |
| codeStream.init(this); |
| codeStream.generateSyntheticBodyForEnumValues(methodBinding); |
| completeCodeAttributeForSyntheticMethod( |
| methodBinding, |
| codeAttributeOffset, |
| ((SourceTypeBinding) methodBinding.declaringClass) |
| .scope |
| .referenceCompilationUnit() |
| .compilationResult |
| .getLineSeparatorPositions()); |
| // update the number of attributes |
| contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); |
| contents[methodAttributeOffset] = (byte) attributeNumber; |
| } |
| |
| /** |
| * 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(SyntheticMethodBinding methodBinding) { |
| generateMethodInfoHeader(methodBinding); |
| int methodAttributeOffset = this.contentsOffset; |
| // this will add exception attribute, synthetic attribute, deprecated attribute,... |
| int attributeNumber = generateMethodInfoAttribute(methodBinding); |
| // Code attribute |
| int codeAttributeOffset = contentsOffset; |
| attributeNumber++; // add code attribute |
| generateCodeAttributeHeader(); |
| codeStream.init(this); |
| codeStream.generateSyntheticBodyForFieldReadAccess(methodBinding); |
| completeCodeAttributeForSyntheticMethod( |
| methodBinding, |
| codeAttributeOffset, |
| ((SourceTypeBinding) methodBinding.declaringClass) |
| .scope |
| .referenceCompilationUnit() |
| .compilationResult |
| .getLineSeparatorPositions()); |
| // update the number of attributes |
| contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); |
| contents[methodAttributeOffset] = (byte) attributeNumber; |
| } |
| |
| /** |
| * 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(SyntheticMethodBinding methodBinding) { |
| generateMethodInfoHeader(methodBinding); |
| int methodAttributeOffset = this.contentsOffset; |
| // this will add exception attribute, synthetic attribute, deprecated attribute,... |
| int attributeNumber = generateMethodInfoAttribute(methodBinding); |
| // Code attribute |
| int codeAttributeOffset = contentsOffset; |
| attributeNumber++; // add code attribute |
| generateCodeAttributeHeader(); |
| codeStream.init(this); |
| codeStream.generateSyntheticBodyForFieldWriteAccess(methodBinding); |
| completeCodeAttributeForSyntheticMethod( |
| methodBinding, |
| codeAttributeOffset, |
| ((SourceTypeBinding) methodBinding.declaringClass) |
| .scope |
| .referenceCompilationUnit() |
| .compilationResult |
| .getLineSeparatorPositions()); |
| // update the number of attributes |
| contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); |
| contents[methodAttributeOffset] = (byte) attributeNumber; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Generate the bytes for a synthetic method that provides access to a private method. |
| * |
| * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding |
| */ |
| public void addSyntheticMethodAccessMethod(SyntheticMethodBinding methodBinding) { |
| generateMethodInfoHeader(methodBinding); |
| int methodAttributeOffset = this.contentsOffset; |
| // this will add exception attribute, synthetic attribute, deprecated attribute,... |
| int attributeNumber = generateMethodInfoAttribute(methodBinding); |
| // Code attribute |
| int codeAttributeOffset = contentsOffset; |
| attributeNumber++; // add code attribute |
| generateCodeAttributeHeader(); |
| codeStream.init(this); |
| codeStream.generateSyntheticBodyForMethodAccess(methodBinding); |
| completeCodeAttributeForSyntheticMethod( |
| methodBinding, |
| codeAttributeOffset, |
| ((SourceTypeBinding) methodBinding.declaringClass) |
| .scope |
| .referenceCompilationUnit() |
| .compilationResult |
| .getLineSeparatorPositions()); |
| // update the number of attributes |
| contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); |
| contents[methodAttributeOffset] = (byte) attributeNumber; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * That method completes the creation of the code attribute by setting |
| * - the attribute_length |
| * - max_stack |
| * - max_locals |
| * - code_length |
| * - exception table |
| * - and debug attributes if necessary. |
| * |
| * @param codeAttributeOffset <CODE>int</CODE> |
| */ |
| public void completeCodeAttribute(int codeAttributeOffset) { |
| // reinitialize the localContents with the byte modified by the code stream |
| this.contents = codeStream.bCodeStream; |
| int localContentsOffset = codeStream.classFileOffset; |
| // codeAttributeOffset is the position inside localContents byte array before we started to write |
| // any information about the codeAttribute |
| // That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset |
| // to get the right position, 6 for the max_stack etc... |
| int code_length = codeStream.position; |
| if (code_length > 65535) { |
| codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit( |
| codeStream.methodDeclaration); |
| } |
| if (localContentsOffset + 20 >= this.contents.length) { |
| resizeContents(20); |
| } |
| int max_stack = codeStream.stackMax; |
| this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); |
| this.contents[codeAttributeOffset + 7] = (byte) max_stack; |
| int max_locals = codeStream.maxLocals; |
| this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); |
| this.contents[codeAttributeOffset + 9] = (byte) max_locals; |
| this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24); |
| this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16); |
| this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8); |
| this.contents[codeAttributeOffset + 13] = (byte) code_length; |
| |
| // write the exception table |
| int exceptionHandlersNumber = codeStream.exceptionHandlersCounter; |
| ExceptionLabel[] exceptionHandlers = codeStream.exceptionHandlers; |
| int exSize = exceptionHandlersNumber * 8 + 2; |
| if (exSize + localContentsOffset >= this.contents.length) { |
| resizeContents(exSize); |
| } |
| // there is no exception table, so we need to offset by 2 the current offset and move |
| // on the attribute generation |
| this.contents[localContentsOffset++] = (byte) (exceptionHandlersNumber >> 8); |
| this.contents[localContentsOffset++] = (byte) exceptionHandlersNumber; |
| for (int i = 0, max = codeStream.exceptionHandlersIndex; i < max; i++) { |
| ExceptionLabel exceptionHandler = exceptionHandlers[i]; |
| if (exceptionHandler != null) { |
| int start = exceptionHandler.start; |
| this.contents[localContentsOffset++] = (byte) (start >> 8); |
| this.contents[localContentsOffset++] = (byte) start; |
| int end = exceptionHandler.end; |
| this.contents[localContentsOffset++] = (byte) (end >> 8); |
| this.contents[localContentsOffset++] = (byte) end; |
| int handlerPC = exceptionHandler.position; |
| this.contents[localContentsOffset++] = (byte) (handlerPC >> 8); |
| this.contents[localContentsOffset++] = (byte) handlerPC; |
| if (exceptionHandler.exceptionType == null) { |
| // any exception handler |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| } else { |
| int nameIndex; |
| if (exceptionHandler.exceptionType == BaseTypes.NullBinding) { |
| /* represents ClassNotFoundException, see class literal access*/ |
| nameIndex = constantPool.literalIndexForType(ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName); |
| } else { |
| nameIndex = constantPool.literalIndexForType(exceptionHandler.exceptionType.constantPoolName()); |
| } |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| } |
| } |
| } |
| // debug attributes |
| int codeAttributeAttributeOffset = localContentsOffset; |
| int attributeNumber = 0; |
| // leave two bytes for the attribute_length |
| localContentsOffset += 2; |
| |
| // first we handle the linenumber attribute |
| if (codeStream.generateLineNumberAttributes) { |
| /* Create and add the line number attribute (used for debugging) |
| * Build the pairs of: |
| * (bytecodePC lineNumber) |
| * according to the table of start line indexes and the pcToSourceMap table |
| * contained into the codestream |
| */ |
| int[] pcToSourceMapTable; |
| if (((pcToSourceMapTable = codeStream.pcToSourceMap) != null) |
| && (codeStream.pcToSourceMapSize != 0)) { |
| int lineNumberNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName); |
| if (localContentsOffset + 8 >= this.contents.length) { |
| resizeContents(8); |
| } |
| this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) lineNumberNameIndex; |
| int lineNumberTableOffset = localContentsOffset; |
| localContentsOffset += 6; |
| // leave space for attribute_length and line_number_table_length |
| int numberOfEntries = 0; |
| int length = codeStream.pcToSourceMapSize; |
| for (int i = 0; i < length;) { |
| // write the entry |
| if (localContentsOffset + 4 >= this.contents.length) { |
| resizeContents(4); |
| } |
| int pc = pcToSourceMapTable[i++]; |
| this.contents[localContentsOffset++] = (byte) (pc >> 8); |
| this.contents[localContentsOffset++] = (byte) pc; |
| int lineNumber = pcToSourceMapTable[i++]; |
| this.contents[localContentsOffset++] = (byte) (lineNumber >> 8); |
| this.contents[localContentsOffset++] = (byte) lineNumber; |
| numberOfEntries++; |
| } |
| // now we change the size of the line number attribute |
| int lineNumberAttr_length = numberOfEntries * 4 + 2; |
| this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 24); |
| this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 16); |
| this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 8); |
| this.contents[lineNumberTableOffset++] = (byte) lineNumberAttr_length; |
| this.contents[lineNumberTableOffset++] = (byte) (numberOfEntries >> 8); |
| this.contents[lineNumberTableOffset++] = (byte) numberOfEntries; |
| attributeNumber++; |
| } |
| } |
| // then we do the local variable attribute |
| if (codeStream.generateLocalVariableTableAttributes) { |
| int localVariableTableOffset = localContentsOffset; |
| int numberOfEntries = 0; |
| int localVariableNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName); |
| final boolean methodDeclarationIsStatic = codeStream.methodDeclaration.isStatic(); |
| int maxOfEntries = 8 + 10 * (methodDeclarationIsStatic ? 0 : 1); |
| for (int i = 0; i < codeStream.allLocalsCounter; i++) { |
| maxOfEntries += 10 * codeStream.locals[i].initializationCount; |
| } |
| // reserve enough space |
| if (localContentsOffset + maxOfEntries >= this.contents.length) { |
| resizeContents(maxOfEntries); |
| } |
| this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) localVariableNameIndex; |
| localContentsOffset += 6; |
| // leave space for attribute_length and local_variable_table_length |
| int nameIndex; |
| int descriptorIndex; |
| SourceTypeBinding declaringClassBinding = null; |
| if (!methodDeclarationIsStatic) { |
| numberOfEntries++; |
| this.contents[localContentsOffset++] = 0; // the startPC for this is always 0 |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = (byte) (code_length >> 8); |
| this.contents[localContentsOffset++] = (byte) code_length; |
| nameIndex = constantPool.literalIndex(ConstantPool.This); |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| declaringClassBinding = (SourceTypeBinding) codeStream.methodDeclaration.binding.declaringClass; |
| descriptorIndex = |
| constantPool.literalIndex( |
| declaringClassBinding.signature()); |
| this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) descriptorIndex; |
| this.contents[localContentsOffset++] = 0;// the resolved position for this is always 0 |
| this.contents[localContentsOffset++] = 0; |
| } |
| // used to remember the local variable with a generic type |
| int genericLocalVariablesCounter = 0; |
| LocalVariableBinding[] genericLocalVariables = null; |
| int numberOfGenericEntries = 0; |
| |
| for (int i = 0, max = codeStream.allLocalsCounter; i < max; i++) { |
| LocalVariableBinding localVariable = codeStream.locals[i]; |
| final TypeBinding localVariableTypeBinding = localVariable.type; |
| boolean isParameterizedType = localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable(); |
| if (localVariable.initializationCount != 0 && isParameterizedType) { |
| if (genericLocalVariables == null) { |
| // we cannot have more than max locals |
| genericLocalVariables = new LocalVariableBinding[max]; |
| } |
| genericLocalVariables[genericLocalVariablesCounter++] = localVariable; |
| } |
| for (int j = 0; j < localVariable.initializationCount; j++) { |
| int startPC = localVariable.initializationPCs[j << 1]; |
| int endPC = localVariable.initializationPCs[(j << 1) + 1]; |
| if (startPC != endPC) { // only entries for non zero length |
| if (endPC == -1) { |
| localVariable.declaringScope.problemReporter().abortDueToInternalError( |
| Messages.bind(Messages.abort_invalidAttribute, new String(localVariable.name)), |
| (ASTNode) localVariable.declaringScope.methodScope().referenceContext); |
| } |
| if (isParameterizedType) { |
| numberOfGenericEntries++; |
| } |
| // now we can safely add the local entry |
| numberOfEntries++; |
| this.contents[localContentsOffset++] = (byte) (startPC >> 8); |
| this.contents[localContentsOffset++] = (byte) startPC; |
| int length = endPC - startPC; |
| this.contents[localContentsOffset++] = (byte) (length >> 8); |
| this.contents[localContentsOffset++] = (byte) length; |
| nameIndex = constantPool.literalIndex(localVariable.name); |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| descriptorIndex = constantPool.literalIndex(localVariableTypeBinding.signature()); |
| this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) descriptorIndex; |
| int resolvedPosition = localVariable.resolvedPosition; |
| this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); |
| this.contents[localContentsOffset++] = (byte) resolvedPosition; |
| } |
| } |
| } |
| int value = numberOfEntries * 10 + 2; |
| localVariableTableOffset += 2; |
| this.contents[localVariableTableOffset++] = (byte) (value >> 24); |
| this.contents[localVariableTableOffset++] = (byte) (value >> 16); |
| this.contents[localVariableTableOffset++] = (byte) (value >> 8); |
| this.contents[localVariableTableOffset++] = (byte) value; |
| this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); |
| this.contents[localVariableTableOffset] = (byte) numberOfEntries; |
| attributeNumber++; |
| |
| final boolean currentInstanceIsGeneric = |
| !methodDeclarationIsStatic |
| && declaringClassBinding != null |
| && declaringClassBinding.typeVariables != NoTypeVariables; |
| if (genericLocalVariablesCounter != 0 || currentInstanceIsGeneric) { |
| // add the local variable type table attribute |
| numberOfGenericEntries += (currentInstanceIsGeneric ? 1 : 0); |
| maxOfEntries = 8 + numberOfGenericEntries * 10; |
| // reserve enough space |
| if (localContentsOffset + maxOfEntries >= this.contents.length) { |
| resizeContents(maxOfEntries); |
| } |
| int localVariableTypeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LocalVariableTypeTableName); |
| this.contents[localContentsOffset++] = (byte) (localVariableTypeNameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) localVariableTypeNameIndex; |
| value = numberOfGenericEntries * 10 + 2; |
| this.contents[localContentsOffset++] = (byte) (value >> 24); |
| this.contents[localContentsOffset++] = (byte) (value >> 16); |
| this.contents[localContentsOffset++] = (byte) (value >> 8); |
| this.contents[localContentsOffset++] = (byte) value; |
| this.contents[localContentsOffset++] = (byte) (numberOfGenericEntries >> 8); |
| this.contents[localContentsOffset++] = (byte) numberOfGenericEntries; |
| if (currentInstanceIsGeneric) { |
| this.contents[localContentsOffset++] = 0; // the startPC for this is always 0 |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = (byte) (code_length >> 8); |
| this.contents[localContentsOffset++] = (byte) code_length; |
| nameIndex = constantPool.literalIndex(ConstantPool.This); |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| descriptorIndex = constantPool.literalIndex(declaringClassBinding.genericTypeSignature()); |
| this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) descriptorIndex; |
| this.contents[localContentsOffset++] = 0;// the resolved position for this is always 0 |
| this.contents[localContentsOffset++] = 0; |
| } |
| |
| for (int i = 0; i < genericLocalVariablesCounter; i++) { |
| LocalVariableBinding localVariable = genericLocalVariables[i]; |
| for (int j = 0; j < localVariable.initializationCount; j++) { |
| int startPC = localVariable.initializationPCs[j << 1]; |
| int endPC = localVariable.initializationPCs[(j << 1) + 1]; |
| if (startPC != endPC) { |
| // only entries for non zero length |
| // now we can safely add the local entry |
| this.contents[localContentsOffset++] = (byte) (startPC >> 8); |
| this.contents[localContentsOffset++] = (byte) startPC; |
| int length = endPC - startPC; |
| this.contents[localContentsOffset++] = (byte) (length >> 8); |
| this.contents[localContentsOffset++] = (byte) length; |
| nameIndex = constantPool.literalIndex(localVariable.name); |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| descriptorIndex = constantPool.literalIndex(localVariable.type.genericTypeSignature()); |
| this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) descriptorIndex; |
| int resolvedPosition = localVariable.resolvedPosition; |
| this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); |
| this.contents[localContentsOffset++] = (byte) resolvedPosition; |
| } |
| } |
| } |
| attributeNumber++; |
| } |
| } |
| // update the number of attributes |
| // ensure first that there is enough space available inside the localContents array |
| if (codeAttributeAttributeOffset + 2 >= this.contents.length) { |
| resizeContents(2); |
| } |
| this.contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); |
| this.contents[codeAttributeAttributeOffset] = (byte) attributeNumber; |
| |
| // update the attribute length |
| int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6); |
| this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); |
| this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); |
| this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); |
| this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength; |
| contentsOffset = localContentsOffset; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * That method completes the creation of the code attribute by setting |
| * - the attribute_length |
| * - max_stack |
| * - max_locals |
| * - code_length |
| * - exception table |
| * - and debug attributes if necessary. |
| * |
| * @param codeAttributeOffset <CODE>int</CODE> |
| */ |
| public void completeCodeAttributeForClinit(int codeAttributeOffset) { |
| // reinitialize the contents with the byte modified by the code stream |
| this.contents = codeStream.bCodeStream; |
| int localContentsOffset = codeStream.classFileOffset; |
| // codeAttributeOffset is the position inside contents byte array before we started to write |
| // any information about the codeAttribute |
| // That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset |
| // to get the right position, 6 for the max_stack etc... |
| int code_length = codeStream.position; |
| if (code_length > 65535) { |
| codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit( |
| codeStream.methodDeclaration.scope.referenceType()); |
| } |
| if (localContentsOffset + 20 >= this.contents.length) { |
| resizeContents(20); |
| } |
| int max_stack = codeStream.stackMax; |
| this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); |
| this.contents[codeAttributeOffset + 7] = (byte) max_stack; |
| int max_locals = codeStream.maxLocals; |
| this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); |
| this.contents[codeAttributeOffset + 9] = (byte) max_locals; |
| this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24); |
| this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16); |
| this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8); |
| this.contents[codeAttributeOffset + 13] = (byte) code_length; |
| |
| // write the exception table |
| int exceptionHandlersNumber = codeStream.exceptionHandlersCounter; |
| ExceptionLabel[] exceptionHandlers = codeStream.exceptionHandlers; |
| int exSize = exceptionHandlersNumber * 8 + 2; |
| if (exSize + localContentsOffset >= this.contents.length) { |
| resizeContents(exSize); |
| } |
| // there is no exception table, so we need to offset by 2 the current offset and move |
| // on the attribute generation |
| this.contents[localContentsOffset++] = (byte) (exceptionHandlersNumber >> 8); |
| this.contents[localContentsOffset++] = (byte) exceptionHandlersNumber; |
| for (int i = 0, max = codeStream.exceptionHandlersIndex; i < max; i++) { |
| ExceptionLabel exceptionHandler = exceptionHandlers[i]; |
| if (exceptionHandler != null) { |
| int start = exceptionHandler.start; |
| this.contents[localContentsOffset++] = (byte) (start >> 8); |
| this.contents[localContentsOffset++] = (byte) start; |
| int end = exceptionHandler.end; |
| this.contents[localContentsOffset++] = (byte) (end >> 8); |
| this.contents[localContentsOffset++] = (byte) end; |
| int handlerPC = exceptionHandler.position; |
| this.contents[localContentsOffset++] = (byte) (handlerPC >> 8); |
| this.contents[localContentsOffset++] = (byte) handlerPC; |
| if (exceptionHandler.exceptionType == null) { |
| // any exception handler |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| } else { |
| int nameIndex; |
| if (exceptionHandler.exceptionType == BaseTypes.NullBinding) { |
| /* represents denote ClassNotFoundException, see class literal access*/ |
| nameIndex = constantPool.literalIndexForType(ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName); |
| } else { |
| nameIndex = constantPool.literalIndexForType(exceptionHandler.exceptionType.constantPoolName()); |
| } |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| } |
| } |
| } |
| // debug attributes |
| int codeAttributeAttributeOffset = localContentsOffset; |
| int attributeNumber = 0; |
| // leave two bytes for the attribute_length |
| localContentsOffset += 2; |
| |
| // first we handle the linenumber attribute |
| if (codeStream.generateLineNumberAttributes) { |
| /* Create and add the line number attribute (used for debugging) |
| * Build the pairs of: |
| * (bytecodePC lineNumber) |
| * according to the table of start line indexes and the pcToSourceMap table |
| * contained into the codestream |
| */ |
| int[] pcToSourceMapTable; |
| if (((pcToSourceMapTable = codeStream.pcToSourceMap) != null) |
| && (codeStream.pcToSourceMapSize != 0)) { |
| int lineNumberNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName); |
| if (localContentsOffset + 8 >= this.contents.length) { |
| resizeContents(8); |
| } |
| this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) lineNumberNameIndex; |
| int lineNumberTableOffset = localContentsOffset; |
| localContentsOffset += 6; |
| // leave space for attribute_length and line_number_table_length |
| int numberOfEntries = 0; |
| int length = codeStream.pcToSourceMapSize; |
| for (int i = 0; i < length;) { |
| // write the entry |
| if (localContentsOffset + 4 >= this.contents.length) { |
| resizeContents(4); |
| } |
| int pc = pcToSourceMapTable[i++]; |
| this.contents[localContentsOffset++] = (byte) (pc >> 8); |
| this.contents[localContentsOffset++] = (byte) pc; |
| int lineNumber = pcToSourceMapTable[i++]; |
| this.contents[localContentsOffset++] = (byte) (lineNumber >> 8); |
| this.contents[localContentsOffset++] = (byte) lineNumber; |
| numberOfEntries++; |
| } |
| // now we change the size of the line number attribute |
| int lineNumberAttr_length = numberOfEntries * 4 + 2; |
| this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 24); |
| this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 16); |
| this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 8); |
| this.contents[lineNumberTableOffset++] = (byte) lineNumberAttr_length; |
| this.contents[lineNumberTableOffset++] = (byte) (numberOfEntries >> 8); |
| this.contents[lineNumberTableOffset++] = (byte) numberOfEntries; |
| attributeNumber++; |
| } |
| } |
| // then we do the local variable attribute |
| if (codeStream.generateLocalVariableTableAttributes) { |
| int localVariableTableOffset = localContentsOffset; |
| int numberOfEntries = 0; |
| // codeAttribute.addLocalVariableTableAttribute(this); |
| if ((codeStream.pcToSourceMap != null) |
| && (codeStream.pcToSourceMapSize != 0)) { |
| int localVariableNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName); |
| if (localContentsOffset + 8 >= this.contents.length) { |
| resizeContents(8); |
| } |
| this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) localVariableNameIndex; |
| localContentsOffset += 6; |
| |
| // leave space for attribute_length and local_variable_table_length |
| int nameIndex; |
| int descriptorIndex; |
| |
| // used to remember the local variable with a generic type |
| int genericLocalVariablesCounter = 0; |
| LocalVariableBinding[] genericLocalVariables = null; |
| int numberOfGenericEntries = 0; |
| |
| for (int i = 0, max = codeStream.allLocalsCounter; i < max; i++) { |
| LocalVariableBinding localVariable = codeStream.locals[i]; |
| final TypeBinding localVariableTypeBinding = localVariable.type; |
| boolean isParameterizedType = localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable(); |
| if (localVariable.initializationCount != 0 && isParameterizedType) { |
| if (genericLocalVariables == null) { |
| // we cannot have more than max locals |
| genericLocalVariables = new LocalVariableBinding[max]; |
| } |
| genericLocalVariables[genericLocalVariablesCounter++] = localVariable; |
| } |
| for (int j = 0; j < localVariable.initializationCount; j++) { |
| int startPC = localVariable.initializationPCs[j << 1]; |
| int endPC = localVariable.initializationPCs[(j << 1) + 1]; |
| if (startPC != endPC) { // only entries for non zero length |
| if (endPC == -1) { |
| localVariable.declaringScope.problemReporter().abortDueToInternalError( |
| Messages.bind(Messages.abort_invalidAttribute, new String(localVariable.name)), |
| (ASTNode) localVariable.declaringScope.methodScope().referenceContext); |
| } |
| if (localContentsOffset + 10 >= this.contents.length) { |
| resizeContents(10); |
| } |
| // now we can safely add the local entry |
| numberOfEntries++; |
| if (isParameterizedType) { |
| numberOfGenericEntries++; |
| } |
| this.contents[localContentsOffset++] = (byte) (startPC >> 8); |
| this.contents[localContentsOffset++] = (byte) startPC; |
| int length = endPC - startPC; |
| this.contents[localContentsOffset++] = (byte) (length >> 8); |
| this.contents[localContentsOffset++] = (byte) length; |
| nameIndex = constantPool.literalIndex(localVariable.name); |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| descriptorIndex = constantPool.literalIndex(localVariableTypeBinding.signature()); |
| this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) descriptorIndex; |
| int resolvedPosition = localVariable.resolvedPosition; |
| this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); |
| this.contents[localContentsOffset++] = (byte) resolvedPosition; |
| } |
| } |
| } |
| int value = numberOfEntries * 10 + 2; |
| localVariableTableOffset += 2; |
| this.contents[localVariableTableOffset++] = (byte) (value >> 24); |
| this.contents[localVariableTableOffset++] = (byte) (value >> 16); |
| this.contents[localVariableTableOffset++] = (byte) (value >> 8); |
| this.contents[localVariableTableOffset++] = (byte) value; |
| this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); |
| this.contents[localVariableTableOffset] = (byte) numberOfEntries; |
| attributeNumber++; |
| |
| if (genericLocalVariablesCounter != 0) { |
| // add the local variable type table attribute |
| // reserve enough space |
| int maxOfEntries = 8 + numberOfGenericEntries * 10; |
| |
| if (localContentsOffset + maxOfEntries >= this.contents.length) { |
| resizeContents(maxOfEntries); |
| } |
| int localVariableTypeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LocalVariableTypeTableName); |
| this.contents[localContentsOffset++] = (byte) (localVariableTypeNameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) localVariableTypeNameIndex; |
| value = numberOfGenericEntries * 10 + 2; |
| this.contents[localContentsOffset++] = (byte) (value >> 24); |
| this.contents[localContentsOffset++] = (byte) (value >> 16); |
| this.contents[localContentsOffset++] = (byte) (value >> 8); |
| this.contents[localContentsOffset++] = (byte) value; |
| this.contents[localContentsOffset++] = (byte) (numberOfGenericEntries >> 8); |
| this.contents[localContentsOffset++] = (byte) numberOfGenericEntries; |
| for (int i = 0; i < genericLocalVariablesCounter; i++) { |
| LocalVariableBinding localVariable = genericLocalVariables[i]; |
| for (int j = 0; j < localVariable.initializationCount; j++) { |
| int startPC = localVariable.initializationPCs[j << 1]; |
| int endPC = localVariable.initializationPCs[(j << 1) + 1]; |
| if (startPC != endPC) { // only entries for non zero length |
| // now we can safely add the local entry |
| this.contents[localContentsOffset++] = (byte) (startPC >> 8); |
| this.contents[localContentsOffset++] = (byte) startPC; |
| int length = endPC - startPC; |
| this.contents[localContentsOffset++] = (byte) (length >> 8); |
| this.contents[localContentsOffset++] = (byte) length; |
| nameIndex = constantPool.literalIndex(localVariable.name); |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| descriptorIndex = constantPool.literalIndex(localVariable.type.genericTypeSignature()); |
| this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) descriptorIndex; |
| int resolvedPosition = localVariable.resolvedPosition; |
| this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); |
| this.contents[localContentsOffset++] = (byte) resolvedPosition; |
| } |
| } |
| } |
| attributeNumber++; |
| } |
| } |
| } |
| // update the number of attributes |
| // ensure first that there is enough space available inside the contents array |
| if (codeAttributeAttributeOffset + 2 >= this.contents.length) { |
| resizeContents(2); |
| } |
| this.contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); |
| this.contents[codeAttributeAttributeOffset] = (byte) attributeNumber; |
| // update the attribute length |
| int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6); |
| this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); |
| this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); |
| this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); |
| this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength; |
| contentsOffset = localContentsOffset; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * That method completes the creation of the code attribute by setting |
| * - the attribute_length |
| * - max_stack |
| * - max_locals |
| * - code_length |
| * - exception table |
| * - and debug attributes if necessary. |
| */ |
| public void completeCodeAttributeForClinit( |
| int codeAttributeOffset, |
| int problemLine) { |
| // reinitialize the contents with the byte modified by the code stream |
| this.contents = codeStream.bCodeStream; |
| int localContentsOffset = codeStream.classFileOffset; |
| // codeAttributeOffset is the position inside contents byte array before we started to write |
| // any information about the codeAttribute |
| // That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset |
| // to get the right position, 6 for the max_stack etc... |
| int code_length = codeStream.position; |
| if (code_length > 65535) { |
| codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit( |
| codeStream.methodDeclaration.scope.referenceType()); |
| } |
| if (localContentsOffset + 20 >= this.contents.length) { |
| resizeContents(20); |
| } |
| int max_stack = codeStream.stackMax; |
| this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); |
| this.contents[codeAttributeOffset + 7] = (byte) max_stack; |
| int max_locals = codeStream.maxLocals; |
| this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); |
| this.contents[codeAttributeOffset + 9] = (byte) max_locals; |
| this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24); |
| this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16); |
| this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8); |
| this.contents[codeAttributeOffset + 13] = (byte) code_length; |
| |
| // write the exception table |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| |
| // debug attributes |
| int codeAttributeAttributeOffset = localContentsOffset; |
| int attributeNumber = 0; // leave two bytes for the attribute_length |
| localContentsOffset += 2; // first we handle the linenumber attribute |
| |
| // first we handle the linenumber attribute |
| if (codeStream.generateLineNumberAttributes) { |
| if (localContentsOffset + 20 >= this.contents.length) { |
| resizeContents(20); |
| } |
| /* Create and add the line number attribute (used for debugging) |
| * Build the pairs of: |
| * (bytecodePC lineNumber) |
| * according to the table of start line indexes and the pcToSourceMap table |
| * contained into the codestream |
| */ |
| int lineNumberNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName); |
| this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) lineNumberNameIndex; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 6; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 1; |
| // first entry at pc = 0 |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = (byte) (problemLine >> 8); |
| this.contents[localContentsOffset++] = (byte) problemLine; |
| // now we change the size of the line number attribute |
| attributeNumber++; |
| } |
| // then we do the local variable attribute |
| if (codeStream.generateLocalVariableTableAttributes) { |
| int localVariableNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName); |
| if (localContentsOffset + 8 >= this.contents.length) { |
| resizeContents(8); |
| } |
| this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) localVariableNameIndex; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 2; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| attributeNumber++; |
| } |
| // update the number of attributes |
| // ensure first that there is enough space available inside the contents array |
| if (codeAttributeAttributeOffset + 2 >= this.contents.length) { |
| resizeContents(2); |
| } |
| this.contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); |
| this.contents[codeAttributeAttributeOffset] = (byte) attributeNumber; |
| // update the attribute length |
| int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6); |
| this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); |
| this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); |
| this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); |
| this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength; |
| contentsOffset = localContentsOffset; |
| } |
| |
| /** |
| * |
| */ |
| public void completeCodeAttributeForMissingAbstractProblemMethod( |
| MethodBinding binding, |
| int codeAttributeOffset, |
| int[] startLineIndexes, |
| int problemLine) { |
| // reinitialize the localContents with the byte modified by the code stream |
| this.contents = codeStream.bCodeStream; |
| int localContentsOffset = codeStream.classFileOffset; |
| // codeAttributeOffset is the position inside localContents byte array before we started to write// any information about the codeAttribute// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset// to get the right position, 6 for the max_stack etc... |
| int max_stack = codeStream.stackMax; |
| this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); |
| this.contents[codeAttributeOffset + 7] = (byte) max_stack; |
| int max_locals = codeStream.maxLocals; |
| this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); |
| this.contents[codeAttributeOffset + 9] = (byte) max_locals; |
| int code_length = codeStream.position; |
| this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24); |
| this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16); |
| this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8); |
| this.contents[codeAttributeOffset + 13] = (byte) code_length; |
| // write the exception table |
| if (localContentsOffset + 50 >= this.contents.length) { |
| resizeContents(50); |
| } |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| // debug attributes |
| int codeAttributeAttributeOffset = localContentsOffset; |
| int attributeNumber = 0; // leave two bytes for the attribute_length |
| localContentsOffset += 2; // first we handle the linenumber attribute |
| |
| if (codeStream.generateLineNumberAttributes) { |
| if (localContentsOffset + 12 >= this.contents.length) { |
| resizeContents(12); |
| } |
| /* Create and add the line number attribute (used for debugging) |
| * Build the pairs of: |
| * (bytecodePC lineNumber) |
| * according to the table of start line indexes and the pcToSourceMap table |
| * contained into the codestream |
| */ |
| int lineNumberNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName); |
| this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) lineNumberNameIndex; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 6; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 1; |
| if (problemLine == 0) { |
| problemLine = searchLineNumber(startLineIndexes, binding.sourceStart()); |
| } |
| // first entry at pc = 0 |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = (byte) (problemLine >> 8); |
| this.contents[localContentsOffset++] = (byte) problemLine; |
| // now we change the size of the line number attribute |
| attributeNumber++; |
| } |
| |
| // then we do the local variable attribute |
| // update the number of attributes// ensure first that there is enough space available inside the localContents array |
| if (codeAttributeAttributeOffset + 2 >= this.contents.length) { |
| resizeContents(2); |
| } |
| this.contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); |
| this.contents[codeAttributeAttributeOffset] = (byte) attributeNumber; |
| // update the attribute length |
| int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6); |
| this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); |
| this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); |
| this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); |
| this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength; |
| contentsOffset = localContentsOffset; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * That method completes the creation of the code attribute by setting |
| * - the attribute_length |
| * - max_stack |
| * - max_locals |
| * - code_length |
| * - exception table |
| * - and debug attributes if necessary. |
| * |
| * @param codeAttributeOffset <CODE>int</CODE> |
| */ |
| public void completeCodeAttributeForProblemMethod( |
| AbstractMethodDeclaration method, |
| MethodBinding binding, |
| int codeAttributeOffset, |
| int[] startLineIndexes, |
| int problemLine) { |
| // reinitialize the localContents with the byte modified by the code stream |
| this.contents = codeStream.bCodeStream; |
| int localContentsOffset = codeStream.classFileOffset; |
| // codeAttributeOffset is the position inside localContents byte array before we started to write// any information about the codeAttribute// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset// to get the right position, 6 for the max_stack etc... |
| int max_stack = codeStream.stackMax; |
| this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); |
| this.contents[codeAttributeOffset + 7] = (byte) max_stack; |
| int max_locals = codeStream.maxLocals; |
| this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); |
| this.contents[codeAttributeOffset + 9] = (byte) max_locals; |
| int code_length = codeStream.position; |
| this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24); |
| this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16); |
| this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8); |
| this.contents[codeAttributeOffset + 13] = (byte) code_length; |
| // write the exception table |
| if (localContentsOffset + 50 >= this.contents.length) { |
| resizeContents(50); |
| } |
| |
| // write the exception table |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| // debug attributes |
| int codeAttributeAttributeOffset = localContentsOffset; |
| int attributeNumber = 0; // leave two bytes for the attribute_length |
| localContentsOffset += 2; // first we handle the linenumber attribute |
| |
| if (codeStream.generateLineNumberAttributes) { |
| if (localContentsOffset + 20 >= this.contents.length) { |
| resizeContents(20); |
| } |
| /* Create and add the line number attribute (used for debugging) |
| * Build the pairs of: |
| * (bytecodePC lineNumber) |
| * according to the table of start line indexes and the pcToSourceMap table |
| * contained into the codestream |
| */ |
| int lineNumberNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName); |
| this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) lineNumberNameIndex; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 6; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 1; |
| if (problemLine == 0) { |
| problemLine = searchLineNumber(startLineIndexes, binding.sourceStart()); |
| } |
| // first entry at pc = 0 |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = (byte) (problemLine >> 8); |
| this.contents[localContentsOffset++] = (byte) problemLine; |
| // now we change the size of the line number attribute |
| attributeNumber++; |
| } |
| // then we do the local variable attribute |
| if (codeStream.generateLocalVariableTableAttributes) { |
| // compute the resolved position for the arguments of the method |
| int argSize; |
| int localVariableTableOffset = localContentsOffset; |
| int numberOfEntries = 0; |
| // codeAttribute.addLocalVariableTableAttribute(this); |
| int localVariableNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName); |
| if (localContentsOffset + 8 >= this.contents.length) { |
| resizeContents(8); |
| } |
| this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) localVariableNameIndex; |
| localContentsOffset += 6; |
| // leave space for attribute_length and local_variable_table_length |
| int descriptorIndex; |
| int nameIndex; |
| SourceTypeBinding declaringClassBinding = null; |
| final boolean methodDeclarationIsStatic = codeStream.methodDeclaration.isStatic(); |
| if (!methodDeclarationIsStatic) { |
| numberOfEntries++; |
| if (localContentsOffset + 10 >= this.contents.length) { |
| resizeContents(10); |
| } |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = (byte) (code_length >> 8); |
| this.contents[localContentsOffset++] = (byte) code_length; |
| nameIndex = constantPool.literalIndex(ConstantPool.This); |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| declaringClassBinding = (SourceTypeBinding) codeStream.methodDeclaration.binding.declaringClass; |
| descriptorIndex = |
| constantPool.literalIndex(declaringClassBinding.signature()); |
| this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) descriptorIndex; |
| // the resolved position for this is always 0 |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| } |
| // used to remember the local variable with a generic type |
| int genericLocalVariablesCounter = 0; |
| LocalVariableBinding[] genericLocalVariables = null; |
| int numberOfGenericEntries = 0; |
| |
| if (binding.isConstructor()) { |
| ReferenceBinding declaringClass = binding.declaringClass; |
| if (declaringClass.isNestedType()) { |
| NestedTypeBinding methodDeclaringClass = (NestedTypeBinding) declaringClass; |
| argSize = methodDeclaringClass.enclosingInstancesSlotSize; |
| SyntheticArgumentBinding[] syntheticArguments; |
| if ((syntheticArguments = methodDeclaringClass.syntheticEnclosingInstances()) != null) { |
| for (int i = 0, max = syntheticArguments.length; i < max; i++) { |
| LocalVariableBinding localVariable = syntheticArguments[i]; |
| final TypeBinding localVariableTypeBinding = localVariable.type; |
| if (localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable()) { |
| if (genericLocalVariables == null) { |
| // we cannot have more than max locals |
| genericLocalVariables = new LocalVariableBinding[max]; |
| } |
| genericLocalVariables[genericLocalVariablesCounter++] = localVariable; |
| numberOfGenericEntries++; |
| } |
| if (localContentsOffset + 10 >= this.contents.length) { |
| resizeContents(10); |
| } |
| // now we can safely add the local entry |
| numberOfEntries++; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = (byte) (code_length >> 8); |
| this.contents[localContentsOffset++] = (byte) code_length; |
| nameIndex = constantPool.literalIndex(localVariable.name); |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| descriptorIndex = constantPool.literalIndex(localVariableTypeBinding.signature()); |
| this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) descriptorIndex; |
| int resolvedPosition = localVariable.resolvedPosition; |
| this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); |
| this.contents[localContentsOffset++] = (byte) resolvedPosition; |
| } |
| } |
| } else { |
| argSize = 1; |
| } |
| } else { |
| argSize = binding.isStatic() ? 0 : 1; |
| } |
| |
| int genericArgumentsCounter = 0; |
| int[] genericArgumentsNameIndexes = null; |
| int[] genericArgumentsResolvedPositions = null; |
| TypeBinding[] genericArgumentsTypeBindings = null; |
| |
| if (method.binding != null) { |
| TypeBinding[] parameters = method.binding.parameters; |
| Argument[] arguments = method.arguments; |
| if ((parameters != null) && (arguments != null)) { |
| for (int i = 0, max = parameters.length; i < max; i++) { |
| TypeBinding argumentBinding = parameters[i]; |
| if (localContentsOffset + 10 >= this.contents.length) { |
| resizeContents(10); |
| } |
| // now we can safely add the local entry |
| numberOfEntries++; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = (byte) (code_length >> 8); |
| this.contents[localContentsOffset++] = (byte) code_length; |
| nameIndex = constantPool.literalIndex(arguments[i].name); |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| int resolvedPosition = argSize; |
| if (argumentBinding.isParameterizedType() || argumentBinding.isTypeVariable()) { |
| if (genericArgumentsCounter == 0) { |
| // we cannot have more than max locals |
| genericArgumentsNameIndexes = new int[max]; |
| genericArgumentsResolvedPositions = new int[max]; |
| genericArgumentsTypeBindings = new TypeBinding[max]; |
| } |
| genericArgumentsNameIndexes[genericArgumentsCounter] = nameIndex; |
| genericArgumentsResolvedPositions[genericArgumentsCounter] = resolvedPosition; |
| genericArgumentsTypeBindings[genericArgumentsCounter++] = argumentBinding; |
| } |
| descriptorIndex = constantPool.literalIndex(argumentBinding.signature()); |
| this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) descriptorIndex; |
| if ((argumentBinding == BaseTypes.LongBinding) |
| || (argumentBinding == BaseTypes.DoubleBinding)) |
| argSize += 2; |
| else |
| argSize++; |
| this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); |
| this.contents[localContentsOffset++] = (byte) resolvedPosition; |
| } |
| } |
| } |
| int value = numberOfEntries * 10 + 2; |
| localVariableTableOffset += 2; |
| this.contents[localVariableTableOffset++] = (byte) (value >> 24); |
| this.contents[localVariableTableOffset++] = (byte) (value >> 16); |
| this.contents[localVariableTableOffset++] = (byte) (value >> 8); |
| this.contents[localVariableTableOffset++] = (byte) value; |
| this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); |
| this.contents[localVariableTableOffset] = (byte) numberOfEntries; |
| attributeNumber++; |
| |
| final boolean currentInstanceIsGeneric = |
| !methodDeclarationIsStatic |
| && declaringClassBinding != null |
| && declaringClassBinding.typeVariables != NoTypeVariables; |
| if (genericLocalVariablesCounter != 0 || genericArgumentsCounter != 0 || currentInstanceIsGeneric) { |
| // add the local variable type table attribute |
| numberOfEntries = numberOfGenericEntries + genericArgumentsCounter + (currentInstanceIsGeneric ? 1 : 0); |
| // reserve enough space |
| int maxOfEntries = 8 + numberOfEntries * 10; |
| if (localContentsOffset + maxOfEntries >= this.contents.length) { |
| resizeContents(maxOfEntries); |
| } |
| int localVariableTypeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LocalVariableTypeTableName); |
| this.contents[localContentsOffset++] = (byte) (localVariableTypeNameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) localVariableTypeNameIndex; |
| value = numberOfEntries * 10 + 2; |
| this.contents[localContentsOffset++] = (byte) (value >> 24); |
| this.contents[localContentsOffset++] = (byte) (value >> 16); |
| this.contents[localContentsOffset++] = (byte) (value >> 8); |
| this.contents[localContentsOffset++] = (byte) value; |
| this.contents[localContentsOffset++] = (byte) (numberOfEntries >> 8); |
| this.contents[localContentsOffset++] = (byte) numberOfEntries; |
| if (currentInstanceIsGeneric) { |
| numberOfEntries++; |
| this.contents[localContentsOffset++] = 0; // the startPC for this is always 0 |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = (byte) (code_length >> 8); |
| this.contents[localContentsOffset++] = (byte) code_length; |
| nameIndex = constantPool.literalIndex(ConstantPool.This); |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| descriptorIndex = constantPool.literalIndex(declaringClassBinding.genericTypeSignature()); |
| this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) descriptorIndex; |
| this.contents[localContentsOffset++] = 0;// the resolved position for this is always 0 |
| this.contents[localContentsOffset++] = 0; |
| } |
| |
| for (int i = 0; i < genericLocalVariablesCounter; i++) { |
| LocalVariableBinding localVariable = genericLocalVariables[i]; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = (byte) (code_length >> 8); |
| this.contents[localContentsOffset++] = (byte) code_length; |
| nameIndex = constantPool.literalIndex(localVariable.name); |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| descriptorIndex = constantPool.literalIndex(localVariable.type.genericTypeSignature()); |
| this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) descriptorIndex; |
| int resolvedPosition = localVariable.resolvedPosition; |
| this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); |
| this.contents[localContentsOffset++] = (byte) resolvedPosition; |
| } |
| for (int i = 0; i < genericArgumentsCounter; i++) { |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = (byte) (code_length >> 8); |
| this.contents[localContentsOffset++] = (byte) code_length; |
| nameIndex = genericArgumentsNameIndexes[i]; |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| descriptorIndex = constantPool.literalIndex(genericArgumentsTypeBindings[i].genericTypeSignature()); |
| this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) descriptorIndex; |
| int resolvedPosition = genericArgumentsResolvedPositions[i]; |
| this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); |
| this.contents[localContentsOffset++] = (byte) resolvedPosition; |
| } |
| attributeNumber++; |
| } |
| } |
| // update the number of attributes// ensure first that there is enough space available inside the localContents array |
| if (codeAttributeAttributeOffset + 2 >= this.contents.length) { |
| resizeContents(2); |
| } |
| this.contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); |
| this.contents[codeAttributeAttributeOffset] = (byte) attributeNumber; |
| // update the attribute length |
| int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6); |
| this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); |
| this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); |
| this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); |
| this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength; |
| contentsOffset = localContentsOffset; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * That method completes the creation of the code attribute by setting |
| * - the attribute_length |
| * - max_stack |
| * - max_locals |
| * - code_length |
| * - exception table |
| * - and debug attributes if necessary. |
| * |
| * @param binding org.eclipse.jdt.internal.compiler.lookup.SyntheticAccessMethodBinding |
| * @param codeAttributeOffset <CODE>int</CODE> |
| */ |
| public void completeCodeAttributeForSyntheticMethod( |
| SyntheticMethodBinding binding, |
| int codeAttributeOffset, |
| int[] startLineIndexes) { |
| |
| this.completeCodeAttributeForSyntheticMethod( |
| false, |
| binding, |
| codeAttributeOffset, |
| startLineIndexes); |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * That method completes the creation of the code attribute by setting |
| * - the attribute_length |
| * - max_stack |
| * - max_locals |
| * - code_length |
| * - exception table |
| * - and debug attributes if necessary. |
| * |
| * @param binding org.eclipse.jdt.internal.compiler.lookup.SyntheticAccessMethodBinding |
| * @param codeAttributeOffset <CODE>int</CODE> |
| */ |
| public void completeCodeAttributeForSyntheticMethod( |
| boolean hasExceptionHandlers, |
| SyntheticMethodBinding binding, |
| int codeAttributeOffset, |
| int[] startLineIndexes) { |
| // reinitialize the contents with the byte modified by the code stream |
| this.contents = codeStream.bCodeStream; |
| int localContentsOffset = codeStream.classFileOffset; |
| // codeAttributeOffset is the position inside contents byte array before we started to write |
| // any information about the codeAttribute |
| // That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset |
| // to get the right position, 6 for the max_stack etc... |
| int max_stack = codeStream.stackMax; |
| contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); |
| contents[codeAttributeOffset + 7] = (byte) max_stack; |
| int max_locals = codeStream.maxLocals; |
| contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); |
| contents[codeAttributeOffset + 9] = (byte) max_locals; |
| int code_length = codeStream.position; |
| contents[codeAttributeOffset + 10] = (byte) (code_length >> 24); |
| contents[codeAttributeOffset + 11] = (byte) (code_length >> 16); |
| contents[codeAttributeOffset + 12] = (byte) (code_length >> 8); |
| contents[codeAttributeOffset + 13] = (byte) code_length; |
| if ((localContentsOffset + 40) >= this.contents.length) { |
| resizeContents(40); |
| } |
| |
| if (hasExceptionHandlers) { |
| // write the exception table |
| int exceptionHandlersNumber = codeStream.exceptionHandlersCounter; |
| ExceptionLabel[] exceptionHandlers = codeStream.exceptionHandlers; |
| int exSize = exceptionHandlersNumber * 8 + 2; |
| if (exSize + localContentsOffset >= this.contents.length) { |
| resizeContents(exSize); |
| } |
| // there is no exception table, so we need to offset by 2 the current offset and move |
| // on the attribute generation |
| this.contents[localContentsOffset++] = (byte) (exceptionHandlersNumber >> 8); |
| this.contents[localContentsOffset++] = (byte) exceptionHandlersNumber; |
| for (int i = 0, max = codeStream.exceptionHandlersIndex; i < max; i++) { |
| ExceptionLabel exceptionHandler = exceptionHandlers[i]; |
| if (exceptionHandler != null) { |
| int start = exceptionHandler.start; |
| this.contents[localContentsOffset++] = (byte) (start >> 8); |
| this.contents[localContentsOffset++] = (byte) start; |
| int end = exceptionHandler.end; |
| this.contents[localContentsOffset++] = (byte) (end >> 8); |
| this.contents[localContentsOffset++] = (byte) end; |
| int handlerPC = exceptionHandler.position; |
| this.contents[localContentsOffset++] = (byte) (handlerPC >> 8); |
| this.contents[localContentsOffset++] = (byte) handlerPC; |
| if (exceptionHandler.exceptionType == null) { |
| // any exception handler |
| this.contents[localContentsOffset++] = 0; |
| this.contents[localContentsOffset++] = 0; |
| } else { |
| int nameIndex; |
| switch(exceptionHandler.exceptionType.id) { |
| case T_null : |
| /* represents ClassNotFoundException, see class literal access*/ |
| nameIndex = constantPool.literalIndexForType(ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName); |
| break; |
| case T_long : |
| /* represents NoSuchFieldError, see switch table generation*/ |
| nameIndex = constantPool.literalIndexForType(ConstantPool.JavaLangNoSuchFieldErrorConstantPoolName); |
| break; |
| default: |
| nameIndex = constantPool.literalIndexForType(exceptionHandler.exceptionType.constantPoolName()); |
| } |
| this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| this.contents[localContentsOffset++] = (byte) nameIndex; |
| } |
| } |
| } |
| } else { |
| // there is no exception table, so we need to offset by 2 the current offset and move |
| // on the attribute generation |
| contents[localContentsOffset++] = 0; |
| contents[localContentsOffset++] = 0; |
| } |
| // debug attributes |
| int codeAttributeAttributeOffset = localContentsOffset; |
| int attributeNumber = 0; |
| // leave two bytes for the attribute_length |
| localContentsOffset += 2; |
| |
| // first we handle the linenumber attribute |
| if (codeStream.generateLineNumberAttributes) { |
| if (localContentsOffset + 12 >= this.contents.length) { |
| resizeContents(12); |
| } |
| int index = 0; |
| int lineNumberNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName); |
| contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8); |
| contents[localContentsOffset++] = (byte) lineNumberNameIndex; |
| int lineNumberTableOffset = localContentsOffset; |
| localContentsOffset += 6; |
| // leave space for attribute_length and line_number_table_length |
| // Seems like do would be better, but this preserves the existing behavior. |
| index = searchLineNumber(startLineIndexes, binding.sourceStart); |
| contents[localContentsOffset++] = 0; |
| contents[localContentsOffset++] = 0; |
| contents[localContentsOffset++] = (byte) (index >> 8); |
| contents[localContentsOffset++] = (byte) index; |
| // now we change the size of the line number attribute |
| contents[lineNumberTableOffset++] = 0; |
| contents[lineNumberTableOffset++] = 0; |
| contents[lineNumberTableOffset++] = 0; |
| contents[lineNumberTableOffset++] = 6; |
| contents[lineNumberTableOffset++] = 0; |
| contents[lineNumberTableOffset++] = 1; |
| attributeNumber++; |
| } |
| // then we do the local variable attribute |
| if (codeStream.generateLocalVariableTableAttributes) { |
| int localVariableTableOffset = localContentsOffset; |
| int numberOfEntries = 0; |
| int localVariableNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName); |
| if (localContentsOffset + 8 > this.contents.length) { |
| resizeContents(8); |
| } |
| contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); |
| contents[localContentsOffset++] = (byte) localVariableNameIndex; |
| localContentsOffset += 6; |
| // leave space for attribute_length and local_variable_table_length |
| int nameIndex; |
| int descriptorIndex; |
| |
| // used to remember the local variable with a generic type |
| int genericLocalVariablesCounter = 0; |
| LocalVariableBinding[] genericLocalVariables = null; |
| int numberOfGenericEntries = 0; |
| |
| for (int i = 0, max = codeStream.allLocalsCounter; i < max; i++) { |
| LocalVariableBinding localVariable = codeStream.locals[i]; |
| final TypeBinding localVariableTypeBinding = localVariable.type; |
| boolean isParameterizedType = localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable(); |
| if (localVariable.initializationCount != 0 && isParameterizedType) { |
| if (genericLocalVariables == null) { |
| // we cannot have more than max locals |
| genericLocalVariables = new LocalVariableBinding[max]; |
| } |
| genericLocalVariables[genericLocalVariablesCounter++] = localVariable; |
| } |
| for (int j = 0; j < localVariable.initializationCount; j++) { |
| int startPC = localVariable.initializationPCs[j << 1]; |
| int endPC = localVariable.initializationPCs[(j << 1) + 1]; |
| if (startPC != endPC) { // only entries for non zero length |
| if (endPC == -1) { |
| localVariable.declaringScope.problemReporter().abortDueToInternalError( |
| Messages.bind(Messages.abort_invalidAttribute, new String(localVariable.name)), |
| (ASTNode) localVariable.declaringScope.methodScope().referenceContext); |
| } |
| if (localContentsOffset + 10 > this.contents.length) { |
| resizeContents(10); |
| } |
| // now we can safely add the local entry |
| numberOfEntries++; |
| if (isParameterizedType) { |
| numberOfGenericEntries++; |
| } |
| contents[localContentsOffset++] = (byte) (startPC >> 8); |
| contents[localContentsOffset++] = (byte) startPC; |
| int length = endPC - startPC; |
| contents[localContentsOffset++] = (byte) (length >> 8); |
| contents[localContentsOffset++] = (byte) length; |
| nameIndex = constantPool.literalIndex(localVariable.name); |
| contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| contents[localContentsOffset++] = (byte) nameIndex; |
| descriptorIndex = constantPool.literalIndex(localVariableTypeBinding.signature()); |
| contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| contents[localContentsOffset++] = (byte) descriptorIndex; |
| int resolvedPosition = localVariable.resolvedPosition; |
| contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); |
| contents[localContentsOffset++] = (byte) resolvedPosition; |
| } |
| } |
| } |
| int value = numberOfEntries * 10 + 2; |
| localVariableTableOffset += 2; |
| contents[localVariableTableOffset++] = (byte) (value >> 24); |
| contents[localVariableTableOffset++] = (byte) (value >> 16); |
| contents[localVariableTableOffset++] = (byte) (value >> 8); |
| contents[localVariableTableOffset++] = (byte) value; |
| contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); |
| contents[localVariableTableOffset] = (byte) numberOfEntries; |
| attributeNumber++; |
| |
| if (genericLocalVariablesCounter != 0) { |
| // add the local variable type table attribute |
| int maxOfEntries = 8 + numberOfGenericEntries * 10; |
| // reserve enough space |
| if (localContentsOffset + maxOfEntries >= this.contents.length) { |
| resizeContents(maxOfEntries); |
| } |
| int localVariableTypeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.LocalVariableTypeTableName); |
| contents[localContentsOffset++] = (byte) (localVariableTypeNameIndex >> 8); |
| contents[localContentsOffset++] = (byte) localVariableTypeNameIndex; |
| value = numberOfGenericEntries * 10 + 2; |
| contents[localContentsOffset++] = (byte) (value >> 24); |
| contents[localContentsOffset++] = (byte) (value >> 16); |
| contents[localContentsOffset++] = (byte) (value >> 8); |
| contents[localContentsOffset++] = (byte) value; |
| contents[localContentsOffset++] = (byte) (numberOfGenericEntries >> 8); |
| contents[localContentsOffset++] = (byte) numberOfGenericEntries; |
| |
| for (int i = 0; i < genericLocalVariablesCounter; i++) { |
| LocalVariableBinding localVariable = genericLocalVariables[i]; |
| for (int j = 0; j < localVariable.initializationCount; j++) { |
| int startPC = localVariable.initializationPCs[j << 1]; |
| int endPC = localVariable.initializationPCs[(j << 1) + 1]; |
| if (startPC != endPC) { // only entries for non zero length |
| // now we can safely add the local entry |
| contents[localContentsOffset++] = (byte) (startPC >> 8); |
| contents[localContentsOffset++] = (byte) startPC; |
| int length = endPC - startPC; |
| contents[localContentsOffset++] = (byte) (length >> 8); |
| contents[localContentsOffset++] = (byte) length; |
| nameIndex = constantPool.literalIndex(localVariable.name); |
| contents[localContentsOffset++] = (byte) (nameIndex >> 8); |
| contents[localContentsOffset++] = (byte) nameIndex; |
| descriptorIndex = constantPool.literalIndex(localVariable.type.genericTypeSignature()); |
| contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); |
| contents[localContentsOffset++] = (byte) descriptorIndex; |
| int resolvedPosition = localVariable.resolvedPosition; |
| contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); |
| contents[localContentsOffset++] = (byte) resolvedPosition; |
| } |
| } |
| } |
| attributeNumber++; |
| } |
| } |
| // update the number of attributes |
| // ensure first that there is enough space available inside the contents array |
| if (codeAttributeAttributeOffset + 2 >= this.contents.length) { |
| resizeContents(2); |
| } |
| contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); |
| contents[codeAttributeAttributeOffset] = (byte) attributeNumber; |
| |
| // update the attribute length |
| int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6); |
| contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); |
| contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); |
| contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); |
| contents[codeAttributeOffset + 5] = (byte) codeAttributeLength; |
| contentsOffset = localContentsOffset; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Complete the creation of a method info by setting up the number of attributes at the right offset. |
| * |
| * @param methodAttributeOffset <CODE>int</CODE> |
| * @param attributeNumber <CODE>int</CODE> |
| */ |
| public void completeMethodInfo( |
| int methodAttributeOffset, |
| int attributeNumber) { |
| // update the number of attributes |
| contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); |
| contents[methodAttributeOffset] = (byte) attributeNumber; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * This methods returns a char[] representing the file name of the receiver |
| * |
| * @return char[] |
| */ |
| public char[] fileName() { |
| return constantPool.UTF8Cache.returnKeyFor(2); |
| } |
| |
| private void generateAnnotation(Annotation annotation, int attributeOffset) { |
| if (contentsOffset + 4 >= this.contents.length) { |
| resizeContents(4); |
| } |
| TypeBinding annotationTypeBinding = annotation.resolvedType; |
| if (annotationTypeBinding == null) { |
| this.contentsOffset = attributeOffset; |
| return; |
| } |
| final int typeIndex = constantPool.literalIndex(annotationTypeBinding.signature()); |
| contents[contentsOffset++] = (byte) (typeIndex >> 8); |
| contents[contentsOffset++] = (byte) typeIndex; |
| if (annotation instanceof NormalAnnotation) { |
| NormalAnnotation normalAnnotation = (NormalAnnotation) annotation; |
| MemberValuePair[] memberValuePairs = normalAnnotation.memberValuePairs; |
| if (memberValuePairs != null) { |
| final int memberValuePairsLength = memberValuePairs.length; |
| contents[contentsOffset++] = (byte) (memberValuePairsLength >> 8); |
| contents[contentsOffset++] = (byte) memberValuePairsLength; |
| for (int i = 0; i < memberValuePairsLength; i++) { |
| MemberValuePair memberValuePair = memberValuePairs[i]; |
| if (contentsOffset + 2 >= this.contents.length) { |
| resizeContents(2); |
| } |
| final int elementNameIndex = constantPool.literalIndex(memberValuePair.name); |
| contents[contentsOffset++] = (byte) (elementNameIndex >> 8); |
| contents[contentsOffset++] = (byte) elementNameIndex; |
| MethodBinding methodBinding = memberValuePair.binding; |
| if (methodBinding == null) { |
| contentsOffset = attributeOffset; |
| } else { |
| generateElementValue(memberValuePair.value, methodBinding.returnType, attributeOffset); |
| } |
| } |
| } else { |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| } |
| } else if (annotation instanceof SingleMemberAnnotation) { |
| SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) annotation; |
| // this is a single member annotation (one member value) |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 1; |
| if (contentsOffset + 2 >= this.contents.length) { |
| resizeContents(2); |
| } |
| final int elementNameIndex = constantPool.literalIndex(VALUE); |
| contents[contentsOffset++] = (byte) (elementNameIndex >> 8); |
| contents[contentsOffset++] = (byte) elementNameIndex; |
| MethodBinding methodBinding = singleMemberAnnotation.memberValuePairs()[0].binding; |
| if (methodBinding == null) { |
| contentsOffset = attributeOffset; |
| } else { |
| generateElementValue(singleMemberAnnotation.memberValue, methodBinding.returnType, attributeOffset); |
| } |
| } else { |
| // this is a marker annotation (no member value pairs) |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| } |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * That method generates the header of a code attribute. |
| * - the index inside the constant pool for the attribute name ("Code") |
| * - leave some space for attribute_length(4), max_stack(2), max_locals(2), code_length(4). |
| */ |
| public void generateCodeAttributeHeader() { |
| if (contentsOffset + 20 >= this.contents.length) { |
| resizeContents(20); |
| } |
| int constantValueNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.CodeName); |
| contents[contentsOffset++] = (byte) (constantValueNameIndex >> 8); |
| contents[contentsOffset++] = (byte) constantValueNameIndex; |
| // leave space for attribute_length(4), max_stack(2), max_locals(2), code_length(4) |
| contentsOffset += 12; |
| } |
| |
| private void generateElementValue( |
| Expression defaultValue, |
| TypeBinding memberValuePairReturnType, |
| int attributeOffset) { |
| Constant constant = defaultValue.constant; |
| TypeBinding defaultValueBinding = defaultValue.resolvedType; |
| if (defaultValueBinding == null) { |
| contentsOffset = attributeOffset; |
| } else { |
| if (memberValuePairReturnType.isArrayType() && !defaultValueBinding.isArrayType()) { |
| // automatic wrapping |
| if (contentsOffset + 3 >= this.contents.length) { |
| resizeContents(3); |
| } |
| contents[contentsOffset++] = (byte) '['; |
| contents[contentsOffset++] = (byte) 0; |
| contents[contentsOffset++] = (byte) 1; |
| } |
| if (constant != null && constant != Constant.NotAConstant) { |
| generateElementValue(attributeOffset, defaultValue, constant, memberValuePairReturnType.leafComponentType()); |
| } else { |
| generateElementValueForNonConstantExpression(defaultValue, attributeOffset, defaultValueBinding); |
| } |
| } |
| } |
| |
| /** |
| * @param attributeOffset |
| */ |
| private void generateElementValue(int attributeOffset, Expression defaultValue, Constant constant, TypeBinding binding) { |
| if (contentsOffset + 3 >= this.contents.length) { |
| resizeContents(3); |
| } |
| switch (binding.id) { |
| case T_boolean : |
| contents[contentsOffset++] = (byte) 'Z'; |
| int booleanValueIndex = |
| constantPool.literalIndex(constant.booleanValue() ? 1 : 0); |
| contents[contentsOffset++] = (byte) (booleanValueIndex >> 8); |
| contents[contentsOffset++] = (byte) booleanValueIndex; |
| break; |
| case T_byte : |
| contents[contentsOffset++] = (byte) 'B'; |
| int integerValueIndex = |
| constantPool.literalIndex(constant.intValue()); |
| contents[contentsOffset++] = (byte) (integerValueIndex >> 8); |
| contents[contentsOffset++] = (byte) integerValueIndex; |
| break; |
| case T_char : |
| contents[contentsOffset++] = (byte) 'C'; |
| integerValueIndex = |
| constantPool.literalIndex(constant.intValue()); |
| contents[contentsOffset++] = (byte) (integerValueIndex >> 8); |
| contents[contentsOffset++] = (byte) integerValueIndex; |
| break; |
| case T_int : |
| contents[contentsOffset++] = (byte) 'I'; |
| integerValueIndex = |
| constantPool.literalIndex(constant.intValue()); |
| contents[contentsOffset++] = (byte) (integerValueIndex >> 8); |
| contents[contentsOffset++] = (byte) integerValueIndex; |
| break; |
| case T_short : |
| contents[contentsOffset++] = (byte) 'S'; |
| integerValueIndex = |
| constantPool.literalIndex(constant.intValue()); |
| contents[contentsOffset++] = (byte) (integerValueIndex >> 8); |
| contents[contentsOffset++] = (byte) integerValueIndex; |
| break; |
| case T_float : |
| contents[contentsOffset++] = (byte) 'F'; |
| int floatValueIndex = |
| constantPool.literalIndex(constant.floatValue()); |
| contents[contentsOffset++] = (byte) (floatValueIndex >> 8); |
| contents[contentsOffset++] = (byte) floatValueIndex; |
| break; |
| case T_double : |
| contents[contentsOffset++] = (byte) 'D'; |
| int doubleValueIndex = |
| constantPool.literalIndex(constant.doubleValue()); |
| contents[contentsOffset++] = (byte) (doubleValueIndex >> 8); |
| contents[contentsOffset++] = (byte) doubleValueIndex; |
| break; |
| case T_long : |
| contents[contentsOffset++] = (byte) 'J'; |
| int longValueIndex = |
| constantPool.literalIndex(constant.longValue()); |
| contents[contentsOffset++] = (byte) (longValueIndex >> 8); |
| contents[contentsOffset++] = (byte) longValueIndex; |
| break; |
| case T_JavaLangString : |
| contents[contentsOffset++] = (byte) 's'; |
| int stringValueIndex = |
| constantPool.literalIndex(((StringConstant) constant).stringValue().toCharArray()); |
| if (stringValueIndex == -1) { |
| if (!creatingProblemType) { |
| // report an error and abort: will lead to a problem type classfile creation |
| TypeDeclaration typeDeclaration = referenceBinding.scope.referenceContext; |
| typeDeclaration.scope.problemReporter().stringConstantIsExceedingUtf8Limit(defaultValue); |
| } else { |
| // already inside a problem type creation : no attribute |
| contentsOffset = attributeOffset; |
| } |
| } else { |
| contents[contentsOffset++] = (byte) (stringValueIndex >> 8); |
| contents[contentsOffset++] = (byte) stringValueIndex; |
| } |
| } |
| } |
| |
| private void generateElementValueForNonConstantExpression(Expression defaultValue, int attributeOffset, TypeBinding defaultValueBinding) { |
| if (defaultValueBinding != null) { |
| if (defaultValueBinding.isEnum()) { |
| if (contentsOffset + 5 >= this.contents.length) { |
| resizeContents(5); |
| } |
| contents[contentsOffset++] = (byte) 'e'; |
| FieldBinding fieldBinding = null; |
| if (defaultValue instanceof QualifiedNameReference) { |
| QualifiedNameReference nameReference = (QualifiedNameReference) defaultValue; |
| fieldBinding = (FieldBinding) nameReference.binding; |
| } else if (defaultValue instanceof SingleNameReference) { |
| SingleNameReference nameReference = (SingleNameReference) defaultValue; |
| fieldBinding = (FieldBinding) nameReference.binding; |
| } else { |
| contentsOffset = attributeOffset; |
| } |
| if (fieldBinding != null) { |
| final int enumConstantTypeNameIndex = constantPool.literalIndex(fieldBinding.type.signature()); |
| final int enumConstantNameIndex = constantPool.literalIndex(fieldBinding.name); |
| contents[contentsOffset++] = (byte) (enumConstantTypeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) enumConstantTypeNameIndex; |
| contents[contentsOffset++] = (byte) (enumConstantNameIndex >> 8); |
| contents[contentsOffset++] = (byte) enumConstantNameIndex; |
| } |
| } else if (defaultValueBinding.isAnnotationType()) { |
| if (contentsOffset + 1 >= this.contents.length) { |
| resizeContents(1); |
| } |
| contents[contentsOffset++] = (byte) '@'; |
| generateAnnotation((Annotation) defaultValue, attributeOffset); |
| } else if (defaultValueBinding.isArrayType()) { |
| // array type |
| if (contentsOffset + 3 >= this.contents.length) { |
| resizeContents(3); |
| } |
| contents[contentsOffset++] = (byte) '['; |
| if (defaultValue instanceof ArrayInitializer) { |
| ArrayInitializer arrayInitializer = (ArrayInitializer) defaultValue; |
| int arrayLength = arrayInitializer.expressions != null ? arrayInitializer.expressions.length : 0; |
| contents[contentsOffset++] = (byte) (arrayLength >> 8); |
| contents[contentsOffset++] = (byte) arrayLength; |
| for (int i = 0; i < arrayLength; i++) { |
| generateElementValue(arrayInitializer.expressions[i], defaultValueBinding.leafComponentType(), attributeOffset); |
| } |
| } else { |
| contentsOffset = attributeOffset; |
| } |
| } else { |
| // class type |
| if (contentsOffset + 3 >= this.contents.length) { |
| resizeContents(3); |
| } |
| contents[contentsOffset++] = (byte) 'c'; |
| if (defaultValue instanceof ClassLiteralAccess) { |
| ClassLiteralAccess classLiteralAccess = (ClassLiteralAccess) defaultValue; |
| final int classInfoIndex = constantPool.literalIndex(classLiteralAccess.targetType.signature()); |
| contents[contentsOffset++] = (byte) (classInfoIndex >> 8); |
| contents[contentsOffset++] = (byte) classInfoIndex; |
| } else { |
| contentsOffset = attributeOffset; |
| } |
| } |
| } else { |
| contentsOffset = attributeOffset; |
| } |
| } |
| |
| public int generateMethodInfoAttribute(MethodBinding methodBinding) { |
| return generateMethodInfoAttribute(methodBinding, false); |
| } |
| /** |
| * INTERNAL USE-ONLY |
| * That method generates the attributes of a code attribute. |
| * They could be: |
| * - an exception attribute for each try/catch found inside the method |
| * - a deprecated attribute |
| * - a synthetic attribute for synthetic access methods |
| * |
| * It returns the number of attributes created for the code attribute. |
| * |
| * @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding |
| * @return <CODE>int</CODE> |
| */ |
| public int generateMethodInfoAttribute(MethodBinding methodBinding, boolean createProblemMethod) { |
| // leave two bytes for the attribute_number |
| contentsOffset += 2; |
| // now we can handle all the attribute for that method info: |
| // it could be: |
| // - a CodeAttribute |
| // - a ExceptionAttribute |
| // - a DeprecatedAttribute |
| // - a SyntheticAttribute |
| |
| // Exception attribute |
| ReferenceBinding[] thrownsExceptions; |
| int attributeNumber = 0; |
| if ((thrownsExceptions = methodBinding.thrownExceptions) != NoExceptions) { |
| // The method has a throw clause. So we need to add an exception attribute |
| // check that there is enough space to write all the bytes for the exception attribute |
| int length = thrownsExceptions.length; |
| int exSize = 8 + length * 2; |
| if (exSize + contentsOffset >= this.contents.length) { |
| resizeContents(exSize); |
| } |
| int exceptionNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.ExceptionsName); |
| contents[contentsOffset++] = (byte) (exceptionNameIndex >> 8); |
| contents[contentsOffset++] = (byte) exceptionNameIndex; |
| // The attribute length = length * 2 + 2 in case of a exception attribute |
| int attributeLength = length * 2 + 2; |
| contents[contentsOffset++] = (byte) (attributeLength >> 24); |
| contents[contentsOffset++] = (byte) (attributeLength >> 16); |
| contents[contentsOffset++] = (byte) (attributeLength >> 8); |
| contents[contentsOffset++] = (byte) attributeLength; |
| contents[contentsOffset++] = (byte) (length >> 8); |
| contents[contentsOffset++] = (byte) length; |
| for (int i = 0; i < length; i++) { |
| int exceptionIndex = constantPool.literalIndexForType(thrownsExceptions[i].constantPoolName()); |
| contents[contentsOffset++] = (byte) (exceptionIndex >> 8); |
| contents[contentsOffset++] = (byte) exceptionIndex; |
| } |
| attributeNumber++; |
| } |
| if (methodBinding.isDeprecated()) { |
| // Deprecated attribute |
| // Check that there is enough space to write the deprecated attribute |
| if (contentsOffset + 6 >= this.contents.length) { |
| resizeContents(6); |
| } |
| int deprecatedAttributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.DeprecatedName); |
| contents[contentsOffset++] = (byte) (deprecatedAttributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) deprecatedAttributeNameIndex; |
| // the length of a deprecated attribute is equals to 0 |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| |
| attributeNumber++; |
| } |
| if (this.targetJDK < ClassFileConstants.JDK1_5 && methodBinding.isSynthetic()) { |
| // Synthetic attribute |
| // Check that there is enough space to write the deprecated attribute |
| if (contentsOffset + 6 >= this.contents.length) { |
| resizeContents(6); |
| } |
| int syntheticAttributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.SyntheticName); |
| contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; |
| // the length of a synthetic attribute is equals to 0 |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| |
| attributeNumber++; |
| } |
| // add signature attribute |
| char[] genericSignature = methodBinding.genericSignature(); |
| if (genericSignature != null) { |
| // check that there is enough space to write all the bytes for the field info corresponding |
| // to the @fieldBinding |
| if (contentsOffset + 8 >= this.contents.length) { |
| resizeContents(8); |
| } |
| int signatureAttributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.SignatureName); |
| contents[contentsOffset++] = (byte) (signatureAttributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) signatureAttributeNameIndex; |
| // the length of a signature attribute is equals to 2 |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 0; |
| contents[contentsOffset++] = 2; |
| int signatureIndex = |
| constantPool.literalIndex(genericSignature); |
| contents[contentsOffset++] = (byte) (signatureIndex >> 8); |
| contents[contentsOffset++] = (byte) signatureIndex; |
| attributeNumber++; |
| } |
| if (this.targetJDK >= ClassFileConstants.JDK1_5 && !this.creatingProblemType && !createProblemMethod) { |
| AbstractMethodDeclaration methodDeclaration = methodBinding.sourceMethod(); |
| if (methodDeclaration != null) { |
| Annotation[] annotations = methodDeclaration.annotations; |
| if (annotations != null) { |
| attributeNumber += generateRuntimeAnnotations(annotations); |
| } |
| if ((methodBinding.tagBits & TagBits.HasParameterAnnotations) != 0) { |
| Argument[] arguments = methodDeclaration.arguments; |
| if (arguments != null) { |
| attributeNumber += generateRuntimeAnnotationsForParameters(arguments); |
| } |
| } |
| } |
| } |
| return attributeNumber; |
| } |
| |
| public int generateMethodInfoAttribute(MethodBinding methodBinding, AnnotationMethodDeclaration declaration) { |
| int attributesNumber = generateMethodInfoAttribute(methodBinding); |
| int attributeOffset = contentsOffset; |
| if ((declaration.modifiers & ClassFileConstants.AccAnnotationDefault) != 0) { |
| // add an annotation default attribute |
| int annotationDefaultNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.AnnotationDefaultName); |
| contents[contentsOffset++] = (byte) (annotationDefaultNameIndex >> 8); |
| contents[contentsOffset++] = (byte) annotationDefaultNameIndex; |
| int attributeLengthOffset = contentsOffset; |
| contentsOffset += 4; |
| |
| generateElementValue(declaration.defaultValue, declaration.binding.returnType, attributeOffset); |
| if (contentsOffset != attributeOffset) { |
| int attributeLength = contentsOffset - attributeLengthOffset - 4; |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); |
| contents[attributeLengthOffset++] = (byte) attributeLength; |
| attributesNumber++; |
| } |
| } |
| return attributesNumber; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * That method generates the header of a method info: |
| * The header consists in: |
| * - the access flags |
| * - the name index of the method name inside the constant pool |
| * - the descriptor index of the signature of the method inside the constant pool. |
| * |
| * @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding |
| */ |
| public void generateMethodInfoHeader(MethodBinding methodBinding) { |
| generateMethodInfoHeader(methodBinding, methodBinding.modifiers); |
| } |
| /** |
| * INTERNAL USE-ONLY |
| * That method generates the header of a method info: |
| * The header consists in: |
| * - the access flags |
| * - the name index of the method name inside the constant pool |
| * - the descriptor index of the signature of the method inside the constant pool. |
| * |
| * @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding |
| * @param accessFlags the access flags |
| */ |
| public void generateMethodInfoHeader(MethodBinding methodBinding, int accessFlags) { |
| // check that there is enough space to write all the bytes for the method info corresponding |
| // to the @methodBinding |
| methodCount++; // add one more method |
| if (contentsOffset + 10 >= this.contents.length) { |
| resizeContents(10); |
| } |
| if (targetJDK < ClassFileConstants.JDK1_5) { |
| // pre 1.5, synthetic was an attribute, not a modifier |
| accessFlags &= ~ClassFileConstants.AccSynthetic; |
| } |
| if (methodBinding.isRequiredToClearPrivateModifier()) { |
| accessFlags &= ~ClassFileConstants.AccPrivate; |
| } |
| contents[contentsOffset++] = (byte) (accessFlags >> 8); |
| contents[contentsOffset++] = (byte) accessFlags; |
| int nameIndex = constantPool.literalIndex(methodBinding.selector); |
| contents[contentsOffset++] = (byte) (nameIndex >> 8); |
| contents[contentsOffset++] = (byte) nameIndex; |
| int descriptorIndex = constantPool.literalIndex(methodBinding.signature()); |
| contents[contentsOffset++] = (byte) (descriptorIndex >> 8); |
| contents[contentsOffset++] = (byte) descriptorIndex; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * That method generates the method info header of a clinit: |
| * The header consists in: |
| * - the access flags (always default access + static) |
| * - the name index of the method name (always <clinit>) inside the constant pool |
| * - the descriptor index of the signature (always ()V) of the method inside the constant pool. |
| */ |
| public void generateMethodInfoHeaderForClinit() { |
| // check that there is enough space to write all the bytes for the method info corresponding |
| // to the @methodBinding |
| methodCount++; // add one more method |
| if (contentsOffset + 10 >= this.contents.length) { |
| resizeContents(10); |
| } |
| contents[contentsOffset++] = (byte) ((ClassFileConstants.AccDefault | ClassFileConstants.AccStatic) >> 8); |
| contents[contentsOffset++] = (byte) (ClassFileConstants.AccDefault | ClassFileConstants.AccStatic); |
| int nameIndex = constantPool.literalIndex(ConstantPool.Clinit); |
| contents[contentsOffset++] = (byte) (nameIndex >> 8); |
| contents[contentsOffset++] = (byte) nameIndex; |
| int descriptorIndex = |
| constantPool.literalIndex(ConstantPool.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; |
| } |
| |
| /** |
| * 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); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * @param annotations |
| * @return the number of attributes created while dumping the annotations in the .class file |
| */ |
| private int generateRuntimeAnnotations(final Annotation[] annotations) { |
| int attributesNumber = 0; |
| final int length = annotations.length; |
| int visibleAnnotationsCounter = 0; |
| int invisibleAnnotationsCounter = 0; |
| |
| for (int i = 0; i < length; i++) { |
| Annotation annotation = annotations[i]; |
| if (isRuntimeInvisible(annotation)) { |
| invisibleAnnotationsCounter++; |
| } else if (isRuntimeVisible(annotation)) { |
| visibleAnnotationsCounter++; |
| } |
| } |
| |
| if (invisibleAnnotationsCounter != 0) { |
| int annotationAttributeOffset = contentsOffset; |
| if (contentsOffset + 10 >= contents.length) { |
| resizeContents(10); |
| } |
| int runtimeInvisibleAnnotationsAttributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.RuntimeInvisibleAnnotationsName); |
| contents[contentsOffset++] = (byte) (runtimeInvisibleAnnotationsAttributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) runtimeInvisibleAnnotationsAttributeNameIndex; |
| int attributeLengthOffset = contentsOffset; |
| contentsOffset += 4; // leave space for the attribute length |
| |
| int annotationsLengthOffset = contentsOffset; |
| contentsOffset += 2; // leave space for the annotations length |
| |
| contents[annotationsLengthOffset++] = (byte) (invisibleAnnotationsCounter >> 8); |
| contents[annotationsLengthOffset++] = (byte) invisibleAnnotationsCounter; |
| |
| loop: for (int i = 0; i < length; i++) { |
| if (invisibleAnnotationsCounter == 0) break loop; |
| Annotation annotation = annotations[i]; |
| if (isRuntimeInvisible(annotation)) { |
| generateAnnotation(annotation, annotationAttributeOffset); |
| invisibleAnnotationsCounter--; |
| if (this.contentsOffset == annotationAttributeOffset) { |
| break loop; |
| } |
| } |
| } |
| if (contentsOffset != annotationAttributeOffset) { |
| int attributeLength = contentsOffset - attributeLengthOffset - 4; |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); |
| contents[attributeLengthOffset++] = (byte) attributeLength; |
| attributesNumber++; |
| } else { |
| contentsOffset = annotationAttributeOffset; |
| } |
| } |
| |
| if (visibleAnnotationsCounter != 0) { |
| int annotationAttributeOffset = contentsOffset; |
| if (contentsOffset + 10 >= contents.length) { |
| resizeContents(10); |
| } |
| int runtimeVisibleAnnotationsAttributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.RuntimeVisibleAnnotationsName); |
| contents[contentsOffset++] = (byte) (runtimeVisibleAnnotationsAttributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) runtimeVisibleAnnotationsAttributeNameIndex; |
| int attributeLengthOffset = contentsOffset; |
| contentsOffset += 4; // leave space for the attribute length |
| |
| int annotationsLengthOffset = contentsOffset; |
| contentsOffset += 2; // leave space for the annotations length |
| |
| contents[annotationsLengthOffset++] = (byte) (visibleAnnotationsCounter >> 8); |
| contents[annotationsLengthOffset++] = (byte) visibleAnnotationsCounter; |
| |
| loop: for (int i = 0; i < length; i++) { |
| if (visibleAnnotationsCounter == 0) break loop; |
| Annotation annotation = annotations[i]; |
| if (isRuntimeVisible(annotation)) { |
| visibleAnnotationsCounter--; |
| generateAnnotation(annotation, annotationAttributeOffset); |
| if (this.contentsOffset == annotationAttributeOffset) { |
| break loop; |
| } |
| } |
| } |
| if (contentsOffset != annotationAttributeOffset) { |
| int attributeLength = contentsOffset - attributeLengthOffset - 4; |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); |
| contents[attributeLengthOffset++] = (byte) attributeLength; |
| attributesNumber++; |
| } else { |
| contentsOffset = annotationAttributeOffset; |
| } |
| } |
| return attributesNumber; |
| } |
| |
| private int generateRuntimeAnnotationsForParameters(Argument[] arguments) { |
| final int argumentsLength = arguments.length; |
| final int VISIBLE_INDEX = 0; |
| final int INVISIBLE_INDEX = 1; |
| int invisibleParametersAnnotationsCounter = 0; |
| int visibleParametersAnnotationsCounter = 0; |
| int[][] annotationsCounters = new int[argumentsLength][2]; |
| for (int i = 0; i < argumentsLength; i++) { |
| Argument argument = arguments[i]; |
| Annotation[] annotations = argument.annotations; |
| if (annotations != null) { |
| for (int j = 0, max2 = annotations.length; j < max2; j++) { |
| Annotation annotation = annotations[j]; |
| if (isRuntimeInvisible(annotation)) { |
| annotationsCounters[i][INVISIBLE_INDEX]++; |
| invisibleParametersAnnotationsCounter++; |
| } else if (isRuntimeVisible(annotation)) { |
| annotationsCounters[i][VISIBLE_INDEX]++; |
| visibleParametersAnnotationsCounter++; |
| } |
| } |
| } |
| } |
| int attributesNumber = 0; |
| int annotationAttributeOffset = contentsOffset; |
| if (invisibleParametersAnnotationsCounter != 0) { |
| if (contentsOffset + 7 >= contents.length) { |
| resizeContents(7); |
| } |
| int attributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.RuntimeInvisibleParameterAnnotationsName); |
| contents[contentsOffset++] = (byte) (attributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) attributeNameIndex; |
| int attributeLengthOffset = contentsOffset; |
| contentsOffset += 4; // leave space for the attribute length |
| |
| contents[contentsOffset++] = (byte) argumentsLength; |
| invisibleLoop: for (int i = 0; i < argumentsLength; i++) { |
| if (contentsOffset + 2 >= contents.length) { |
| resizeContents(2); |
| } |
| if (invisibleParametersAnnotationsCounter == 0) { |
| contents[contentsOffset++] = (byte) 0; |
| contents[contentsOffset++] = (byte) 0; |
| } else { |
| final int numberOfInvisibleAnnotations = annotationsCounters[i][INVISIBLE_INDEX]; |
| contents[contentsOffset++] = (byte) (numberOfInvisibleAnnotations >> 8); |
| contents[contentsOffset++] = (byte) numberOfInvisibleAnnotations; |
| if (numberOfInvisibleAnnotations != 0) { |
| Argument argument = arguments[i]; |
| Annotation[] annotations = argument.annotations; |
| for (int j = 0, max = annotations.length; j < max; j++) { |
| Annotation annotation = annotations[j]; |
| if (isRuntimeInvisible(annotation)) { |
| generateAnnotation(annotation, annotationAttributeOffset); |
| if (contentsOffset == annotationAttributeOffset) { |
| break invisibleLoop; |
| } |
| invisibleParametersAnnotationsCounter--; |
| } |
| } |
| } |
| } |
| } |
| if (contentsOffset != annotationAttributeOffset) { |
| int attributeLength = contentsOffset - attributeLengthOffset - 4; |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); |
| contents[attributeLengthOffset++] = (byte) attributeLength; |
| attributesNumber++; |
| } else { |
| contentsOffset = annotationAttributeOffset; |
| } |
| } |
| if (visibleParametersAnnotationsCounter != 0) { |
| if (contentsOffset + 7 >= contents.length) { |
| resizeContents(7); |
| } |
| int attributeNameIndex = |
| constantPool.literalIndex(AttributeNamesConstants.RuntimeVisibleParameterAnnotationsName); |
| contents[contentsOffset++] = (byte) (attributeNameIndex >> 8); |
| contents[contentsOffset++] = (byte) attributeNameIndex; |
| int attributeLengthOffset = contentsOffset; |
| contentsOffset += 4; // leave space for the attribute length |
| |
| contents[contentsOffset++] = (byte) argumentsLength; |
| visibleLoop: for (int i = 0; i < argumentsLength; i++) { |
| if (contentsOffset + 2 >= contents.length) { |
| resizeContents(2); |
| } |
| if (visibleParametersAnnotationsCounter == 0) { |
| contents[contentsOffset++] = (byte) 0; |
| contents[contentsOffset++] = (byte) 0; |
| } else { |
| final int numberOfVisibleAnnotations = annotationsCounters[i][VISIBLE_INDEX]; |
| contents[contentsOffset++] = (byte) (numberOfVisibleAnnotations >> 8); |
| contents[contentsOffset++] = (byte) numberOfVisibleAnnotations; |
| if (numberOfVisibleAnnotations != 0) { |
| Argument argument = arguments[i]; |
| Annotation[] annotations = argument.annotations; |
| for (int j = 0, max = annotations.length; j < max; j++) { |
| Annotation annotation = annotations[j]; |
| if (isRuntimeVisible(annotation)) { |
| generateAnnotation(annotation, annotationAttributeOffset); |
| if (contentsOffset == annotationAttributeOffset) { |
| break visibleLoop; |
| } |
| visibleParametersAnnotationsCounter--; |
| } |
| } |
| } |
| } |
| } |
| if (contentsOffset != annotationAttributeOffset) { |
| int attributeLength = contentsOffset - attributeLengthOffset - 4; |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); |
| contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); |
| contents[attributeLengthOffset++] = (byte) attributeLength; |
| attributesNumber++; |
| } else { |
| contentsOffset = annotationAttributeOffset; |
| } |
| } |
| return attributesNumber; |
| } |
| /** |
| * EXTERNAL API |
| * Answer the actual bytes of the class file |
| * |
| * This method encodes the receiver structure into a byte array which is the content of the classfile. |
| * Returns the byte array that represents the encoded structure of the receiver. |
| * |
| * @return byte[] |
| */ |
| public byte[] getBytes() { |
| byte[] fullContents = new byte[headerOffset + contentsOffset]; |
| System.arraycopy(header, 0, fullContents, 0, headerOffset); |
| System.arraycopy(contents, 0, fullContents, headerOffset, contentsOffset); |
| return fullContents; |
| } |
| |
| /** |
| * EXTERNAL API |
| * Answer the compound name of the class file. |
| * @return char[][] |
| * e.g. {{java}, {util}, {Hashtable}}. |
| */ |
| public char[][] getCompoundName() { |
| return CharOperation.splitOn('/', fileName()); |
| } |
| |
| protected void initByteArrays() { |
| LookupEnvironment env = this.referenceBinding.scope.environment(); |
| synchronized (env) { |
| if (env.sharedArraysUsed) { |
| this.ownSharedArrays = false; |
| int members = referenceBinding.methods().length + referenceBinding.fields().length; |
| this.header = new byte[INITIAL_HEADER_SIZE]; |
| this.contents = new byte[members < 15 ? INITIAL_CONTENTS_SIZE : INITIAL_HEADER_SIZE]; |
| } else { |
| this.ownSharedArrays = env.sharedArraysUsed = true; |
| this.header = env.sharedClassFileHeader; |
| this.contents = env.sharedClassFileContents; |
| } |
| } |
| } |
| |
| |
| private boolean isRuntimeInvisible(Annotation annotation) { |
| final TypeBinding annotationBinding = annotation.resolvedType; |
| if (annotationBinding == null) { |
| return false; |
| } |
| long metaTagBits = annotationBinding.getAnnotationTagBits(); // could be forward reference |
| if ((metaTagBits & TagBits.AnnotationRetentionMASK) == 0) |
| return true; // by default the retention is CLASS |
| |
| return (metaTagBits & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationClassRetention; |
| } |
| |
| private boolean isRuntimeVisible(Annotation annotation) { |
| final TypeBinding annotationBinding = annotation.resolvedType; |
| if (annotationBinding == null) { |
| return false; |
| } |
| long metaTagBits = annotationBinding.getAnnotationTagBits(); |
| if ((metaTagBits & TagBits.AnnotationRetentionMASK) == 0) |
| return false; // by default the retention is CLASS |
| |
| return (metaTagBits & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationRuntimeRetention; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * Returns the most enclosing classfile of the receiver. This is used know to store the constant pool name |
| * for all inner types of the receiver. |
| * @return org.eclipse.jdt.internal.compiler.codegen.ClassFile |
| */ |
| public ClassFile outerMostEnclosingClassFile() { |
| ClassFile current = this; |
| while (current.enclosingClassFile != null) |
| current = current.enclosingClassFile; |
| return current; |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * This is used to store a new inner class. It checks that the binding @binding doesn't already exist inside the |
| * collection of inner classes. Add all the necessary classes in the right order to fit to the specifications. |
| * |
| * @param binding org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding |
| */ |
| public void recordEnclosingTypeAttributes(ReferenceBinding binding) { |
| // add all the enclosing types |
| ReferenceBinding enclosingType = referenceBinding.enclosingType(); |
| int depth = 0; |
| while (enclosingType != null) { |
| depth++; |
| enclosingType = enclosingType.enclosingType(); |
| } |
| enclosingType = referenceBinding; |
| ReferenceBinding enclosingTypes[]; |
| if (depth >= 2) { |
| enclosingTypes = new ReferenceBinding[depth]; |
| for (int i = depth - 1; i >= 0; i--) { |
| enclosingTypes[i] = enclosingType; |
| enclosingType = enclosingType.enclosingType(); |
| } |
| for (int i = 0; i < depth; i++) { |
| addInnerClasses(enclosingTypes[i]); |
| } |
| } else { |
| addInnerClasses(referenceBinding); |
| } |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * This is used to store a new inner class. It checks that the binding @binding doesn't already exist inside the |
| * collection of inner classes. Add all the necessary classes in the right order to fit to the specifications. |
| * |
| * @param binding org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding |
| */ |
| public void recordNestedLocalAttribute(ReferenceBinding binding) { |
| // add all the enclosing types |
| ReferenceBinding enclosingType = referenceBinding.enclosingType(); |
| int depth = 0; |
| while (enclosingType != null) { |
| depth++; |
| enclosingType = enclosingType.enclosingType(); |
| } |
| enclosingType = referenceBinding; |
| ReferenceBinding enclosingTypes[]; |
| if (depth >= 2) { |
| enclosingTypes = new ReferenceBinding[depth]; |
| for (int i = depth - 1; i >= 0; i--) { |
| enclosingTypes[i] = enclosingType; |
| enclosingType = enclosingType.enclosingType(); |
| } |
| for (int i = 0; i < depth; i++) |
| addInnerClasses(enclosingTypes[i]); |
| } else { |
| addInnerClasses(binding); |
| } |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * This is used to store a new inner class. It checks that the binding @binding doesn't already exist inside the |
| * collection of inner classes. Add all the necessary classes in the right order to fit to the specifications. |
| * |
| * @param binding org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding |
| */ |
| public void recordNestedMemberAttribute(ReferenceBinding binding) { |
| addInnerClasses(binding); |
| } |
| |
| /** |
| * Resize the pool contents |
| */ |
| private final void resizeContents(int minimalSize) { |
| int length = this.contents.length; |
| int toAdd = length; |
| if (toAdd < minimalSize) |
| toAdd = minimalSize; |
| System.arraycopy(this.contents, 0, this.contents = new byte[length + toAdd], 0, length); |
| } |
| |
| /** |
| * INTERNAL USE-ONLY |
| * This methods leaves the space for method counts recording. |
| */ |
| public void setForMethodInfos() { |
| // leave some space for the methodCount |
| methodCountOffset = contentsOffset; |
| contentsOffset += 2; |
| } |
| } |