Java tutorial
/******************************************************************************* * Copyright (c) 2000, 2019 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Jesper S Moller - Contributions for * Bug 405066 - [1.8][compiler][codegen] Implement code generation infrastructure for JSR335 * Bug 406982 - [1.8][compiler] Generation of MethodParameters Attribute in classfile * Bug 416885 - [1.8][compiler]IncompatibleClassChange error (edit) * Bug 412149 - [1.8][compiler] Emit repeated annotations into the designated container * Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for * Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work) * Bug 409236 - [1.8][compiler] Type annotations on intersection cast types dropped by code generator * Bug 409246 - [1.8][compiler] Type annotations on catch parameters not handled properly * Bug 415541 - [1.8][compiler] Type annotations in the body of static initializer get dropped * Bug 415399 - [1.8][compiler] Type annotations on constructor results dropped by the code generator * Bug 415470 - [1.8][compiler] Type annotations on class declaration go vanishing * Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas * Bug 434556 - Broken class file generated for incorrect annotation usage * Bug 442416 - $deserializeLambda$ missing cases for nested lambdas * Stephan Herrmann - Contribution for * Bug 438458 - [1.8][null] clean up handling of null type annotations wrt type variables * Olivier Tardieu tardieu@us.ibm.com - Contributions for * Bug 442416 - $deserializeLambda$ missing cases for nested lambdas *******************************************************************************/ package org.eclipse.jdt.internal.compiler; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ExportsStatement; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.FunctionalExpression; import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration; import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation; import org.eclipse.jdt.internal.compiler.ast.OpensStatement; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.Receiver; import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression; import org.eclipse.jdt.internal.compiler.ast.RequiresStatement; import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SwitchStatement; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.AnnotationContext; import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants; import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants; import org.eclipse.jdt.internal.compiler.codegen.CodeStream; import org.eclipse.jdt.internal.compiler.codegen.ConstantPool; import org.eclipse.jdt.internal.compiler.codegen.ExceptionLabel; import org.eclipse.jdt.internal.compiler.codegen.Opcodes; import org.eclipse.jdt.internal.compiler.codegen.StackMapFrame; import org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream; import org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream.ExceptionMarker; import org.eclipse.jdt.internal.compiler.codegen.TypeAnnotationCodeStream; import org.eclipse.jdt.internal.compiler.codegen.VerificationTypeInfo; 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.ArrayBinding; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding; import org.eclipse.jdt.internal.compiler.lookup.PolymorphicMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding; import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.TagBits; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; import org.eclipse.jdt.internal.compiler.problem.AbortMethod; import org.eclipse.jdt.internal.compiler.problem.AbortType; import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; import org.eclipse.jdt.internal.compiler.problem.ShouldNotImplement; import org.eclipse.jdt.internal.compiler.util.Messages; import org.eclipse.jdt.internal.compiler.util.Util; /** * Represents a class file wrapper on bytes, it is aware of its actual * type name. * * Public APIs are listed below: * * byte[] getBytes(); * Answer the actual bytes of the class file * * char[][] getCompoundName(); * Answer the compound name of the class file. * For example, {{java}, {util}, {Hashtable}}. * * byte[] getReducedBytes(); * Answer a smaller byte format, which is only contains some structural * information. Those bytes are decodable with a regular class file reader, * such as DietClassFileReader */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class ClassFile implements TypeConstants, TypeIds { private byte[] bytes; 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 Map<TypeBinding, Boolean> innerClassesBindings; public List bootstrapMethods = null; public int methodCount; public int methodCountOffset; // pool managment boolean isShared = false; // used to generate private access methods // debug and stack map attributes public int produceAttributes; public SourceTypeBinding referenceBinding; public boolean isNestedType; public long targetJDK; public List<TypeBinding> missingTypes = null; public Set visitedTypes; public static final int INITIAL_CONTENTS_SIZE = 400; public static final int INITIAL_HEADER_SIZE = 1500; public static final int INNER_CLASSES_SIZE = 5; public static final int NESTED_MEMBER_SIZE = 5; /** * 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) { createProblemType(typeDeclaration, null, unitResult); } private static void createProblemType(TypeDeclaration typeDeclaration, ClassFile parentClassFile, CompilationResult unitResult) { SourceTypeBinding typeBinding = typeDeclaration.binding; ClassFile classFile = ClassFile.getNewInstance(typeBinding); classFile.initialize(typeBinding, parentClassFile, true); if (typeBinding.hasMemberTypes()) { // see bug 180109 ReferenceBinding[] members = typeBinding.memberTypes; for (int i = 0, l = members.length; i < l; i++) classFile.recordInnerClasses(members[i]); } // TODO (olivier) handle cases where a field cannot be generated (name too long) // TODO (olivier) handle too many methods // inner attributes if (typeBinding.isNestedType()) { classFile.recordInnerClasses(typeBinding); } TypeVariableBinding[] typeVariables = typeBinding.typeVariables(); for (int i = 0, max = typeVariables.length; i < max; i++) { TypeVariableBinding typeVariableBinding = typeVariables[i]; if ((typeVariableBinding.tagBits & TagBits.ContainsNestedTypeReferences) != 0) { Util.recordNestedType(classFile, typeVariableBinding); } } // add its fields FieldBinding[] fields = typeBinding.fields(); if ((fields != null) && (fields != Binding.NO_FIELDS)) { classFile.addFieldInfos(); } else { // we have to set the number of fields to be equals to 0 if (classFile.contentsOffset + 2 >= classFile.contents.length) { classFile.resizeContents(2); } classFile.contents[classFile.contentsOffset++] = 0; classFile.contents[classFile.contentsOffset++] = 0; } // leave some space for the methodCount classFile.setForMethodInfos(); // add its user defined methods int problemsLength; CategorizedProblem[] problems = unitResult.getErrors(); if (problems == null) { problems = new CategorizedProblem[0]; } CategorizedProblem[] problemsCopy = new CategorizedProblem[problemsLength = problems.length]; System.arraycopy(problems, 0, problemsCopy, 0, problemsLength); AbstractMethodDeclaration[] methodDecls = typeDeclaration.methods; boolean abstractMethodsOnly = false; if (methodDecls != null) { if (typeBinding.isInterface()) { if (typeBinding.scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8) abstractMethodsOnly = true; // We generate a clinit which contains all the problems, since we may not be able to generate problem methods (< 1.8) and problem constructors (all levels). classFile.addProblemClinit(problemsCopy); } for (int i = 0, length = methodDecls.length; i < length; i++) { AbstractMethodDeclaration methodDecl = methodDecls[i]; MethodBinding method = methodDecl.binding; if (method == null) continue; if (abstractMethodsOnly) { method.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract; } if (method.isConstructor()) { if (typeBinding.isInterface()) continue; classFile.addProblemConstructor(methodDecl, method, problemsCopy); } else if (method.isAbstract()) { classFile.addAbstractMethod(methodDecl, method); } else { classFile.addProblemMethod(methodDecl, method, problemsCopy); } } // 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.createProblemType(memberType, classFile, unitResult); } } } classFile.addAttributes(); unitResult.record(typeBinding.constantPoolName(), classFile); } public static ClassFile getNewInstance(SourceTypeBinding typeBinding) { LookupEnvironment env = typeBinding.scope.environment(); return env.classFilePool.acquire(typeBinding); } /** * INTERNAL USE-ONLY * This methods creates a new instance of the receiver. */ protected ClassFile() { // default constructor for subclasses } public ClassFile(SourceTypeBinding typeBinding) { // default constructor for subclasses this.constantPool = new ConstantPool(this); final CompilerOptions options = typeBinding.scope.compilerOptions(); this.targetJDK = options.targetJDK; this.produceAttributes = options.produceDebugAttributes; this.referenceBinding = typeBinding; this.isNestedType = typeBinding.isNestedType(); if (this.targetJDK >= ClassFileConstants.JDK1_6) { this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP_TABLE; if (this.targetJDK >= ClassFileConstants.JDK1_8) { this.produceAttributes |= ClassFileConstants.ATTR_TYPE_ANNOTATION; this.codeStream = new TypeAnnotationCodeStream(this); if (options.produceMethodParameters) { this.produceAttributes |= ClassFileConstants.ATTR_METHOD_PARAMETERS; } } else { this.codeStream = new StackMapFrameCodeStream(this); } } else if (this.targetJDK == ClassFileConstants.CLDC_1_1) { this.targetJDK = ClassFileConstants.JDK1_1; // put back 45.3 this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP; this.codeStream = new StackMapFrameCodeStream(this); } else { this.codeStream = new CodeStream(this); } initByteArrays(this.referenceBinding.methods().length + this.referenceBinding.fields().length); } public ClassFile(ModuleBinding moduleBinding, CompilerOptions options) { this.constantPool = new ConstantPool(this); this.targetJDK = options.targetJDK; this.produceAttributes = ClassFileConstants.ATTR_SOURCE; this.isNestedType = false; this.codeStream = new StackMapFrameCodeStream(this); initByteArrays(0); } /** * INTERNAL USE-ONLY * Generate the byte for a problem method info that correspond to a bogus 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) { this.generateMethodInfoHeader(methodBinding); int methodAttributeOffset = this.contentsOffset; int attributeNumber = this.generateMethodInfoAttributes(methodBinding); completeMethodInfo(methodBinding, 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 this.contents[this.methodCountOffset++] = (byte) (this.methodCount >> 8); this.contents[this.methodCountOffset] = (byte) this.methodCount; int attributesNumber = 0; // leave two bytes for the number of attributes and store the current offset int attributeOffset = this.contentsOffset; this.contentsOffset += 2; // source attribute if ((this.produceAttributes & ClassFileConstants.ATTR_SOURCE) != 0) { String fullFileName = new String(this.referenceBinding.scope.referenceCompilationUnit().getFileName()); fullFileName = fullFileName.replace('\\', '/'); int lastIndex = fullFileName.lastIndexOf('/'); if (lastIndex != -1) { fullFileName = fullFileName.substring(lastIndex + 1, fullFileName.length()); } attributesNumber += generateSourceAttribute(fullFileName); } // Deprecated attribute if (this.referenceBinding.isDeprecated()) { // check that there is enough space to write all the bytes for the field info corresponding // to the @fieldBinding attributesNumber += generateDeprecatedAttribute(); } // add signature attribute char[] genericSignature = this.referenceBinding.genericSignature(); if (genericSignature != null) { attributesNumber += generateSignatureAttribute(genericSignature); } if (this.targetJDK >= ClassFileConstants.JDK1_5 && this.referenceBinding.isNestedType() && !this.referenceBinding.isMemberType()) { // add enclosing method attribute (1.5 mode only) attributesNumber += generateEnclosingMethodAttribute(); } if (this.targetJDK >= ClassFileConstants.JDK1_4) { TypeDeclaration typeDeclaration = this.referenceBinding.scope.referenceContext; if (typeDeclaration != null) { final Annotation[] annotations = typeDeclaration.annotations; if (annotations != null) { long targetMask; if (typeDeclaration.isPackageInfo()) targetMask = TagBits.AnnotationForPackage; else if (this.referenceBinding.isAnnotationType()) targetMask = TagBits.AnnotationForType | TagBits.AnnotationForAnnotationType; else targetMask = TagBits.AnnotationForType | TagBits.AnnotationForTypeUse; attributesNumber += generateRuntimeAnnotations(annotations, targetMask); } } } if (this.referenceBinding.isHierarchyInconsistent()) { ReferenceBinding superclass = this.referenceBinding.superclass; if (superclass != null) { this.missingTypes = superclass.collectMissingTypes(this.missingTypes); } ReferenceBinding[] superInterfaces = this.referenceBinding.superInterfaces(); for (int i = 0, max = superInterfaces.length; i < max; i++) { this.missingTypes = superInterfaces[i].collectMissingTypes(this.missingTypes); } attributesNumber += generateHierarchyInconsistentAttribute(); } // Functional expression and lambda bootstrap methods if (this.bootstrapMethods != null && !this.bootstrapMethods.isEmpty()) { attributesNumber += generateBootstrapMethods(this.bootstrapMethods); } // Inner class attribute int numberOfInnerClasses = this.innerClassesBindings == null ? 0 : this.innerClassesBindings.size(); if (numberOfInnerClasses != 0) { ReferenceBinding[] innerClasses = new ReferenceBinding[numberOfInnerClasses]; this.innerClassesBindings.keySet().toArray(innerClasses); Arrays.sort(innerClasses, new Comparator() { @Override public int compare(Object o1, Object o2) { TypeBinding binding1 = (TypeBinding) o1; TypeBinding binding2 = (TypeBinding) o2; Boolean onBottom1 = ClassFile.this.innerClassesBindings.get(o1); Boolean onBottom2 = ClassFile.this.innerClassesBindings.get(o2); if (onBottom1) { if (!onBottom2) { return 1; } } else { if (onBottom2) { return -1; } } return CharOperation.compareTo(binding1.constantPoolName(), binding2.constantPoolName()); } }); attributesNumber += generateInnerClassAttribute(numberOfInnerClasses, innerClasses); } if (this.missingTypes != null) { generateMissingTypesAttribute(); attributesNumber++; } attributesNumber += generateTypeAnnotationAttributeForTypeDeclaration(); if (this.targetJDK >= ClassFileConstants.JDK11) { // add nestMember and nestHost attributes attributesNumber += generateNestAttributes(); } // update the number of attributes if (attributeOffset + 2 >= this.contents.length) { resizeContents(2); } this.contents[attributeOffset++] = (byte) (attributesNumber >> 8); this.contents[attributeOffset] = (byte) attributesNumber; // resynchronize all offsets of the classfile this.header = this.constantPool.poolContent; this.headerOffset = this.constantPool.currentOffset; int constantPoolCount = this.constantPool.currentIndex; this.header[this.constantPoolOffset++] = (byte) (constantPoolCount >> 8); this.header[this.constantPoolOffset] = (byte) constantPoolCount; } /** * INTERNAL USE-ONLY * This methods generate all the module attributes for the receiver. */ public void addModuleAttributes(ModuleBinding module, Annotation[] annotations, CompilationUnitDeclaration cud) { int attributesNumber = 0; // leave two bytes for the number of attributes and store the current offset int attributeOffset = this.contentsOffset; this.contentsOffset += 2; // source attribute if ((this.produceAttributes & ClassFileConstants.ATTR_SOURCE) != 0) { String fullFileName = new String(cud.getFileName()); fullFileName = fullFileName.replace('\\', '/'); int lastIndex = fullFileName.lastIndexOf('/'); if (lastIndex != -1) { fullFileName = fullFileName.substring(lastIndex + 1, fullFileName.length()); } attributesNumber += generateSourceAttribute(fullFileName); } attributesNumber += generateModuleAttribute(cud.moduleDeclaration); if (annotations != null) { long targetMask = TagBits.AnnotationForModule; attributesNumber += generateRuntimeAnnotations(annotations, targetMask); } char[] mainClass = cud.moduleDeclaration.binding.mainClassName; if (mainClass != null) { attributesNumber += generateModuleMainClassAttribute(CharOperation.replaceOnCopy(mainClass, '.', '/')); } char[][] packageNames = cud.moduleDeclaration.binding.getPackageNamesForClassFile(); if (packageNames != null) { attributesNumber += generateModulePackagesAttribute(packageNames); } // update the number of attributes if (attributeOffset + 2 >= this.contents.length) { resizeContents(2); } this.contents[attributeOffset++] = (byte) (attributesNumber >> 8); this.contents[attributeOffset] = (byte) attributesNumber; // resynchronize all offsets of the classfile this.header = this.constantPool.poolContent; this.headerOffset = this.constantPool.currentOffset; int constantPoolCount = this.constantPool.currentIndex; this.header[this.constantPoolOffset++] = (byte) (constantPoolCount >> 8); this.header[this.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 = this.referenceBinding.getDefaultAbstractMethods(); for (int i = 0, max = defaultAbstractMethods.length; i < max; i++) { MethodBinding methodBinding = defaultAbstractMethods[i]; generateMethodInfoHeader(methodBinding); int methodAttributeOffset = this.contentsOffset; int attributeNumber = generateMethodInfoAttributes(methodBinding); completeMethodInfo(methodBinding, 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 Constant fieldConstant = fieldBinding.constant(); if (fieldConstant != Constant.NotAConstant) { attributesNumber += generateConstantValueAttribute(fieldConstant, fieldBinding, fieldAttributeOffset); } if (this.targetJDK < ClassFileConstants.JDK1_5 && fieldBinding.isSynthetic()) { attributesNumber += generateSyntheticAttribute(); } if (fieldBinding.isDeprecated()) { attributesNumber += generateDeprecatedAttribute(); } // add signature attribute char[] genericSignature = fieldBinding.genericSignature(); if (genericSignature != null) { attributesNumber += generateSignatureAttribute(genericSignature); } if (this.targetJDK >= ClassFileConstants.JDK1_4) { FieldDeclaration fieldDeclaration = fieldBinding.sourceField(); if (fieldDeclaration != null) { Annotation[] annotations = fieldDeclaration.annotations; if (annotations != null) { attributesNumber += generateRuntimeAnnotations(annotations, TagBits.AnnotationForField); } if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) { List allTypeAnnotationContexts = new ArrayList(); if (annotations != null && (fieldDeclaration.bits & ASTNode.HasTypeAnnotations) != 0) { fieldDeclaration.getAllAnnotationContexts(AnnotationTargetTypeConstants.FIELD, allTypeAnnotationContexts); } int invisibleTypeAnnotationsCounter = 0; int visibleTypeAnnotationsCounter = 0; TypeReference fieldType = fieldDeclaration.type; if (fieldType != null && ((fieldType.bits & ASTNode.HasTypeAnnotations) != 0)) { fieldType.getAllAnnotationContexts(AnnotationTargetTypeConstants.FIELD, allTypeAnnotationContexts); } int size = allTypeAnnotationContexts.size(); if (size != 0) { AnnotationContext[] allTypeAnnotationContextsArray = new AnnotationContext[size]; allTypeAnnotationContexts.toArray(allTypeAnnotationContextsArray); for (int i = 0, max = allTypeAnnotationContextsArray.length; i < max; i++) { AnnotationContext annotationContext = allTypeAnnotationContextsArray[i]; if ((annotationContext.visibility & AnnotationContext.INVISIBLE) != 0) { invisibleTypeAnnotationsCounter++; allTypeAnnotationContexts.add(annotationContext); } else { visibleTypeAnnotationsCounter++; allTypeAnnotationContexts.add(annotationContext); } } attributesNumber += generateRuntimeTypeAnnotations(allTypeAnnotationContextsArray, visibleTypeAnnotationsCounter, invisibleTypeAnnotationsCounter); } } } } if ((fieldBinding.tagBits & TagBits.HasMissingType) != 0) { this.missingTypes = fieldBinding.type.collectMissingTypes(this.missingTypes); } 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 (this.contentsOffset + 8 >= this.contents.length) { resizeContents(8); } // Now we can generate all entries into the byte array // First the accessFlags int accessFlags = fieldBinding.getAccessFlags(); if (this.targetJDK < ClassFileConstants.JDK1_5) { // pre 1.5, synthetic was an attribute, not a modifier accessFlags &= ~ClassFileConstants.AccSynthetic; } this.contents[this.contentsOffset++] = (byte) (accessFlags >> 8); this.contents[this.contentsOffset++] = (byte) accessFlags; // Then the nameIndex int nameIndex = this.constantPool.literalIndex(fieldBinding.name); this.contents[this.contentsOffset++] = (byte) (nameIndex >> 8); this.contents[this.contentsOffset++] = (byte) nameIndex; // Then the descriptorIndex int descriptorIndex = this.constantPool.literalIndex(fieldBinding.type); this.contents[this.contentsOffset++] = (byte) (descriptorIndex >> 8); this.contents[this.contentsOffset++] = (byte) descriptorIndex; int fieldAttributeOffset = this.contentsOffset; int attributeNumber = 0; // leave some space for the number of attributes this.contentsOffset += 2; attributeNumber += addFieldAttributes(fieldBinding, fieldAttributeOffset); if (this.contentsOffset + 2 >= this.contents.length) { resizeContents(2); } this.contents[fieldAttributeOffset++] = (byte) (attributeNumber >> 8); this.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 = this.referenceBinding; FieldBinding[] syntheticFields = currentBinding.syntheticFields(); int fieldCount = currentBinding.fieldCount() + (syntheticFields == null ? 0 : syntheticFields.length); // write the number of fields if (fieldCount > 0xFFFF) { this.referenceBinding.scope.problemReporter() .tooManyFields(this.referenceBinding.scope.referenceType()); } if (this.contentsOffset + 2 >= this.contents.length) { resizeContents(2); } this.contents[this.contentsOffset++] = (byte) (fieldCount >> 8); this.contents[this.contentsOffset++] = (byte) fieldCount; FieldDeclaration[] fieldDecls = currentBinding.scope.referenceContext.fields; for (int i = 0, max = fieldDecls == null ? 0 : fieldDecls.length; i < max; i++) { FieldDeclaration fieldDecl = fieldDecls[i]; if (fieldDecl.binding != null) { addFieldInfo(fieldDecl.binding); } } if (syntheticFields != null) { for (int i = 0, max = syntheticFields.length; i < max; i++) { addFieldInfo(syntheticFields[i]); } } } private void addMissingAbstractProblemMethod(MethodDeclaration methodDeclaration, MethodBinding methodBinding, CategorizedProblem 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 = this.contentsOffset; int attributeNumber = generateMethodInfoAttributes(methodBinding); // Code attribute attributeNumber++; int codeAttributeOffset = this.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(); this.codeStream.init(this); this.codeStream.preserveUnusedLocals = true; this.codeStream.initializeMaxLocals(methodBinding); // return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "") this.codeStream.generateCodeAttributeForProblemMethod(problemString); completeCodeAttributeForMissingAbstractProblemMethod(methodBinding, codeAttributeOffset, compilationResult.getLineSeparatorPositions(), problem.getSourceLineNumber()); completeMethodInfo(methodBinding, 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(CategorizedProblem[] problems) { generateMethodInfoHeaderForClinit(); // leave two spaces for the number of attributes this.contentsOffset -= 2; int attributeOffset = this.contentsOffset; this.contentsOffset += 2; int attributeNumber = 0; int codeAttributeOffset = this.contentsOffset; generateCodeAttributeHeader(); this.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++) { CategorizedProblem 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, "") this.codeStream.generateCodeAttributeForProblemMethod(problemString); attributeNumber++; // code attribute completeCodeAttributeForClinit(codeAttributeOffset, problemLine, null); if (this.contentsOffset + 2 >= this.contents.length) { resizeContents(2); } this.contents[attributeOffset++] = (byte) (attributeNumber >> 8); this.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, CategorizedProblem[] problems) { if (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 = this.contentsOffset; int attributesNumber = generateMethodInfoAttributes(methodBinding); // Code attribute attributesNumber++; int codeAttributeOffset = this.contentsOffset; generateCodeAttributeHeader(); this.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++) { CategorizedProblem 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, "") this.codeStream.generateCodeAttributeForProblemMethod(problemString); completeCodeAttributeForProblemMethod(method, methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions(), problemLine); completeMethodInfo(methodBinding, methodAttributeOffset, attributesNumber); } /** * 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, CategorizedProblem[] problems, int savedOffset) { // we need to move back the contentsOffset to the value at the beginning of the method this.contentsOffset = savedOffset; this.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, CategorizedProblem[] 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 = this.contentsOffset; int attributesNumber = generateMethodInfoAttributes(methodBinding); // Code attribute attributesNumber++; int codeAttributeOffset = this.contentsOffset; generateCodeAttributeHeader(); this.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++) { CategorizedProblem 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, "") this.codeStream.generateCodeAttributeForProblemMethod(problemString); completeCodeAttributeForProblemMethod(method, methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions(), problemLine); completeMethodInfo(methodBinding, methodAttributeOffset, attributesNumber); } /** * 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, CategorizedProblem[] problems, int savedOffset) { // we need to move back the contentsOffset to the value at the beginning of the method this.contentsOffset = savedOffset; this.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 * - lambda methods. */ public void addSpecialMethods() { // add all methods (default abstract methods and synthetic) // default abstract methods generateMissingAbstractMethods(this.referenceBinding.scope.referenceType().missingAbstractMethods, this.referenceBinding.scope.referenceCompilationUnit().compilationResult); MethodBinding[] defaultAbstractMethods = this.referenceBinding.getDefaultAbstractMethods(); for (int i = 0, max = defaultAbstractMethods.length; i < max; i++) { MethodBinding methodBinding = defaultAbstractMethods[i]; generateMethodInfoHeader(methodBinding); int methodAttributeOffset = this.contentsOffset; int attributeNumber = generateMethodInfoAttributes(methodBinding); completeMethodInfo(methodBinding, methodAttributeOffset, attributeNumber); } // add synthetic methods infos int emittedSyntheticsCount = 0; SyntheticMethodBinding deserializeLambdaMethod = null; boolean continueScanningSynthetics = true; while (continueScanningSynthetics) { continueScanningSynthetics = false; SyntheticMethodBinding[] syntheticMethods = this.referenceBinding.syntheticMethods(); int currentSyntheticsCount = syntheticMethods == null ? 0 : syntheticMethods.length; if (emittedSyntheticsCount != currentSyntheticsCount) { for (int i = emittedSyntheticsCount, max = currentSyntheticsCount; i < max; i++) { SyntheticMethodBinding syntheticMethod = syntheticMethods[i]; switch (syntheticMethod.purpose) { case SyntheticMethodBinding.FieldReadAccess: case SyntheticMethodBinding.SuperFieldReadAccess: // generate a method info to emulate an reading access to // a non-accessible field addSyntheticFieldReadAccessMethod(syntheticMethod); break; case SyntheticMethodBinding.FieldWriteAccess: case SyntheticMethodBinding.SuperFieldWriteAccess: // 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); break; case SyntheticMethodBinding.TooManyEnumsConstants: addSyntheticEnumInitializationMethod(syntheticMethod); break; case SyntheticMethodBinding.LambdaMethod: syntheticMethod.lambda.generateCode(this.referenceBinding.scope, this); continueScanningSynthetics = true; // lambda code generation could schedule additional nested lambdas for code generation. break; case SyntheticMethodBinding.ArrayConstructor: addSyntheticArrayConstructor(syntheticMethod); break; case SyntheticMethodBinding.ArrayClone: addSyntheticArrayClone(syntheticMethod); break; case SyntheticMethodBinding.FactoryMethod: addSyntheticFactoryMethod(syntheticMethod); break; case SyntheticMethodBinding.DeserializeLambda: deserializeLambdaMethod = syntheticMethod; // delay processing break; case SyntheticMethodBinding.SerializableMethodReference: // Nothing to be done break; } } emittedSyntheticsCount = currentSyntheticsCount; } } if (deserializeLambdaMethod != null) { int problemResetPC = 0; this.codeStream.wideMode = false; boolean restart = false; do { try { problemResetPC = this.contentsOffset; addSyntheticDeserializeLambda(deserializeLambdaMethod, this.referenceBinding.syntheticMethods()); restart = false; } catch (AbortMethod e) { // Restart code generation if possible ... if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) { // a branch target required a goto_w, restart code generation in wide mode. this.contentsOffset = problemResetPC; this.methodCount--; this.codeStream.resetInWideMode(); // request wide mode restart = true; } else { throw new AbortType(this.referenceBinding.scope.referenceContext.compilationResult, e.problem); } } } while (restart); } } public void addSyntheticArrayConstructor(SyntheticMethodBinding methodBinding) { generateMethodInfoHeader(methodBinding); int methodAttributeOffset = this.contentsOffset; // this will add exception attribute, synthetic attribute, deprecated attribute,... int attributeNumber = generateMethodInfoAttributes(methodBinding); // Code attribute int codeAttributeOffset = this.contentsOffset; attributeNumber++; // add code attribute generateCodeAttributeHeader(); this.codeStream.init(this); this.codeStream.generateSyntheticBodyForArrayConstructor(methodBinding); completeCodeAttributeForSyntheticMethod(methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions()); // update the number of attributes this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); this.contents[methodAttributeOffset] = (byte) attributeNumber; } public void addSyntheticArrayClone(SyntheticMethodBinding methodBinding) { generateMethodInfoHeader(methodBinding); int methodAttributeOffset = this.contentsOffset; // this will add exception attribute, synthetic attribute, deprecated attribute,... int attributeNumber = generateMethodInfoAttributes(methodBinding); // Code attribute int codeAttributeOffset = this.contentsOffset; attributeNumber++; // add code attribute generateCodeAttributeHeader(); this.codeStream.init(this); this.codeStream.generateSyntheticBodyForArrayClone(methodBinding); completeCodeAttributeForSyntheticMethod(methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions()); // update the number of attributes this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); this.contents[methodAttributeOffset] = (byte) attributeNumber; } public void addSyntheticFactoryMethod(SyntheticMethodBinding methodBinding) { generateMethodInfoHeader(methodBinding); int methodAttributeOffset = this.contentsOffset; // this will add exception attribute, synthetic attribute, deprecated attribute,... int attributeNumber = generateMethodInfoAttributes(methodBinding); // Code attribute int codeAttributeOffset = this.contentsOffset; attributeNumber++; // add code attribute generateCodeAttributeHeader(); this.codeStream.init(this); this.codeStream.generateSyntheticBodyForFactoryMethod(methodBinding); completeCodeAttributeForSyntheticMethod(methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions()); // update the number of attributes this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); this.contents[methodAttributeOffset] = (byte) attributeNumber; } /** * 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 = generateMethodInfoAttributes(methodBinding); // Code attribute int codeAttributeOffset = this.contentsOffset; attributeNumber++; // add code attribute generateCodeAttributeHeader(); this.codeStream.init(this); this.codeStream.generateSyntheticBodyForConstructorAccess(methodBinding); completeCodeAttributeForSyntheticMethod(methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions()); // update the number of attributes this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); this.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 = generateMethodInfoAttributes(methodBinding); // Code attribute int codeAttributeOffset = this.contentsOffset; attributeNumber++; // add code attribute generateCodeAttributeHeader(); this.codeStream.init(this); this.codeStream.generateSyntheticBodyForEnumValueOf(methodBinding); completeCodeAttributeForSyntheticMethod(methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions()); // update the number of attributes if ((this.produceAttributes & ClassFileConstants.ATTR_METHOD_PARAMETERS) != 0) { attributeNumber += generateMethodParameters(methodBinding); } this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); this.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 = generateMethodInfoAttributes(methodBinding); // Code attribute int codeAttributeOffset = this.contentsOffset; attributeNumber++; // add code attribute generateCodeAttributeHeader(); this.codeStream.init(this); this.codeStream.generateSyntheticBodyForEnumValues(methodBinding); completeCodeAttributeForSyntheticMethod(methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions()); // update the number of attributes this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); this.contents[methodAttributeOffset] = (byte) attributeNumber; } public void addSyntheticEnumInitializationMethod(SyntheticMethodBinding methodBinding) { generateMethodInfoHeader(methodBinding); int methodAttributeOffset = this.contentsOffset; // this will add exception attribute, synthetic attribute, deprecated attribute,... int attributeNumber = generateMethodInfoAttributes(methodBinding); // Code attribute int codeAttributeOffset = this.contentsOffset; attributeNumber++; // add code attribute generateCodeAttributeHeader(); this.codeStream.init(this); this.codeStream.generateSyntheticBodyForEnumInitializationMethod(methodBinding); completeCodeAttributeForSyntheticMethod(methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions()); // update the number of attributes this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); this.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 = generateMethodInfoAttributes(methodBinding); // Code attribute int codeAttributeOffset = this.contentsOffset; attributeNumber++; // add code attribute generateCodeAttributeHeader(); this.codeStream.init(this); this.codeStream.generateSyntheticBodyForFieldReadAccess(methodBinding); completeCodeAttributeForSyntheticMethod(methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions()); // update the number of attributes this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); this.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 = generateMethodInfoAttributes(methodBinding); // Code attribute int codeAttributeOffset = this.contentsOffset; attributeNumber++; // add code attribute generateCodeAttributeHeader(); this.codeStream.init(this); this.codeStream.generateSyntheticBodyForFieldWriteAccess(methodBinding); completeCodeAttributeForSyntheticMethod(methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions()); // update the number of attributes this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); this.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 = generateMethodInfoAttributes(methodBinding); // Code attribute int codeAttributeOffset = this.contentsOffset; attributeNumber++; // add code attribute generateCodeAttributeHeader(); this.codeStream.init(this); this.codeStream.generateSyntheticBodyForMethodAccess(methodBinding); completeCodeAttributeForSyntheticMethod(methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions()); // update the number of attributes this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); this.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 = generateMethodInfoAttributes(methodBinding); // Code attribute int codeAttributeOffset = this.contentsOffset; attributeNumber++; // add code attribute generateCodeAttributeHeader(); this.codeStream.init(this); this.codeStream.generateSyntheticBodyForSwitchTable(methodBinding); int code_length = this.codeStream.position; if (code_length > 65535) { SwitchStatement switchStatement = methodBinding.switchStatement; if (switchStatement != null) { switchStatement.scope.problemReporter().bytecodeExceeds64KLimit(switchStatement); } } completeCodeAttributeForSyntheticMethod(true, methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions(), ((SourceTypeBinding) methodBinding.declaringClass).scope); // update the number of attributes this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); this.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, MethodScope scope) { // reinitialize the localContents with the byte modified by the code stream this.contents = this.codeStream.bCodeStream; int localContentsOffset = this.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 = this.codeStream.position; if (code_length > 65535) { if (this.codeStream.methodDeclaration != null) { this.codeStream.methodDeclaration.scope.problemReporter() .bytecodeExceeds64KLimit(this.codeStream.methodDeclaration); } else { this.codeStream.lambdaExpression.scope.problemReporter() .bytecodeExceeds64KLimit(this.codeStream.lambdaExpression); } } if (localContentsOffset + 20 >= this.contents.length) { resizeContents(20); } int max_stack = this.codeStream.stackMax; this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); this.contents[codeAttributeOffset + 7] = (byte) max_stack; int max_locals = this.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; boolean addStackMaps = (this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0; // write the exception table ExceptionLabel[] exceptionLabels = this.codeStream.exceptionLabels; int exceptionHandlersCount = 0; // each label holds one handler per range (start/end contiguous) for (int i = 0, length = this.codeStream.exceptionLabelsCounter; i < length; i++) { exceptionHandlersCount += this.codeStream.exceptionLabels[i].getCount() / 2; } int exSize = exceptionHandlersCount * 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) (exceptionHandlersCount >> 8); this.contents[localContentsOffset++] = (byte) exceptionHandlersCount; for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) { ExceptionLabel exceptionLabel = exceptionLabels[i]; if (exceptionLabel != null) { int iRange = 0, maxRange = exceptionLabel.getCount(); if ((maxRange & 1) != 0) { if (this.codeStream.methodDeclaration != null) { this.codeStream.methodDeclaration.scope.problemReporter().abortDueToInternalError( Messages.bind(Messages.abort_invalidExceptionAttribute, new String(this.codeStream.methodDeclaration.selector)), this.codeStream.methodDeclaration); } else { this.codeStream.lambdaExpression.scope.problemReporter().abortDueToInternalError( Messages.bind(Messages.abort_invalidExceptionAttribute, new String(this.codeStream.lambdaExpression.binding.selector)), this.codeStream.lambdaExpression); } } while (iRange < maxRange) { int start = exceptionLabel.ranges[iRange++]; // even ranges are start positions this.contents[localContentsOffset++] = (byte) (start >> 8); this.contents[localContentsOffset++] = (byte) start; int end = exceptionLabel.ranges[iRange++]; // odd ranges are end positions this.contents[localContentsOffset++] = (byte) (end >> 8); this.contents[localContentsOffset++] = (byte) end; int handlerPC = exceptionLabel.position; if (addStackMaps) { StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream; stackMapFrameCodeStream.addFramePosition(handlerPC); // stackMapFrameCodeStream.addExceptionMarker(handlerPC, exceptionLabel.exceptionType); } this.contents[localContentsOffset++] = (byte) (handlerPC >> 8); this.contents[localContentsOffset++] = (byte) handlerPC; if (exceptionLabel.exceptionType == null) { // any exception handler this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; } else { int nameIndex; if (exceptionLabel.exceptionType == TypeBinding.NULL) { /* represents ClassNotFoundException, see class literal access*/ nameIndex = this.constantPool.literalIndexForType( ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName); } else { nameIndex = this.constantPool.literalIndexForType(exceptionLabel.exceptionType); } this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; } } } } // debug attributes int codeAttributeAttributeOffset = localContentsOffset; int attributesNumber = 0; // leave two bytes for the attribute_length localContentsOffset += 2; if (localContentsOffset + 2 >= this.contents.length) { resizeContents(2); } this.contentsOffset = localContentsOffset; // first we handle the linenumber attribute if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) { attributesNumber += generateLineNumberAttribute(); } // then we do the local variable attribute if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) { final boolean methodDeclarationIsStatic = this.codeStream.methodDeclaration != null ? this.codeStream.methodDeclaration.isStatic() : this.codeStream.lambdaExpression.binding.isStatic(); attributesNumber += generateLocalVariableTableAttribute(code_length, methodDeclarationIsStatic, false); } if (addStackMaps) { attributesNumber += generateStackMapTableAttribute( this.codeStream.methodDeclaration != null ? this.codeStream.methodDeclaration.binding : this.codeStream.lambdaExpression.binding, code_length, codeAttributeOffset, max_locals, false, scope); } if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) { attributesNumber += generateStackMapAttribute( this.codeStream.methodDeclaration != null ? this.codeStream.methodDeclaration.binding : this.codeStream.lambdaExpression.binding, code_length, codeAttributeOffset, max_locals, false, scope); } if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) { attributesNumber += generateTypeAnnotationsOnCodeAttribute(); } this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8); this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber; // update the attribute length int codeAttributeLength = this.contentsOffset - (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; } public int generateTypeAnnotationsOnCodeAttribute() { int attributesNumber = 0; List allTypeAnnotationContexts = ((TypeAnnotationCodeStream) this.codeStream).allTypeAnnotationContexts; int invisibleTypeAnnotationsCounter = 0; int visibleTypeAnnotationsCounter = 0; for (int i = 0, max = this.codeStream.allLocalsCounter; i < max; i++) { LocalVariableBinding localVariable = this.codeStream.locals[i]; if (localVariable.isCatchParameter()) continue; LocalDeclaration declaration = localVariable.declaration; if (declaration == null || (declaration.isArgument() && ((declaration.bits & ASTNode.IsUnionType) == 0)) || (localVariable.initializationCount == 0) || ((declaration.bits & ASTNode.HasTypeAnnotations) == 0)) { continue; } int targetType = ((localVariable.tagBits & TagBits.IsResource) == 0) ? AnnotationTargetTypeConstants.LOCAL_VARIABLE : AnnotationTargetTypeConstants.RESOURCE_VARIABLE; declaration.getAllAnnotationContexts(targetType, localVariable, allTypeAnnotationContexts); } ExceptionLabel[] exceptionLabels = this.codeStream.exceptionLabels; for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) { ExceptionLabel exceptionLabel = exceptionLabels[i]; if (exceptionLabel.exceptionTypeReference != null && (exceptionLabel.exceptionTypeReference.bits & ASTNode.HasTypeAnnotations) != 0) { exceptionLabel.exceptionTypeReference.getAllAnnotationContexts( AnnotationTargetTypeConstants.EXCEPTION_PARAMETER, i, allTypeAnnotationContexts, exceptionLabel.se7Annotations); } } int size = allTypeAnnotationContexts.size(); if (size != 0) { AnnotationContext[] allTypeAnnotationContextsArray = new AnnotationContext[size]; allTypeAnnotationContexts.toArray(allTypeAnnotationContextsArray); for (int j = 0, max2 = allTypeAnnotationContextsArray.length; j < max2; j++) { AnnotationContext annotationContext = allTypeAnnotationContextsArray[j]; if ((annotationContext.visibility & AnnotationContext.INVISIBLE) != 0) { invisibleTypeAnnotationsCounter++; } else { visibleTypeAnnotationsCounter++; } } attributesNumber += generateRuntimeTypeAnnotations(allTypeAnnotationContextsArray, visibleTypeAnnotationsCounter, invisibleTypeAnnotationsCounter); } return attributesNumber; } /** * 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, Scope scope) { // reinitialize the contents with the byte modified by the code stream this.contents = this.codeStream.bCodeStream; int localContentsOffset = this.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 = this.codeStream.position; if (code_length > 65535) { this.codeStream.methodDeclaration.scope.problemReporter() .bytecodeExceeds64KLimit(this.codeStream.methodDeclaration.scope.referenceType()); } if (localContentsOffset + 20 >= this.contents.length) { resizeContents(20); } int max_stack = this.codeStream.stackMax; this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); this.contents[codeAttributeOffset + 7] = (byte) max_stack; int max_locals = this.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; boolean addStackMaps = (this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0; // write the exception table ExceptionLabel[] exceptionLabels = this.codeStream.exceptionLabels; int exceptionHandlersCount = 0; // each label holds one handler per range (start/end contiguous) for (int i = 0, length = this.codeStream.exceptionLabelsCounter; i < length; i++) { exceptionHandlersCount += this.codeStream.exceptionLabels[i].getCount() / 2; } int exSize = exceptionHandlersCount * 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) (exceptionHandlersCount >> 8); this.contents[localContentsOffset++] = (byte) exceptionHandlersCount; for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) { ExceptionLabel exceptionLabel = exceptionLabels[i]; if (exceptionLabel != null) { int iRange = 0, maxRange = exceptionLabel.getCount(); if ((maxRange & 1) != 0) { this.codeStream.methodDeclaration.scope.problemReporter().abortDueToInternalError( Messages.bind(Messages.abort_invalidExceptionAttribute, new String(this.codeStream.methodDeclaration.selector)), this.codeStream.methodDeclaration); } while (iRange < maxRange) { int start = exceptionLabel.ranges[iRange++]; // even ranges are start positions this.contents[localContentsOffset++] = (byte) (start >> 8); this.contents[localContentsOffset++] = (byte) start; int end = exceptionLabel.ranges[iRange++]; // odd ranges are end positions this.contents[localContentsOffset++] = (byte) (end >> 8); this.contents[localContentsOffset++] = (byte) end; int handlerPC = exceptionLabel.position; this.contents[localContentsOffset++] = (byte) (handlerPC >> 8); this.contents[localContentsOffset++] = (byte) handlerPC; if (addStackMaps) { StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream; stackMapFrameCodeStream.addFramePosition(handlerPC); // stackMapFrameCodeStream.addExceptionMarker(handlerPC, exceptionLabel.exceptionType); } if (exceptionLabel.exceptionType == null) { // any exception handler this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; } else { int nameIndex; if (exceptionLabel.exceptionType == TypeBinding.NULL) { /* represents denote ClassNotFoundException, see class literal access*/ nameIndex = this.constantPool.literalIndexForType( ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName); } else { nameIndex = this.constantPool.literalIndexForType(exceptionLabel.exceptionType); } this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; } } } } // debug attributes int codeAttributeAttributeOffset = localContentsOffset; int attributesNumber = 0; // leave two bytes for the attribute_length localContentsOffset += 2; if (localContentsOffset + 2 >= this.contents.length) { resizeContents(2); } this.contentsOffset = localContentsOffset; // first we handle the linenumber attribute if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) { attributesNumber += generateLineNumberAttribute(); } // then we do the local variable attribute if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) { attributesNumber += generateLocalVariableTableAttribute(code_length, true, false); } if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0) { attributesNumber += generateStackMapTableAttribute(null, code_length, codeAttributeOffset, max_locals, true, scope); } if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) { attributesNumber += generateStackMapAttribute(null, code_length, codeAttributeOffset, max_locals, true, scope); } if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) { attributesNumber += generateTypeAnnotationsOnCodeAttribute(); } // 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) (attributesNumber >> 8); this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber; // update the attribute length int codeAttributeLength = this.contentsOffset - (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; } /** * 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, MethodScope scope) { // reinitialize the contents with the byte modified by the code stream this.contents = this.codeStream.bCodeStream; int localContentsOffset = this.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 = this.codeStream.position; if (code_length > 65535) { this.codeStream.methodDeclaration.scope.problemReporter() .bytecodeExceeds64KLimit(this.codeStream.methodDeclaration.scope.referenceType()); } if (localContentsOffset + 20 >= this.contents.length) { resizeContents(20); } int max_stack = this.codeStream.stackMax; this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); this.contents[codeAttributeOffset + 7] = (byte) max_stack; int max_locals = this.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 attributesNumber = 0; // leave two bytes for the attribute_length localContentsOffset += 2; // first we handle the linenumber attribute if (localContentsOffset + 2 >= this.contents.length) { resizeContents(2); } this.contentsOffset = localContentsOffset; // first we handle the linenumber attribute if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) { attributesNumber += generateLineNumberAttribute(problemLine); } localContentsOffset = this.contentsOffset; // then we do the local variable attribute if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) { int localVariableNameIndex = this.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; attributesNumber++; } this.contentsOffset = localContentsOffset; if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0) { attributesNumber += generateStackMapTableAttribute(null, code_length, codeAttributeOffset, max_locals, true, scope); } if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) { attributesNumber += generateStackMapAttribute(null, code_length, codeAttributeOffset, max_locals, true, scope); } if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) { attributesNumber += generateTypeAnnotationsOnCodeAttribute(); } // 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) (attributesNumber >> 8); this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber; // update the attribute length int codeAttributeLength = this.contentsOffset - (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; } /** * */ public void completeCodeAttributeForMissingAbstractProblemMethod(MethodBinding binding, int codeAttributeOffset, int[] startLineIndexes, int problemLine) { // reinitialize the localContents with the byte modified by the code stream this.contents = this.codeStream.bCodeStream; int localContentsOffset = this.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 = this.codeStream.stackMax; this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); this.contents[codeAttributeOffset + 7] = (byte) max_stack; int max_locals = this.codeStream.maxLocals; this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); this.contents[codeAttributeOffset + 9] = (byte) max_locals; int code_length = this.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 attributesNumber = 0; // leave two bytes for the attribute_length localContentsOffset += 2; // first we handle the linenumber attribute if (localContentsOffset + 2 >= this.contents.length) { resizeContents(2); } this.contentsOffset = localContentsOffset; if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) { if (problemLine == 0) { problemLine = Util.getLineNumber(binding.sourceStart(), startLineIndexes, 0, startLineIndexes.length - 1); } attributesNumber += generateLineNumberAttribute(problemLine); } if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0) { attributesNumber += generateStackMapTableAttribute(binding, code_length, codeAttributeOffset, max_locals, false, null); } if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) { attributesNumber += generateStackMapAttribute(binding, code_length, codeAttributeOffset, max_locals, false, null); } // 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) (attributesNumber >> 8); this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber; // update the attribute length int codeAttributeLength = this.contentsOffset - (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; } /** * 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 = this.codeStream.bCodeStream; int localContentsOffset = this.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 = this.codeStream.stackMax; this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); this.contents[codeAttributeOffset + 7] = (byte) max_stack; int max_locals = this.codeStream.maxLocals; this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); this.contents[codeAttributeOffset + 9] = (byte) max_locals; int code_length = this.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 attributesNumber = 0; // leave two bytes for the attribute_length localContentsOffset += 2; // first we handle the linenumber attribute if (localContentsOffset + 2 >= this.contents.length) { resizeContents(2); } this.contentsOffset = localContentsOffset; if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) { if (problemLine == 0) { problemLine = Util.getLineNumber(binding.sourceStart(), startLineIndexes, 0, startLineIndexes.length - 1); } attributesNumber += generateLineNumberAttribute(problemLine); } // then we do the local variable attribute if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) { final boolean methodDeclarationIsStatic = this.codeStream.methodDeclaration.isStatic(); attributesNumber += generateLocalVariableTableAttribute(code_length, methodDeclarationIsStatic, false); } if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0) { attributesNumber += generateStackMapTableAttribute(binding, code_length, codeAttributeOffset, max_locals, false, null); } if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) { attributesNumber += generateStackMapAttribute(binding, code_length, codeAttributeOffset, max_locals, false, null); } // 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) (attributesNumber >> 8); this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber; // update the attribute length int codeAttributeLength = this.contentsOffset - (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; } /** * 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, Scope scope) { // reinitialize the contents with the byte modified by the code stream this.contents = this.codeStream.bCodeStream; int localContentsOffset = this.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 = this.codeStream.stackMax; this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); this.contents[codeAttributeOffset + 7] = (byte) max_stack; int max_locals = this.codeStream.maxLocals; this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); this.contents[codeAttributeOffset + 9] = (byte) max_locals; int code_length = this.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; if ((localContentsOffset + 40) >= this.contents.length) { resizeContents(40); } boolean addStackMaps = (this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0; if (hasExceptionHandlers) { // write the exception table ExceptionLabel[] exceptionLabels = this.codeStream.exceptionLabels; int exceptionHandlersCount = 0; // each label holds one handler per range (start/end contiguous) for (int i = 0, length = this.codeStream.exceptionLabelsCounter; i < length; i++) { exceptionHandlersCount += this.codeStream.exceptionLabels[i].getCount() / 2; } int exSize = exceptionHandlersCount * 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) (exceptionHandlersCount >> 8); this.contents[localContentsOffset++] = (byte) exceptionHandlersCount; for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) { ExceptionLabel exceptionLabel = exceptionLabels[i]; if (exceptionLabel != null) { int iRange = 0, maxRange = exceptionLabel.getCount(); if ((maxRange & 1) != 0) { this.referenceBinding.scope.problemReporter() .abortDueToInternalError(Messages.bind(Messages.abort_invalidExceptionAttribute, new String(binding.selector), this.referenceBinding.scope.problemReporter().referenceContext)); } while (iRange < maxRange) { int start = exceptionLabel.ranges[iRange++]; // even ranges are start positions this.contents[localContentsOffset++] = (byte) (start >> 8); this.contents[localContentsOffset++] = (byte) start; int end = exceptionLabel.ranges[iRange++]; // odd ranges are end positions this.contents[localContentsOffset++] = (byte) (end >> 8); this.contents[localContentsOffset++] = (byte) end; int handlerPC = exceptionLabel.position; if (addStackMaps) { StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream; stackMapFrameCodeStream.addFramePosition(handlerPC); } this.contents[localContentsOffset++] = (byte) (handlerPC >> 8); this.contents[localContentsOffset++] = (byte) handlerPC; if (exceptionLabel.exceptionType == null) { // any exception handler this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; } else { int nameIndex; switch (exceptionLabel.exceptionType.id) { case T_null: /* represents ClassNotFoundException, see class literal access*/ nameIndex = this.constantPool.literalIndexForType( ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName); break; case T_long: /* represents NoSuchFieldError, see switch table generation*/ nameIndex = this.constantPool .literalIndexForType(ConstantPool.JavaLangNoSuchFieldErrorConstantPoolName); break; default: nameIndex = this.constantPool.literalIndexForType(exceptionLabel.exceptionType); } 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 this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; } // debug attributes int codeAttributeAttributeOffset = localContentsOffset; int attributesNumber = 0; // leave two bytes for the attribute_length localContentsOffset += 2; if (localContentsOffset + 2 >= this.contents.length) { resizeContents(2); } this.contentsOffset = localContentsOffset; // first we handle the linenumber attribute if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) { int lineNumber = Util.getLineNumber(binding.sourceStart, startLineIndexes, 0, startLineIndexes.length - 1); attributesNumber += generateLineNumberAttribute(lineNumber); } // then we do the local variable attribute if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) { final boolean methodDeclarationIsStatic = binding.isStatic(); attributesNumber += generateLocalVariableTableAttribute(code_length, methodDeclarationIsStatic, true); } if (addStackMaps) { attributesNumber += generateStackMapTableAttribute(binding, code_length, codeAttributeOffset, max_locals, false, scope); } if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) { attributesNumber += generateStackMapAttribute(binding, code_length, codeAttributeOffset, max_locals, false, scope); } // 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) (attributesNumber >> 8); this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber; // update the attribute length int codeAttributeLength = this.contentsOffset - (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; } /** * 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, ((SourceTypeBinding) binding.declaringClass).scope); } private void completeArgumentAnnotationInfo(Argument[] arguments, List allAnnotationContexts) { for (int i = 0, max = arguments.length; i < max; i++) { Argument argument = arguments[i]; if ((argument.bits & ASTNode.HasTypeAnnotations) != 0) { argument.getAllAnnotationContexts(AnnotationTargetTypeConstants.METHOD_FORMAL_PARAMETER, i, allAnnotationContexts); } } } /** * 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 attributesNumber <CODE>int</CODE> */ public void completeMethodInfo(MethodBinding binding, int methodAttributeOffset, int attributesNumber) { if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) { List allTypeAnnotationContexts = new ArrayList(); int invisibleTypeAnnotationsCounter = 0; int visibleTypeAnnotationsCounter = 0; AbstractMethodDeclaration methodDeclaration = binding.sourceMethod(); if (methodDeclaration != null) { if ((methodDeclaration.bits & ASTNode.HasTypeAnnotations) != 0) { Argument[] arguments = methodDeclaration.arguments; if (arguments != null) { completeArgumentAnnotationInfo(arguments, allTypeAnnotationContexts); } Receiver receiver = methodDeclaration.receiver; if (receiver != null && (receiver.type.bits & ASTNode.HasTypeAnnotations) != 0) { receiver.type.getAllAnnotationContexts(AnnotationTargetTypeConstants.METHOD_RECEIVER, allTypeAnnotationContexts); } } Annotation[] annotations = methodDeclaration.annotations; if (annotations != null && !methodDeclaration.isClinit() && (methodDeclaration.isConstructor() || binding.returnType.id != T_void)) { methodDeclaration.getAllAnnotationContexts(AnnotationTargetTypeConstants.METHOD_RETURN, allTypeAnnotationContexts); } if (!methodDeclaration.isConstructor() && !methodDeclaration.isClinit() && binding.returnType.id != T_void) { MethodDeclaration declaration = (MethodDeclaration) methodDeclaration; TypeReference typeReference = declaration.returnType; if ((typeReference.bits & ASTNode.HasTypeAnnotations) != 0) { typeReference.getAllAnnotationContexts(AnnotationTargetTypeConstants.METHOD_RETURN, allTypeAnnotationContexts); } } TypeReference[] thrownExceptions = methodDeclaration.thrownExceptions; if (thrownExceptions != null) { for (int i = 0, max = thrownExceptions.length; i < max; i++) { TypeReference thrownException = thrownExceptions[i]; thrownException.getAllAnnotationContexts(AnnotationTargetTypeConstants.THROWS, i, allTypeAnnotationContexts); } } TypeParameter[] typeParameters = methodDeclaration.typeParameters(); if (typeParameters != null) { for (int i = 0, max = typeParameters.length; i < max; i++) { TypeParameter typeParameter = typeParameters[i]; if ((typeParameter.bits & ASTNode.HasTypeAnnotations) != 0) { typeParameter.getAllAnnotationContexts( AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER, i, allTypeAnnotationContexts); } } } } else if (binding.sourceLambda() != null) { // SyntheticMethodBinding, purpose : LambdaMethod. LambdaExpression lambda = binding.sourceLambda(); if ((lambda.bits & ASTNode.HasTypeAnnotations) != 0) { if (lambda.arguments != null) completeArgumentAnnotationInfo(lambda.arguments, allTypeAnnotationContexts); } } int size = allTypeAnnotationContexts.size(); if (size != 0) { AnnotationContext[] allTypeAnnotationContextsArray = new AnnotationContext[size]; allTypeAnnotationContexts.toArray(allTypeAnnotationContextsArray); for (int j = 0, max2 = allTypeAnnotationContextsArray.length; j < max2; j++) { AnnotationContext annotationContext = allTypeAnnotationContextsArray[j]; if ((annotationContext.visibility & AnnotationContext.INVISIBLE) != 0) { invisibleTypeAnnotationsCounter++; } else { visibleTypeAnnotationsCounter++; } } attributesNumber += generateRuntimeTypeAnnotations(allTypeAnnotationContextsArray, visibleTypeAnnotationsCounter, invisibleTypeAnnotationsCounter); } } if ((this.produceAttributes & ClassFileConstants.ATTR_METHOD_PARAMETERS) != 0) { attributesNumber += generateMethodParameters(binding); } // update the number of attributes this.contents[methodAttributeOffset++] = (byte) (attributesNumber >> 8); this.contents[methodAttributeOffset] = (byte) attributesNumber; } private void dumpLocations(int[] locations) { if (locations == null) { // no type path if (this.contentsOffset + 1 >= this.contents.length) { resizeContents(1); } this.contents[this.contentsOffset++] = (byte) 0; } else { int length = locations.length; if (this.contentsOffset + length >= this.contents.length) { resizeContents(length + 1); } this.contents[this.contentsOffset++] = (byte) (locations.length / 2); for (int i = 0; i < length; i++) { this.contents[this.contentsOffset++] = (byte) locations[i]; } } } private void dumpTargetTypeContents(int targetType, AnnotationContext annotationContext) { switch (targetType) { case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER: case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER: // parameter index this.contents[this.contentsOffset++] = (byte) annotationContext.info; break; case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER_BOUND: // type_parameter_index this.contents[this.contentsOffset++] = (byte) annotationContext.info; // bound_index this.contents[this.contentsOffset++] = (byte) annotationContext.info2; break; case AnnotationTargetTypeConstants.FIELD: case AnnotationTargetTypeConstants.METHOD_RECEIVER: case AnnotationTargetTypeConstants.METHOD_RETURN: // target_info is empty_target break; case AnnotationTargetTypeConstants.METHOD_FORMAL_PARAMETER: // target_info is parameter index this.contents[this.contentsOffset++] = (byte) annotationContext.info; break; case AnnotationTargetTypeConstants.INSTANCEOF: case AnnotationTargetTypeConstants.NEW: case AnnotationTargetTypeConstants.EXCEPTION_PARAMETER: case AnnotationTargetTypeConstants.CONSTRUCTOR_REFERENCE: case AnnotationTargetTypeConstants.METHOD_REFERENCE: // bytecode offset for new/instanceof/method_reference // exception table entry index for exception_parameter this.contents[this.contentsOffset++] = (byte) (annotationContext.info >> 8); this.contents[this.contentsOffset++] = (byte) annotationContext.info; break; case AnnotationTargetTypeConstants.CAST: // bytecode offset this.contents[this.contentsOffset++] = (byte) (annotationContext.info >> 8); this.contents[this.contentsOffset++] = (byte) annotationContext.info; this.contents[this.contentsOffset++] = (byte) annotationContext.info2; break; case AnnotationTargetTypeConstants.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: case AnnotationTargetTypeConstants.METHOD_INVOCATION_TYPE_ARGUMENT: case AnnotationTargetTypeConstants.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: case AnnotationTargetTypeConstants.METHOD_REFERENCE_TYPE_ARGUMENT: // bytecode offset this.contents[this.contentsOffset++] = (byte) (annotationContext.info >> 8); this.contents[this.contentsOffset++] = (byte) annotationContext.info; // type_argument_index this.contents[this.contentsOffset++] = (byte) annotationContext.info2; break; case AnnotationTargetTypeConstants.CLASS_EXTENDS: case AnnotationTargetTypeConstants.THROWS: // For CLASS_EXTENDS - info is supertype index (-1 = superclass) // For THROWS - info is exception table index this.contents[this.contentsOffset++] = (byte) (annotationContext.info >> 8); this.contents[this.contentsOffset++] = (byte) annotationContext.info; break; case AnnotationTargetTypeConstants.LOCAL_VARIABLE: case AnnotationTargetTypeConstants.RESOURCE_VARIABLE: int localVariableTableOffset = this.contentsOffset; LocalVariableBinding localVariable = annotationContext.variableBinding; int actualSize = 0; int initializationCount = localVariable.initializationCount; actualSize += 2 /* for number of entries */ + (6 * initializationCount); // reserve enough space if (this.contentsOffset + actualSize >= this.contents.length) { resizeContents(actualSize); } this.contentsOffset += 2; int numberOfEntries = 0; for (int j = 0; j < 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 numberOfEntries++; this.contents[this.contentsOffset++] = (byte) (startPC >> 8); this.contents[this.contentsOffset++] = (byte) startPC; int length = endPC - startPC; this.contents[this.contentsOffset++] = (byte) (length >> 8); this.contents[this.contentsOffset++] = (byte) length; int resolvedPosition = localVariable.resolvedPosition; this.contents[this.contentsOffset++] = (byte) (resolvedPosition >> 8); this.contents[this.contentsOffset++] = (byte) resolvedPosition; } } this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); this.contents[localVariableTableOffset] = (byte) numberOfEntries; break; case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER_BOUND: this.contents[this.contentsOffset++] = (byte) annotationContext.info; this.contents[this.contentsOffset++] = (byte) annotationContext.info2; break; } } /** * INTERNAL USE-ONLY * This methods returns a char[] representing the file name of the receiver * * @return char[] */ public char[] fileName() { return this.constantPool.UTF8Cache.returnKeyFor(2); } private void generateAnnotation(Annotation annotation, int currentOffset) { int startingContentsOffset = currentOffset; if (this.contentsOffset + 4 >= this.contents.length) { resizeContents(4); } TypeBinding annotationTypeBinding = annotation.resolvedType; if (annotationTypeBinding == null) { this.contentsOffset = startingContentsOffset; return; } if (annotationTypeBinding.isMemberType()) { this.recordInnerClasses(annotationTypeBinding); } final int typeIndex = this.constantPool.literalIndex(annotationTypeBinding.signature()); this.contents[this.contentsOffset++] = (byte) (typeIndex >> 8); this.contents[this.contentsOffset++] = (byte) typeIndex; if (annotation instanceof NormalAnnotation) { NormalAnnotation normalAnnotation = (NormalAnnotation) annotation; MemberValuePair[] memberValuePairs = normalAnnotation.memberValuePairs; int memberValuePairOffset = this.contentsOffset; if (memberValuePairs != null) { int memberValuePairsCount = 0; int memberValuePairsLengthPosition = this.contentsOffset; this.contentsOffset += 2; // leave space to fill in the pair count later int resetPosition = this.contentsOffset; final int memberValuePairsLength = memberValuePairs.length; loop: for (int i = 0; i < memberValuePairsLength; i++) { MemberValuePair memberValuePair = memberValuePairs[i]; if (this.contentsOffset + 2 >= this.contents.length) { resizeContents(2); } final int elementNameIndex = this.constantPool.literalIndex(memberValuePair.name); this.contents[this.contentsOffset++] = (byte) (elementNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) elementNameIndex; MethodBinding methodBinding = memberValuePair.binding; if (methodBinding == null) { this.contentsOffset = resetPosition; } else { try { generateElementValue(memberValuePair.value, methodBinding.returnType, memberValuePairOffset); if (this.contentsOffset == memberValuePairOffset) { // ignore all annotation values this.contents[this.contentsOffset++] = 0; this.contents[this.contentsOffset++] = 0; break loop; } memberValuePairsCount++; resetPosition = this.contentsOffset; } catch (ClassCastException | ShouldNotImplement e) { this.contentsOffset = resetPosition; } } } this.contents[memberValuePairsLengthPosition++] = (byte) (memberValuePairsCount >> 8); this.contents[memberValuePairsLengthPosition++] = (byte) memberValuePairsCount; } else { this.contents[this.contentsOffset++] = 0; this.contents[this.contentsOffset++] = 0; } } else if (annotation instanceof SingleMemberAnnotation) { SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) annotation; // this is a single member annotation (one member value) this.contents[this.contentsOffset++] = 0; this.contents[this.contentsOffset++] = 1; if (this.contentsOffset + 2 >= this.contents.length) { resizeContents(2); } final int elementNameIndex = this.constantPool.literalIndex(VALUE); this.contents[this.contentsOffset++] = (byte) (elementNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) elementNameIndex; MethodBinding methodBinding = singleMemberAnnotation.memberValuePairs()[0].binding; if (methodBinding == null) { this.contentsOffset = startingContentsOffset; } else { int memberValuePairOffset = this.contentsOffset; try { generateElementValue(singleMemberAnnotation.memberValue, methodBinding.returnType, memberValuePairOffset); if (this.contentsOffset == memberValuePairOffset) { // completely remove the annotation as its value is invalid this.contentsOffset = startingContentsOffset; } } catch (ClassCastException | ShouldNotImplement e) { this.contentsOffset = startingContentsOffset; } } } else { // this is a marker annotation (no member value pairs) this.contents[this.contentsOffset++] = 0; this.contents[this.contentsOffset++] = 0; } } private int generateAnnotationDefaultAttribute(AnnotationMethodDeclaration declaration, int attributeOffset) { int attributesNumber = 0; // add an annotation default attribute int annotationDefaultNameIndex = this.constantPool .literalIndex(AttributeNamesConstants.AnnotationDefaultName); if (this.contentsOffset + 6 >= this.contents.length) { resizeContents(6); } this.contents[this.contentsOffset++] = (byte) (annotationDefaultNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) annotationDefaultNameIndex; int attributeLengthOffset = this.contentsOffset; this.contentsOffset += 4; generateElementValue(declaration.defaultValue, declaration.binding.returnType, attributeOffset); if (this.contentsOffset != attributeOffset) { int attributeLength = this.contentsOffset - attributeLengthOffset - 4; this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); this.contents[attributeLengthOffset++] = (byte) attributeLength; attributesNumber++; } return attributesNumber; } /** * 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 (this.contentsOffset + 20 >= this.contents.length) { resizeContents(20); } int constantValueNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.CodeName); this.contents[this.contentsOffset++] = (byte) (constantValueNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) constantValueNameIndex; // leave space for attribute_length(4), max_stack(2), max_locals(2), code_length(4) this.contentsOffset += 12; } private int generateConstantValueAttribute(Constant fieldConstant, FieldBinding fieldBinding, int fieldAttributeOffset) { int localContentsOffset = this.contentsOffset; int attributesNumber = 1; if (localContentsOffset + 8 >= this.contents.length) { resizeContents(8); } // Now we generate the constant attribute corresponding to the fieldBinding int constantValueNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.ConstantValueName); this.contents[localContentsOffset++] = (byte) (constantValueNameIndex >> 8); this.contents[localContentsOffset++] = (byte) constantValueNameIndex; // The attribute length = 2 in case of a constantValue attribute this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 2; // Need to add the constant_value_index switch (fieldConstant.typeID()) { case T_boolean: int booleanValueIndex = this.constantPool.literalIndex(fieldConstant.booleanValue() ? 1 : 0); this.contents[localContentsOffset++] = (byte) (booleanValueIndex >> 8); this.contents[localContentsOffset++] = (byte) booleanValueIndex; break; case T_byte: case T_char: case T_int: case T_short: int integerValueIndex = this.constantPool.literalIndex(fieldConstant.intValue()); this.contents[localContentsOffset++] = (byte) (integerValueIndex >> 8); this.contents[localContentsOffset++] = (byte) integerValueIndex; break; case T_float: int floatValueIndex = this.constantPool.literalIndex(fieldConstant.floatValue()); this.contents[localContentsOffset++] = (byte) (floatValueIndex >> 8); this.contents[localContentsOffset++] = (byte) floatValueIndex; break; case T_double: int doubleValueIndex = this.constantPool.literalIndex(fieldConstant.doubleValue()); this.contents[localContentsOffset++] = (byte) (doubleValueIndex >> 8); this.contents[localContentsOffset++] = (byte) doubleValueIndex; break; case T_long: int longValueIndex = this.constantPool.literalIndex(fieldConstant.longValue()); this.contents[localContentsOffset++] = (byte) (longValueIndex >> 8); this.contents[localContentsOffset++] = (byte) longValueIndex; break; case T_JavaLangString: int stringValueIndex = this.constantPool.literalIndex(((StringConstant) fieldConstant).stringValue()); if (stringValueIndex == -1) { if (!this.creatingProblemType) { // report an error and abort: will lead to a problem type classfile creation TypeDeclaration typeDeclaration = this.referenceBinding.scope.referenceContext; FieldDeclaration[] fieldDecls = typeDeclaration.fields; int max = fieldDecls == null ? 0 : fieldDecls.length; for (int i = 0; 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 this.contentsOffset = fieldAttributeOffset; attributesNumber = 0; } } else { this.contents[localContentsOffset++] = (byte) (stringValueIndex >> 8); this.contents[localContentsOffset++] = (byte) stringValueIndex; } } this.contentsOffset = localContentsOffset; return attributesNumber; } private int generateDeprecatedAttribute() { int localContentsOffset = this.contentsOffset; if (localContentsOffset + 6 >= this.contents.length) { resizeContents(6); } int deprecatedAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.DeprecatedName); this.contents[localContentsOffset++] = (byte) (deprecatedAttributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) deprecatedAttributeNameIndex; // the length of a deprecated attribute is equals to 0 this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contentsOffset = localContentsOffset; return 1; } private int generateNestHostAttribute() { SourceTypeBinding nestHost = this.referenceBinding.getNestHost(); if (nestHost == null) return 0; int localContentsOffset = this.contentsOffset; if (localContentsOffset + 10 >= this.contents.length) { resizeContents(10); } int nestHostAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.NestHost); this.contents[localContentsOffset++] = (byte) (nestHostAttributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) nestHostAttributeNameIndex; // The value of the attribute_length item must be two. this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 2; int nestHostIndex = this.constantPool.literalIndexForType(nestHost.constantPoolName()); this.contents[localContentsOffset++] = (byte) (nestHostIndex >> 8); this.contents[localContentsOffset++] = (byte) nestHostIndex; this.contentsOffset = localContentsOffset; return 1; } private int generateNestMembersAttribute() { int localContentsOffset = this.contentsOffset; List<String> nestedMembers = this.referenceBinding.getNestMembers(); int numberOfNestedMembers = nestedMembers != null ? nestedMembers.size() : 0; if (numberOfNestedMembers == 0) // JVMS 11 4.7.29 says "at most one" NestMembers attribute - return if none. return 0; int exSize = 8 + 2 * numberOfNestedMembers; if (exSize + localContentsOffset >= this.contents.length) { resizeContents(exSize); } int attributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.NestMembers); this.contents[localContentsOffset++] = (byte) (attributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) attributeNameIndex; int value = (numberOfNestedMembers << 1) + 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) (numberOfNestedMembers >> 8); this.contents[localContentsOffset++] = (byte) numberOfNestedMembers; for (int i = 0; i < numberOfNestedMembers; i++) { char[] nestMemberName = nestedMembers.get(i).toCharArray(); int nestedMemberIndex = this.constantPool.literalIndexForType(nestMemberName); this.contents[localContentsOffset++] = (byte) (nestedMemberIndex >> 8); this.contents[localContentsOffset++] = (byte) nestedMemberIndex; } this.contentsOffset = localContentsOffset; return 1; } private int generateNestAttributes() { int nAttrs = generateNestMembersAttribute(); //either member or host will exist 4.7.29 nAttrs += generateNestHostAttribute(); return nAttrs; } private int generateModuleAttribute(ModuleDeclaration module) { ModuleBinding binding = module.binding; int localContentsOffset = this.contentsOffset; if (localContentsOffset + 10 >= this.contents.length) { resizeContents(10); } int moduleAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.ModuleName); this.contents[localContentsOffset++] = (byte) (moduleAttributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) moduleAttributeNameIndex; int attrLengthOffset = localContentsOffset; localContentsOffset += 4; int moduleNameIndex = this.constantPool.literalIndexForModule(binding.moduleName); this.contents[localContentsOffset++] = (byte) (moduleNameIndex >> 8); this.contents[localContentsOffset++] = (byte) moduleNameIndex; int flags = module.modifiers & ~(ClassFileConstants.AccModule); this.contents[localContentsOffset++] = (byte) (flags >> 8); this.contents[localContentsOffset++] = (byte) flags; String moduleVersion = module.getModuleVersion(); int module_version_idx = moduleVersion == null ? 0 : this.constantPool.literalIndex(moduleVersion.toCharArray()); this.contents[localContentsOffset++] = (byte) (module_version_idx >> 8); this.contents[localContentsOffset++] = (byte) module_version_idx; int attrLength = 6; // ================= requires section ================= /** u2 requires_count; { u2 requires_index; u2 requires_flags; } requires[requires_count]; **/ int requiresCountOffset = localContentsOffset; int requiresCount = module.requiresCount; int requiresSize = 2 + requiresCount * 6; if (localContentsOffset + requiresSize >= this.contents.length) { resizeContents(requiresSize); } localContentsOffset += 2; ModuleBinding javaBaseBinding = null; for (int i = 0; i < module.requiresCount; i++) { RequiresStatement req = module.requires[i]; ModuleBinding reqBinding = req.resolvedBinding; if (CharOperation.equals(reqBinding.moduleName, TypeConstants.JAVA_BASE)) { javaBaseBinding = reqBinding; } int nameIndex = this.constantPool.literalIndexForModule(reqBinding.moduleName); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) (nameIndex); flags = req.modifiers; this.contents[localContentsOffset++] = (byte) (flags >> 8); this.contents[localContentsOffset++] = (byte) (flags); int required_version = 0; this.contents[localContentsOffset++] = (byte) (required_version >> 8); this.contents[localContentsOffset++] = (byte) (required_version); } if (!CharOperation.equals(binding.moduleName, TypeConstants.JAVA_BASE) && javaBaseBinding == null) { if (localContentsOffset + 6 >= this.contents.length) { resizeContents(6); } javaBaseBinding = binding.environment.javaBaseModule(); int javabase_index = this.constantPool.literalIndexForModule(javaBaseBinding.moduleName); this.contents[localContentsOffset++] = (byte) (javabase_index >> 8); this.contents[localContentsOffset++] = (byte) (javabase_index); flags = ClassFileConstants.AccMandated; this.contents[localContentsOffset++] = (byte) (flags >> 8); this.contents[localContentsOffset++] = (byte) flags; int required_version = 0; this.contents[localContentsOffset++] = (byte) (required_version >> 8); this.contents[localContentsOffset++] = (byte) (required_version); requiresCount++; } this.contents[requiresCountOffset++] = (byte) (requiresCount >> 8); this.contents[requiresCountOffset++] = (byte) requiresCount; attrLength += 2 + 6 * requiresCount; // ================= end requires section ================= // ================= exports section ================= /** * u2 exports_count; * { u2 exports_index; * u2 exports_flags; * u2 exports_to_count; * u2 exports_to_index[exports_to_count]; * } exports[exports_count]; */ int exportsSize = 2 + module.exportsCount * 6; if (localContentsOffset + exportsSize >= this.contents.length) { resizeContents(exportsSize); } this.contents[localContentsOffset++] = (byte) (module.exportsCount >> 8); this.contents[localContentsOffset++] = (byte) module.exportsCount; for (int i = 0; i < module.exportsCount; i++) { ExportsStatement ref = module.exports[i]; if (localContentsOffset + 6 >= this.contents.length) { resizeContents((module.exportsCount - i) * 6); } int nameIndex = this.constantPool .literalIndexForPackage(CharOperation.replaceOnCopy(ref.pkgName, '.', '/')); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) (nameIndex); // TODO exports_flags - check when they are set this.contents[localContentsOffset++] = (byte) 0; this.contents[localContentsOffset++] = (byte) 0; int exportsToCount = ref.isQualified() ? ref.targets.length : 0; this.contents[localContentsOffset++] = (byte) (exportsToCount >> 8); this.contents[localContentsOffset++] = (byte) (exportsToCount); if (exportsToCount > 0) { int targetSize = 2 * exportsToCount; if (localContentsOffset + targetSize >= this.contents.length) { resizeContents(targetSize); } for (int j = 0; j < exportsToCount; j++) { nameIndex = this.constantPool.literalIndexForModule(ref.targets[j].moduleName); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) (nameIndex); } attrLength += targetSize; } } attrLength += exportsSize; // ================= end exports section ================= // ================= opens section ================= /** * u2 opens_count; * { u2 opens_index; * u2 opens_flags; * u2 opens_to_count; * u2 opens_to_index[opens_to_count]; * } exports[exports_count]; */ int opensSize = 2 + module.opensCount * 6; if (localContentsOffset + opensSize >= this.contents.length) { resizeContents(opensSize); } this.contents[localContentsOffset++] = (byte) (module.opensCount >> 8); this.contents[localContentsOffset++] = (byte) module.opensCount; for (int i = 0; i < module.opensCount; i++) { OpensStatement ref = module.opens[i]; if (localContentsOffset + 6 >= this.contents.length) { resizeContents((module.opensCount - i) * 6); } int nameIndex = this.constantPool .literalIndexForPackage(CharOperation.replaceOnCopy(ref.pkgName, '.', '/')); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) (nameIndex); // TODO opens_flags - check when they are set this.contents[localContentsOffset++] = (byte) 0; this.contents[localContentsOffset++] = (byte) 0; int opensToCount = ref.isQualified() ? ref.targets.length : 0; this.contents[localContentsOffset++] = (byte) (opensToCount >> 8); this.contents[localContentsOffset++] = (byte) (opensToCount); if (opensToCount > 0) { int targetSize = 2 * opensToCount; if (localContentsOffset + targetSize >= this.contents.length) { resizeContents(targetSize); } for (int j = 0; j < opensToCount; j++) { nameIndex = this.constantPool.literalIndexForModule(ref.targets[j].moduleName); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) (nameIndex); } attrLength += targetSize; } } attrLength += opensSize; // ================= end opens section ================= // ================= uses section ================= /** * u2 uses_count; * u2 uses_index[uses_count]; */ int usesSize = 2 + 2 * module.usesCount; if (localContentsOffset + usesSize >= this.contents.length) { resizeContents(usesSize); } this.contents[localContentsOffset++] = (byte) (module.usesCount >> 8); this.contents[localContentsOffset++] = (byte) module.usesCount; for (int i = 0; i < module.usesCount; i++) { int nameIndex = this.constantPool .literalIndexForType(module.uses[i].serviceInterface.resolvedType.constantPoolName()); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) (nameIndex); } attrLength += usesSize; // ================= end uses section ================= // ================= provides section ================= /** * u2 provides_count; * { * u2 provides_index; * u2 provides_with_count; * u2 provides_with_index[provides_with_count]; * } provides[provides_count]; */ int servicesSize = 2 + 4 * module.servicesCount; if (localContentsOffset + servicesSize >= this.contents.length) { resizeContents(servicesSize); } this.contents[localContentsOffset++] = (byte) (module.servicesCount >> 8); this.contents[localContentsOffset++] = (byte) module.servicesCount; for (int i = 0; i < module.servicesCount; i++) { if (localContentsOffset + 4 >= this.contents.length) { resizeContents((module.servicesCount - i) * 4); } int nameIndex = this.constantPool .literalIndexForType(module.services[i].serviceInterface.resolvedType.constantPoolName()); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) (nameIndex); TypeReference[] impls = module.services[i].implementations; int implLength = impls.length; this.contents[localContentsOffset++] = (byte) (implLength >> 8); this.contents[localContentsOffset++] = (byte) implLength; int targetSize = implLength * 2; if (localContentsOffset + targetSize >= this.contents.length) { resizeContents(targetSize); } for (int j = 0; j < implLength; j++) { nameIndex = this.constantPool.literalIndexForType(impls[j].resolvedType.constantPoolName()); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) (nameIndex); } attrLength += targetSize; } attrLength += servicesSize; // ================= end provides section ================= this.contents[attrLengthOffset++] = (byte) (attrLength >> 24); this.contents[attrLengthOffset++] = (byte) (attrLength >> 16); this.contents[attrLengthOffset++] = (byte) (attrLength >> 8); this.contents[attrLengthOffset++] = (byte) attrLength; this.contentsOffset = localContentsOffset; return 1; } private int generateModuleMainClassAttribute(char[] moduleMainClass) { int localContentsOffset = this.contentsOffset; if (localContentsOffset + 8 >= this.contents.length) { resizeContents(8); } int moduleAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.ModuleMainClass); this.contents[localContentsOffset++] = (byte) (moduleAttributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) moduleAttributeNameIndex; int attrLength = 2; this.contents[localContentsOffset++] = (byte) (attrLength >> 24); this.contents[localContentsOffset++] = (byte) (attrLength >> 16); this.contents[localContentsOffset++] = (byte) (attrLength >> 8); this.contents[localContentsOffset++] = (byte) attrLength; int moduleNameIndex = this.constantPool.literalIndexForType(moduleMainClass); this.contents[localContentsOffset++] = (byte) (moduleNameIndex >> 8); this.contents[localContentsOffset++] = (byte) moduleNameIndex; this.contentsOffset = localContentsOffset; return 1; } private int generateModulePackagesAttribute(char[][] packageNames) { int localContentsOffset = this.contentsOffset; int maxSize = 6 + 2 * packageNames.length; if (localContentsOffset + maxSize >= this.contents.length) { resizeContents(maxSize); } int moduleAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.ModulePackages); this.contents[localContentsOffset++] = (byte) (moduleAttributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) moduleAttributeNameIndex; int attrLengthOffset = localContentsOffset; localContentsOffset += 4; int packageCountOffset = localContentsOffset; localContentsOffset += 2; int packagesCount = 0; for (char[] packageName : packageNames) { if (packageName == null || packageName.length == 0) continue; int packageNameIndex = this.constantPool.literalIndexForPackage(packageName); this.contents[localContentsOffset++] = (byte) (packageNameIndex >> 8); this.contents[localContentsOffset++] = (byte) packageNameIndex; packagesCount++; } this.contents[packageCountOffset++] = (byte) (packagesCount >> 8); this.contents[packageCountOffset++] = (byte) packagesCount; int attrLength = 2 + 2 * packagesCount; this.contents[attrLengthOffset++] = (byte) (attrLength >> 24); this.contents[attrLengthOffset++] = (byte) (attrLength >> 16); this.contents[attrLengthOffset++] = (byte) (attrLength >> 8); this.contents[attrLengthOffset++] = (byte) attrLength; this.contentsOffset = localContentsOffset; return 1; } private void generateElementValue(Expression defaultValue, TypeBinding memberValuePairReturnType, int attributeOffset) { Constant constant = defaultValue.constant; TypeBinding defaultValueBinding = defaultValue.resolvedType; if (defaultValueBinding == null) { this.contentsOffset = attributeOffset; } else { if (defaultValueBinding.isMemberType()) { this.recordInnerClasses(defaultValueBinding); } if (memberValuePairReturnType.isMemberType()) { this.recordInnerClasses(memberValuePairReturnType); } if (memberValuePairReturnType.isArrayType() && !defaultValueBinding.isArrayType()) { // automatic wrapping if (this.contentsOffset + 3 >= this.contents.length) { resizeContents(3); } this.contents[this.contentsOffset++] = (byte) '['; this.contents[this.contentsOffset++] = (byte) 0; this.contents[this.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 (this.contentsOffset + 3 >= this.contents.length) { resizeContents(3); } switch (binding.id) { case T_boolean: this.contents[this.contentsOffset++] = (byte) 'Z'; int booleanValueIndex = this.constantPool.literalIndex(constant.booleanValue() ? 1 : 0); this.contents[this.contentsOffset++] = (byte) (booleanValueIndex >> 8); this.contents[this.contentsOffset++] = (byte) booleanValueIndex; break; case T_byte: this.contents[this.contentsOffset++] = (byte) 'B'; int integerValueIndex = this.constantPool.literalIndex(constant.intValue()); this.contents[this.contentsOffset++] = (byte) (integerValueIndex >> 8); this.contents[this.contentsOffset++] = (byte) integerValueIndex; break; case T_char: this.contents[this.contentsOffset++] = (byte) 'C'; integerValueIndex = this.constantPool.literalIndex(constant.intValue()); this.contents[this.contentsOffset++] = (byte) (integerValueIndex >> 8); this.contents[this.contentsOffset++] = (byte) integerValueIndex; break; case T_int: this.contents[this.contentsOffset++] = (byte) 'I'; integerValueIndex = this.constantPool.literalIndex(constant.intValue()); this.contents[this.contentsOffset++] = (byte) (integerValueIndex >> 8); this.contents[this.contentsOffset++] = (byte) integerValueIndex; break; case T_short: this.contents[this.contentsOffset++] = (byte) 'S'; integerValueIndex = this.constantPool.literalIndex(constant.intValue()); this.contents[this.contentsOffset++] = (byte) (integerValueIndex >> 8); this.contents[this.contentsOffset++] = (byte) integerValueIndex; break; case T_float: this.contents[this.contentsOffset++] = (byte) 'F'; int floatValueIndex = this.constantPool.literalIndex(constant.floatValue()); this.contents[this.contentsOffset++] = (byte) (floatValueIndex >> 8); this.contents[this.contentsOffset++] = (byte) floatValueIndex; break; case T_double: this.contents[this.contentsOffset++] = (byte) 'D'; int doubleValueIndex = this.constantPool.literalIndex(constant.doubleValue()); this.contents[this.contentsOffset++] = (byte) (doubleValueIndex >> 8); this.contents[this.contentsOffset++] = (byte) doubleValueIndex; break; case T_long: this.contents[this.contentsOffset++] = (byte) 'J'; int longValueIndex = this.constantPool.literalIndex(constant.longValue()); this.contents[this.contentsOffset++] = (byte) (longValueIndex >> 8); this.contents[this.contentsOffset++] = (byte) longValueIndex; break; case T_JavaLangString: this.contents[this.contentsOffset++] = (byte) 's'; int stringValueIndex = this.constantPool .literalIndex(((StringConstant) constant).stringValue().toCharArray()); if (stringValueIndex == -1) { if (!this.creatingProblemType) { // report an error and abort: will lead to a problem type classfile creation TypeDeclaration typeDeclaration = this.referenceBinding.scope.referenceContext; typeDeclaration.scope.problemReporter().stringConstantIsExceedingUtf8Limit(defaultValue); } else { // already inside a problem type creation : no attribute this.contentsOffset = attributeOffset; } } else { this.contents[this.contentsOffset++] = (byte) (stringValueIndex >> 8); this.contents[this.contentsOffset++] = (byte) stringValueIndex; } } } private void generateElementValueForNonConstantExpression(Expression defaultValue, int attributeOffset, TypeBinding defaultValueBinding) { if (defaultValueBinding != null) { if (defaultValueBinding.isEnum()) { if (this.contentsOffset + 5 >= this.contents.length) { resizeContents(5); } this.contents[this.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 { this.contentsOffset = attributeOffset; } if (fieldBinding != null) { final int enumConstantTypeNameIndex = this.constantPool .literalIndex(fieldBinding.type.signature()); final int enumConstantNameIndex = this.constantPool.literalIndex(fieldBinding.name); this.contents[this.contentsOffset++] = (byte) (enumConstantTypeNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) enumConstantTypeNameIndex; this.contents[this.contentsOffset++] = (byte) (enumConstantNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) enumConstantNameIndex; } } else if (defaultValueBinding.isAnnotationType()) { if (this.contentsOffset + 1 >= this.contents.length) { resizeContents(1); } this.contents[this.contentsOffset++] = (byte) '@'; generateAnnotation((Annotation) defaultValue, attributeOffset); } else if (defaultValueBinding.isArrayType()) { // array type if (this.contentsOffset + 3 >= this.contents.length) { resizeContents(3); } this.contents[this.contentsOffset++] = (byte) '['; if (defaultValue instanceof ArrayInitializer) { ArrayInitializer arrayInitializer = (ArrayInitializer) defaultValue; int arrayLength = arrayInitializer.expressions != null ? arrayInitializer.expressions.length : 0; this.contents[this.contentsOffset++] = (byte) (arrayLength >> 8); this.contents[this.contentsOffset++] = (byte) arrayLength; for (int i = 0; i < arrayLength; i++) { generateElementValue(arrayInitializer.expressions[i], defaultValueBinding.leafComponentType(), attributeOffset); } } else { this.contentsOffset = attributeOffset; } } else { // class type if (this.contentsOffset + 3 >= this.contents.length) { resizeContents(3); } this.contents[this.contentsOffset++] = (byte) 'c'; if (defaultValue instanceof ClassLiteralAccess) { ClassLiteralAccess classLiteralAccess = (ClassLiteralAccess) defaultValue; final int classInfoIndex = this.constantPool .literalIndex(classLiteralAccess.targetType.signature()); this.contents[this.contentsOffset++] = (byte) (classInfoIndex >> 8); this.contents[this.contentsOffset++] = (byte) classInfoIndex; } else { this.contentsOffset = attributeOffset; } } } else { this.contentsOffset = attributeOffset; } } private int generateEnclosingMethodAttribute() { int localContentsOffset = this.contentsOffset; // add enclosing method attribute (1.5 mode only) if (localContentsOffset + 10 >= this.contents.length) { resizeContents(10); } int enclosingMethodAttributeNameIndex = this.constantPool .literalIndex(AttributeNamesConstants.EnclosingMethodName); this.contents[localContentsOffset++] = (byte) (enclosingMethodAttributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) enclosingMethodAttributeNameIndex; // the length of a signature attribute is equals to 2 this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 4; int enclosingTypeIndex = this.constantPool .literalIndexForType(this.referenceBinding.enclosingType().constantPoolName()); this.contents[localContentsOffset++] = (byte) (enclosingTypeIndex >> 8); this.contents[localContentsOffset++] = (byte) enclosingTypeIndex; byte methodIndexByte1 = 0; byte methodIndexByte2 = 0; if (this.referenceBinding instanceof LocalTypeBinding) { MethodBinding methodBinding = ((LocalTypeBinding) this.referenceBinding).enclosingMethod; if (methodBinding != null) { int enclosingMethodIndex = this.constantPool.literalIndexForNameAndType(methodBinding.selector, methodBinding.signature(this)); methodIndexByte1 = (byte) (enclosingMethodIndex >> 8); methodIndexByte2 = (byte) enclosingMethodIndex; } } this.contents[localContentsOffset++] = methodIndexByte1; this.contents[localContentsOffset++] = methodIndexByte2; this.contentsOffset = localContentsOffset; return 1; } private int generateExceptionsAttribute(ReferenceBinding[] thrownsExceptions) { int localContentsOffset = this.contentsOffset; int length = thrownsExceptions.length; int exSize = 8 + length * 2; if (exSize + this.contentsOffset >= this.contents.length) { resizeContents(exSize); } int exceptionNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.ExceptionsName); this.contents[localContentsOffset++] = (byte) (exceptionNameIndex >> 8); this.contents[localContentsOffset++] = (byte) exceptionNameIndex; // The attribute length = length * 2 + 2 in case of a exception attribute int attributeLength = length * 2 + 2; this.contents[localContentsOffset++] = (byte) (attributeLength >> 24); this.contents[localContentsOffset++] = (byte) (attributeLength >> 16); this.contents[localContentsOffset++] = (byte) (attributeLength >> 8); this.contents[localContentsOffset++] = (byte) attributeLength; this.contents[localContentsOffset++] = (byte) (length >> 8); this.contents[localContentsOffset++] = (byte) length; for (int i = 0; i < length; i++) { int exceptionIndex = this.constantPool.literalIndexForType(thrownsExceptions[i]); this.contents[localContentsOffset++] = (byte) (exceptionIndex >> 8); this.contents[localContentsOffset++] = (byte) exceptionIndex; } this.contentsOffset = localContentsOffset; return 1; } private int generateHierarchyInconsistentAttribute() { int localContentsOffset = this.contentsOffset; // add an attribute for inconsistent hierarchy if (localContentsOffset + 6 >= this.contents.length) { resizeContents(6); } int inconsistentHierarchyNameIndex = this.constantPool .literalIndex(AttributeNamesConstants.InconsistentHierarchy); this.contents[localContentsOffset++] = (byte) (inconsistentHierarchyNameIndex >> 8); this.contents[localContentsOffset++] = (byte) inconsistentHierarchyNameIndex; // the length of an inconsistent hierarchy attribute is equals to 0 this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contentsOffset = localContentsOffset; return 1; } private int generateInnerClassAttribute(int numberOfInnerClasses, ReferenceBinding[] innerClasses) { int localContentsOffset = this.contentsOffset; // Generate the inner class attribute int exSize = 8 * numberOfInnerClasses + 8; if (exSize + localContentsOffset >= this.contents.length) { resizeContents(exSize); } // Now we now the size of the attribute and the number of entries // attribute name int attributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.InnerClassName); this.contents[localContentsOffset++] = (byte) (attributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) attributeNameIndex; int value = (numberOfInnerClasses << 3) + 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) (numberOfInnerClasses >> 8); this.contents[localContentsOffset++] = (byte) numberOfInnerClasses; for (int i = 0; i < numberOfInnerClasses; i++) { ReferenceBinding innerClass = innerClasses[i]; int accessFlags = innerClass.getAccessFlags(); int innerClassIndex = this.constantPool.literalIndexForType(innerClass.constantPoolName()); // inner class index this.contents[localContentsOffset++] = (byte) (innerClassIndex >> 8); this.contents[localContentsOffset++] = (byte) innerClassIndex; // outer class index: anonymous and local have no outer class index if (innerClass.isMemberType()) { // member or member of local int outerClassIndex = this.constantPool .literalIndexForType(innerClass.enclosingType().constantPoolName()); this.contents[localContentsOffset++] = (byte) (outerClassIndex >> 8); this.contents[localContentsOffset++] = (byte) outerClassIndex; } else { // equals to 0 if the innerClass is not a member type this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; } // name index if (!innerClass.isAnonymousType()) { int nameIndex = this.constantPool.literalIndex(innerClass.sourceName()); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; } else { // equals to 0 if the innerClass is an anonymous type this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; } // access flag if (innerClass.isAnonymousType()) { accessFlags &= ~ClassFileConstants.AccFinal; } else if (innerClass.isMemberType() && innerClass.isInterface()) { accessFlags |= ClassFileConstants.AccStatic; // implicitely static } this.contents[localContentsOffset++] = (byte) (accessFlags >> 8); this.contents[localContentsOffset++] = (byte) accessFlags; } this.contentsOffset = localContentsOffset; return 1; } private int generateBootstrapMethods(List functionalExpressionList) { /* See JVM spec 4.7.21 The BootstrapMethods attribute has the following format: BootstrapMethods_attribute { u2 attribute_name_index; u4 attribute_length; u2 num_bootstrap_methods; { u2 bootstrap_method_ref; u2 num_bootstrap_arguments; u2 bootstrap_arguments[num_bootstrap_arguments]; } bootstrap_methods[num_bootstrap_methods]; } */ // Record inner classes for MethodHandles$Lookup ReferenceBinding methodHandlesLookup = this.referenceBinding.scope.getJavaLangInvokeMethodHandlesLookup(); if (methodHandlesLookup == null) return 0; // skip bootstrap section, class path problem already reported, just avoid NPE. recordInnerClasses(methodHandlesLookup); // Should be done, it's what javac does also ReferenceBinding javaLangInvokeLambdaMetafactory = this.referenceBinding.scope .getJavaLangInvokeLambdaMetafactory(); // Depending on the complexity of the expression it may be necessary to use the altMetafactory() rather than the metafactory() int indexForMetaFactory = 0; int indexForAltMetaFactory = 0; int numberOfBootstraps = functionalExpressionList.size(); int localContentsOffset = this.contentsOffset; // Generate the boot strap attribute - since we are only making lambdas and // functional expressions, we know the size ahead of time - this less general // than the full invokedynamic scope, but fine for Java 8 final int contentsEntries = 10; int exSize = contentsEntries * numberOfBootstraps + 8; if (exSize + localContentsOffset >= this.contents.length) { resizeContents(exSize); } int attributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.BootstrapMethodsName); this.contents[localContentsOffset++] = (byte) (attributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) attributeNameIndex; // leave space for attribute_length and remember where to insert it int attributeLengthPosition = localContentsOffset; localContentsOffset += 4; this.contents[localContentsOffset++] = (byte) (numberOfBootstraps >> 8); this.contents[localContentsOffset++] = (byte) numberOfBootstraps; for (int i = 0; i < numberOfBootstraps; i++) { FunctionalExpression functional = (FunctionalExpression) functionalExpressionList.get(i); MethodBinding[] bridges = functional.getRequiredBridges(); TypeBinding[] markerInterfaces = null; if ((functional instanceof LambdaExpression && (((markerInterfaces = ((LambdaExpression) functional).getMarkerInterfaces()) != null)) || bridges != null) || functional.isSerializable) { // may need even more space int extraSpace = 2; // at least 2 more than when the normal metafactory is used, for the bitflags entry if (markerInterfaces != null) { // 2 for the marker interface list size then 2 per marker interface index extraSpace += (2 + 2 * markerInterfaces.length); } if (bridges != null) { // 2 for bridge count then 2 per bridge method type. extraSpace += (2 + 2 * bridges.length); } if (extraSpace + contentsEntries + localContentsOffset >= this.contents.length) { resizeContents(extraSpace + contentsEntries); } if (indexForAltMetaFactory == 0) { indexForAltMetaFactory = this.constantPool.literalIndexForMethodHandle( ClassFileConstants.MethodHandleRefKindInvokeStatic, javaLangInvokeLambdaMetafactory, ConstantPool.ALTMETAFACTORY, ConstantPool.JAVA_LANG_INVOKE_LAMBDAMETAFACTORY_ALTMETAFACTORY_SIGNATURE, false); } this.contents[localContentsOffset++] = (byte) (indexForAltMetaFactory >> 8); this.contents[localContentsOffset++] = (byte) indexForAltMetaFactory; // u2 num_bootstrap_arguments this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = (byte) (4 + (markerInterfaces == null ? 0 : 1 + markerInterfaces.length) + (bridges == null ? 0 : 1 + bridges.length)); int functionalDescriptorIndex = this.constantPool .literalIndexForMethodType(functional.descriptor.original().signature()); this.contents[localContentsOffset++] = (byte) (functionalDescriptorIndex >> 8); this.contents[localContentsOffset++] = (byte) functionalDescriptorIndex; int methodHandleIndex = this.constantPool .literalIndexForMethodHandle(functional.binding.original()); // Speak of " implementation" (erased) version here, adaptations described below. this.contents[localContentsOffset++] = (byte) (methodHandleIndex >> 8); this.contents[localContentsOffset++] = (byte) methodHandleIndex; char[] instantiatedSignature = functional.descriptor.signature(); int methodTypeIndex = this.constantPool.literalIndexForMethodType(instantiatedSignature); this.contents[localContentsOffset++] = (byte) (methodTypeIndex >> 8); this.contents[localContentsOffset++] = (byte) methodTypeIndex; int bitflags = 0; if (functional.isSerializable) { bitflags |= ClassFileConstants.FLAG_SERIALIZABLE; } if (markerInterfaces != null) { bitflags |= ClassFileConstants.FLAG_MARKERS; } if (bridges != null) { bitflags |= ClassFileConstants.FLAG_BRIDGES; } int indexForBitflags = this.constantPool.literalIndex(bitflags); this.contents[localContentsOffset++] = (byte) (indexForBitflags >> 8); this.contents[localContentsOffset++] = (byte) (indexForBitflags); if (markerInterfaces != null) { int markerInterfaceCountIndex = this.constantPool.literalIndex(markerInterfaces.length); this.contents[localContentsOffset++] = (byte) (markerInterfaceCountIndex >> 8); this.contents[localContentsOffset++] = (byte) (markerInterfaceCountIndex); for (int m = 0, maxm = markerInterfaces.length; m < maxm; m++) { int classTypeIndex = this.constantPool.literalIndexForType(markerInterfaces[m]); this.contents[localContentsOffset++] = (byte) (classTypeIndex >> 8); this.contents[localContentsOffset++] = (byte) (classTypeIndex); } } if (bridges != null) { int bridgeCountIndex = this.constantPool.literalIndex(bridges.length); this.contents[localContentsOffset++] = (byte) (bridgeCountIndex >> 8); this.contents[localContentsOffset++] = (byte) (bridgeCountIndex); for (int m = 0, maxm = bridges.length; m < maxm; m++) { char[] bridgeSignature = bridges[m].signature(); int bridgeMethodTypeIndex = this.constantPool.literalIndexForMethodType(bridgeSignature); this.contents[localContentsOffset++] = (byte) (bridgeMethodTypeIndex >> 8); this.contents[localContentsOffset++] = (byte) bridgeMethodTypeIndex; } } } else { if (contentsEntries + localContentsOffset >= this.contents.length) { resizeContents(contentsEntries); } if (indexForMetaFactory == 0) { indexForMetaFactory = this.constantPool.literalIndexForMethodHandle( ClassFileConstants.MethodHandleRefKindInvokeStatic, javaLangInvokeLambdaMetafactory, ConstantPool.METAFACTORY, ConstantPool.JAVA_LANG_INVOKE_LAMBDAMETAFACTORY_METAFACTORY_SIGNATURE, false); } this.contents[localContentsOffset++] = (byte) (indexForMetaFactory >> 8); this.contents[localContentsOffset++] = (byte) indexForMetaFactory; // u2 num_bootstrap_arguments this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = (byte) 3; int functionalDescriptorIndex = this.constantPool .literalIndexForMethodType(functional.descriptor.original().signature()); this.contents[localContentsOffset++] = (byte) (functionalDescriptorIndex >> 8); this.contents[localContentsOffset++] = (byte) functionalDescriptorIndex; int methodHandleIndex = this.constantPool.literalIndexForMethodHandle( functional.binding instanceof PolymorphicMethodBinding ? functional.binding : functional.binding.original()); // Speak of " implementation" (erased) version here, adaptations described below. this.contents[localContentsOffset++] = (byte) (methodHandleIndex >> 8); this.contents[localContentsOffset++] = (byte) methodHandleIndex; char[] instantiatedSignature = functional.descriptor.signature(); int methodTypeIndex = this.constantPool.literalIndexForMethodType(instantiatedSignature); this.contents[localContentsOffset++] = (byte) (methodTypeIndex >> 8); this.contents[localContentsOffset++] = (byte) methodTypeIndex; } } int attributeLength = localContentsOffset - attributeLengthPosition - 4; this.contents[attributeLengthPosition++] = (byte) (attributeLength >> 24); this.contents[attributeLengthPosition++] = (byte) (attributeLength >> 16); this.contents[attributeLengthPosition++] = (byte) (attributeLength >> 8); this.contents[attributeLengthPosition++] = (byte) attributeLength; this.contentsOffset = localContentsOffset; return 1; } private int generateLineNumberAttribute() { int localContentsOffset = this.contentsOffset; int attributesNumber = 0; /* 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 = this.codeStream.pcToSourceMap) != null) && (this.codeStream.pcToSourceMapSize != 0)) { int lineNumberNameIndex = this.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 = this.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; attributesNumber = 1; } this.contentsOffset = localContentsOffset; return attributesNumber; } // this is used for problem and synthetic methods private int generateLineNumberAttribute(int problemLine) { int localContentsOffset = this.contentsOffset; 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 = this.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 this.contentsOffset = localContentsOffset; return 1; } private int generateLocalVariableTableAttribute(int code_length, boolean methodDeclarationIsStatic, boolean isSynthetic) { int attributesNumber = 0; int localContentsOffset = this.contentsOffset; int numberOfEntries = 0; int localVariableNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName); int maxOfEntries = 8 + 10 * (methodDeclarationIsStatic ? 0 : 1); for (int i = 0; i < this.codeStream.allLocalsCounter; i++) { LocalVariableBinding localVariableBinding = this.codeStream.locals[i]; maxOfEntries += 10 * localVariableBinding.initializationCount; } // reserve enough space if (localContentsOffset + maxOfEntries >= this.contents.length) { resizeContents(maxOfEntries); } this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); this.contents[localContentsOffset++] = (byte) localVariableNameIndex; int localVariableTableOffset = localContentsOffset; // leave space for attribute_length and local_variable_table_length localContentsOffset += 6; int nameIndex; int descriptorIndex; SourceTypeBinding declaringClassBinding = null; if (!methodDeclarationIsStatic && !isSynthetic) { 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 = this.constantPool.literalIndex(ConstantPool.This); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; declaringClassBinding = (SourceTypeBinding) (this.codeStream.methodDeclaration != null ? this.codeStream.methodDeclaration.binding.declaringClass : this.codeStream.lambdaExpression.binding.declaringClass); descriptorIndex = this.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 = this.codeStream.allLocalsCounter; i < max; i++) { LocalVariableBinding localVariable = this.codeStream.locals[i]; int initializationCount = localVariable.initializationCount; if (initializationCount == 0) continue; if (localVariable.declaration == null) continue; final TypeBinding localVariableTypeBinding = localVariable.type; boolean isParameterizedType = localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable(); if (isParameterizedType) { if (genericLocalVariables == null) { // we cannot have more than max locals genericLocalVariables = new LocalVariableBinding[max]; } genericLocalVariables[genericLocalVariablesCounter++] = localVariable; } for (int j = 0; j < 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 = this.constantPool.literalIndex(localVariable.name); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; descriptorIndex = this.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; 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; attributesNumber++; final boolean currentInstanceIsGeneric = !methodDeclarationIsStatic && declaringClassBinding != null && declaringClassBinding.typeVariables != Binding.NO_TYPE_VARIABLES; 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 = this.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 = this.constantPool.literalIndex(ConstantPool.This); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; descriptorIndex = this.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 = this.constantPool.literalIndex(localVariable.name); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; descriptorIndex = this.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; } } } attributesNumber++; } this.contentsOffset = localContentsOffset; return attributesNumber; } /** * 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 generateMethodInfoAttributes(MethodBinding methodBinding) { // leave two bytes for the attribute_number this.contentsOffset += 2; if (this.contentsOffset + 2 >= this.contents.length) { resizeContents(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 attributesNumber = 0; if ((thrownsExceptions = methodBinding.thrownExceptions) != Binding.NO_EXCEPTIONS) { // 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 attributesNumber += generateExceptionsAttribute(thrownsExceptions); } if (methodBinding.isDeprecated()) { // Deprecated attribute attributesNumber += generateDeprecatedAttribute(); } if (this.targetJDK < ClassFileConstants.JDK1_5) { if (methodBinding.isSynthetic()) { attributesNumber += generateSyntheticAttribute(); } if (methodBinding.isVarargs()) { attributesNumber += generateVarargsAttribute(); } } // add signature attribute char[] genericSignature = methodBinding.genericSignature(); if (genericSignature != null) { attributesNumber += generateSignatureAttribute(genericSignature); } if (this.targetJDK >= ClassFileConstants.JDK1_4) { AbstractMethodDeclaration methodDeclaration = methodBinding.sourceMethod(); if (methodBinding instanceof SyntheticMethodBinding) { SyntheticMethodBinding syntheticMethod = (SyntheticMethodBinding) methodBinding; if (syntheticMethod.purpose == SyntheticMethodBinding.SuperMethodAccess && CharOperation.equals(syntheticMethod.selector, syntheticMethod.targetMethod.selector)) methodDeclaration = ((SyntheticMethodBinding) methodBinding).targetMethod.sourceMethod(); } if (methodDeclaration != null) { Annotation[] annotations = methodDeclaration.annotations; if (annotations != null) { attributesNumber += generateRuntimeAnnotations(annotations, methodBinding.isConstructor() ? TagBits.AnnotationForConstructor : TagBits.AnnotationForMethod); } if ((methodBinding.tagBits & TagBits.HasParameterAnnotations) != 0) { Argument[] arguments = methodDeclaration.arguments; if (arguments != null) { attributesNumber += generateRuntimeAnnotationsForParameters(arguments); } } } else { LambdaExpression lambda = methodBinding.sourceLambda(); if (lambda != null) { if ((methodBinding.tagBits & TagBits.HasParameterAnnotations) != 0) { Argument[] arguments = lambda.arguments(); if (arguments != null) { int parameterCount = methodBinding.parameters.length; int argumentCount = arguments.length; if (parameterCount > argumentCount) { // synthetics prefixed int redShift = parameterCount - argumentCount; System.arraycopy(arguments, 0, arguments = new Argument[parameterCount], redShift, argumentCount); for (int i = 0; i < redShift; i++) arguments[i] = new Argument(CharOperation.NO_CHAR, 0, null, 0); } attributesNumber += generateRuntimeAnnotationsForParameters(arguments); } } } } } if ((methodBinding.tagBits & TagBits.HasMissingType) != 0) { this.missingTypes = methodBinding.collectMissingTypes(this.missingTypes); } return attributesNumber; } public int generateMethodInfoAttributes(MethodBinding methodBinding, AnnotationMethodDeclaration declaration) { int attributesNumber = generateMethodInfoAttributes(methodBinding); int attributeOffset = this.contentsOffset; if ((declaration.modifiers & ClassFileConstants.AccAnnotationDefault) != 0) { // add an annotation default attribute attributesNumber += generateAnnotationDefaultAttribute(declaration, attributeOffset); } 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 this.methodCount++; // add one more method if (this.contentsOffset + 10 >= this.contents.length) { resizeContents(10); } if (this.targetJDK < ClassFileConstants.JDK1_5) { // pre 1.5, synthetic is an attribute, not a modifier // pre 1.5, varargs is an attribute, not a modifier (-target jsr14 mode) accessFlags &= ~(ClassFileConstants.AccSynthetic | ClassFileConstants.AccVarargs); } if ((methodBinding.tagBits & TagBits.ClearPrivateModifier) != 0) { accessFlags &= ~ClassFileConstants.AccPrivate; } this.contents[this.contentsOffset++] = (byte) (accessFlags >> 8); this.contents[this.contentsOffset++] = (byte) accessFlags; int nameIndex = this.constantPool.literalIndex(methodBinding.selector); this.contents[this.contentsOffset++] = (byte) (nameIndex >> 8); this.contents[this.contentsOffset++] = (byte) nameIndex; int descriptorIndex = this.constantPool.literalIndex(methodBinding.signature(this)); this.contents[this.contentsOffset++] = (byte) (descriptorIndex >> 8); this.contents[this.contentsOffset++] = (byte) descriptorIndex; } public void addSyntheticDeserializeLambda(SyntheticMethodBinding methodBinding, SyntheticMethodBinding[] syntheticMethodBindings) { generateMethodInfoHeader(methodBinding); int methodAttributeOffset = this.contentsOffset; // this will add exception attribute, synthetic attribute, deprecated attribute,... int attributeNumber = generateMethodInfoAttributes(methodBinding); // Code attribute int codeAttributeOffset = this.contentsOffset; attributeNumber++; // add code attribute generateCodeAttributeHeader(); this.codeStream.init(this); this.codeStream.generateSyntheticBodyForDeserializeLambda(methodBinding, syntheticMethodBindings); int code_length = this.codeStream.position; if (code_length > 65535) { this.referenceBinding.scope.problemReporter().bytecodeExceeds64KLimit(methodBinding, this.referenceBinding.sourceStart(), this.referenceBinding.sourceEnd()); } completeCodeAttributeForSyntheticMethod(methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass).scope .referenceCompilationUnit().compilationResult.getLineSeparatorPositions()); this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); this.contents[methodAttributeOffset] = (byte) attributeNumber; } /** * 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 this.methodCount++; // add one more method if (this.contentsOffset + 10 >= this.contents.length) { resizeContents(10); } this.contents[this.contentsOffset++] = (byte) ((ClassFileConstants.AccDefault | ClassFileConstants.AccStatic) >> 8); this.contents[this.contentsOffset++] = (byte) (ClassFileConstants.AccDefault | ClassFileConstants.AccStatic); int nameIndex = this.constantPool.literalIndex(ConstantPool.Clinit); this.contents[this.contentsOffset++] = (byte) (nameIndex >> 8); this.contents[this.contentsOffset++] = (byte) nameIndex; int descriptorIndex = this.constantPool.literalIndex(ConstantPool.ClinitSignature); this.contents[this.contentsOffset++] = (byte) (descriptorIndex >> 8); this.contents[this.contentsOffset++] = (byte) descriptorIndex; // We know that we won't get more than 1 attribute: the code attribute this.contents[this.contentsOffset++] = 0; this.contents[this.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) { TypeDeclaration currentDeclaration = this.referenceBinding.scope.referenceContext; int typeDeclarationSourceStart = currentDeclaration.sourceStart(); int typeDeclarationSourceEnd = currentDeclaration.sourceEnd(); for (int i = 0, max = methodDeclarations.length; i < max; i++) { MethodDeclaration methodDeclaration = methodDeclarations[i]; MethodBinding methodBinding = methodDeclaration.binding; String readableName = new String(methodBinding.readableName()); CategorizedProblem[] problems = compilationResult.problems; int problemsCount = compilationResult.problemCount; for (int j = 0; j < problemsCount; j++) { CategorizedProblem problem = problems[j]; if (problem != null && problem.getID() == IProblem.AbstractMethodMustBeImplemented && problem.getMessage().indexOf(readableName) != -1 && problem.getSourceStart() >= typeDeclarationSourceStart && problem.getSourceEnd() <= typeDeclarationSourceEnd) { // we found a match addMissingAbstractProblemMethod(methodDeclaration, methodBinding, problem, compilationResult); } } } } } private void generateMissingTypesAttribute() { int initialSize = this.missingTypes.size(); int[] missingTypesIndexes = new int[initialSize]; int numberOfMissingTypes = 0; if (initialSize > 1) { Collections.sort(this.missingTypes, new Comparator() { @Override public int compare(Object o1, Object o2) { TypeBinding typeBinding1 = (TypeBinding) o1; TypeBinding typeBinding2 = (TypeBinding) o2; return CharOperation.compareTo(typeBinding1.constantPoolName(), typeBinding2.constantPoolName()); } }); } int previousIndex = 0; next: for (int i = 0; i < initialSize; i++) { int missingTypeIndex = this.constantPool.literalIndexForType(this.missingTypes.get(i)); if (previousIndex == missingTypeIndex) { continue next; } previousIndex = missingTypeIndex; missingTypesIndexes[numberOfMissingTypes++] = missingTypeIndex; } // we don't need to resize as we interate from 0 to numberOfMissingTypes when recording the indexes in the .class file int attributeLength = numberOfMissingTypes * 2 + 2; if (this.contentsOffset + attributeLength + 6 >= this.contents.length) { resizeContents(attributeLength + 6); } int missingTypesNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.MissingTypesName); this.contents[this.contentsOffset++] = (byte) (missingTypesNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) missingTypesNameIndex; // generate attribute length this.contents[this.contentsOffset++] = (byte) (attributeLength >> 24); this.contents[this.contentsOffset++] = (byte) (attributeLength >> 16); this.contents[this.contentsOffset++] = (byte) (attributeLength >> 8); this.contents[this.contentsOffset++] = (byte) attributeLength; // generate number of missing types this.contents[this.contentsOffset++] = (byte) (numberOfMissingTypes >> 8); this.contents[this.contentsOffset++] = (byte) numberOfMissingTypes; // generate entry for each missing type for (int i = 0; i < numberOfMissingTypes; i++) { int missingTypeIndex = missingTypesIndexes[i]; this.contents[this.contentsOffset++] = (byte) (missingTypeIndex >> 8); this.contents[this.contentsOffset++] = (byte) missingTypeIndex; } } private boolean jdk16packageInfoAnnotation(final long annotationMask, final long targetMask) { if (this.targetJDK <= ClassFileConstants.JDK1_6 && targetMask == TagBits.AnnotationForPackage && annotationMask != 0 && (annotationMask & TagBits.AnnotationForPackage) == 0) { return true; } return false; } /** * @param annotations * @param targetMask allowed targets * @return the number of attributes created while dumping the annotations in the .class file */ private int generateRuntimeAnnotations(final Annotation[] annotations, final long targetMask) { int attributesNumber = 0; final int length = annotations.length; int visibleAnnotationsCounter = 0; int invisibleAnnotationsCounter = 0; for (int i = 0; i < length; i++) { Annotation annotation; if ((annotation = annotations[i].getPersistibleAnnotation()) == null) continue; // already packaged into container. long annotationMask = annotation.resolvedType != null ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK : 0; if (annotationMask != 0 && (annotationMask & targetMask) == 0) { if (!jdk16packageInfoAnnotation(annotationMask, targetMask)) continue; } if (annotation.isRuntimeInvisible() || annotation.isRuntimeTypeInvisible()) { invisibleAnnotationsCounter++; } else if (annotation.isRuntimeVisible() || annotation.isRuntimeTypeVisible()) { visibleAnnotationsCounter++; } } int annotationAttributeOffset = this.contentsOffset; if (invisibleAnnotationsCounter != 0) { if (this.contentsOffset + 10 >= this.contents.length) { resizeContents(10); } int runtimeInvisibleAnnotationsAttributeNameIndex = this.constantPool .literalIndex(AttributeNamesConstants.RuntimeInvisibleAnnotationsName); this.contents[this.contentsOffset++] = (byte) (runtimeInvisibleAnnotationsAttributeNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) runtimeInvisibleAnnotationsAttributeNameIndex; int attributeLengthOffset = this.contentsOffset; this.contentsOffset += 4; // leave space for the attribute length int annotationsLengthOffset = this.contentsOffset; this.contentsOffset += 2; // leave space for the annotations length int counter = 0; loop: for (int i = 0; i < length; i++) { if (invisibleAnnotationsCounter == 0) break loop; Annotation annotation; if ((annotation = annotations[i].getPersistibleAnnotation()) == null) continue; // already packaged into container. long annotationMask = annotation.resolvedType != null ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK : 0; if (annotationMask != 0 && (annotationMask & targetMask) == 0) { if (!jdk16packageInfoAnnotation(annotationMask, targetMask)) continue; } if (annotation.isRuntimeInvisible() || annotation.isRuntimeTypeInvisible()) { int currentAnnotationOffset = this.contentsOffset; generateAnnotation(annotation, currentAnnotationOffset); invisibleAnnotationsCounter--; if (this.contentsOffset != currentAnnotationOffset) { counter++; } } } if (counter != 0) { this.contents[annotationsLengthOffset++] = (byte) (counter >> 8); this.contents[annotationsLengthOffset++] = (byte) counter; int attributeLength = this.contentsOffset - attributeLengthOffset - 4; this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); this.contents[attributeLengthOffset++] = (byte) attributeLength; attributesNumber++; } else { this.contentsOffset = annotationAttributeOffset; } } annotationAttributeOffset = this.contentsOffset; if (visibleAnnotationsCounter != 0) { if (this.contentsOffset + 10 >= this.contents.length) { resizeContents(10); } int runtimeVisibleAnnotationsAttributeNameIndex = this.constantPool .literalIndex(AttributeNamesConstants.RuntimeVisibleAnnotationsName); this.contents[this.contentsOffset++] = (byte) (runtimeVisibleAnnotationsAttributeNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) runtimeVisibleAnnotationsAttributeNameIndex; int attributeLengthOffset = this.contentsOffset; this.contentsOffset += 4; // leave space for the attribute length int annotationsLengthOffset = this.contentsOffset; this.contentsOffset += 2; // leave space for the annotations length int counter = 0; loop: for (int i = 0; i < length; i++) { if (visibleAnnotationsCounter == 0) break loop; Annotation annotation; if ((annotation = annotations[i].getPersistibleAnnotation()) == null) continue; // already packaged into container. long annotationMask = annotation.resolvedType != null ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK : 0; if (annotationMask != 0 && (annotationMask & targetMask) == 0) { if (!jdk16packageInfoAnnotation(annotationMask, targetMask)) continue; } if (annotation.isRuntimeVisible() || annotation.isRuntimeTypeVisible()) { visibleAnnotationsCounter--; int currentAnnotationOffset = this.contentsOffset; generateAnnotation(annotation, currentAnnotationOffset); if (this.contentsOffset != currentAnnotationOffset) { counter++; } } } if (counter != 0) { this.contents[annotationsLengthOffset++] = (byte) (counter >> 8); this.contents[annotationsLengthOffset++] = (byte) counter; int attributeLength = this.contentsOffset - attributeLengthOffset - 4; this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); this.contents[attributeLengthOffset++] = (byte) attributeLength; attributesNumber++; } else { this.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; if ((annotation = annotations[j].getPersistibleAnnotation()) == null) continue; // already packaged into container. long annotationMask = annotation.resolvedType != null ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK : 0; if (annotationMask != 0 && (annotationMask & TagBits.AnnotationForParameter) == 0) continue; if (annotation.isRuntimeInvisible()) { annotationsCounters[i][INVISIBLE_INDEX]++; invisibleParametersAnnotationsCounter++; } else if (annotation.isRuntimeVisible()) { annotationsCounters[i][VISIBLE_INDEX]++; visibleParametersAnnotationsCounter++; } } } } int attributesNumber = 0; int annotationAttributeOffset = this.contentsOffset; if (invisibleParametersAnnotationsCounter != 0) { int globalCounter = 0; if (this.contentsOffset + 7 >= this.contents.length) { resizeContents(7); } int attributeNameIndex = this.constantPool .literalIndex(AttributeNamesConstants.RuntimeInvisibleParameterAnnotationsName); this.contents[this.contentsOffset++] = (byte) (attributeNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) attributeNameIndex; int attributeLengthOffset = this.contentsOffset; this.contentsOffset += 4; // leave space for the attribute length this.contents[this.contentsOffset++] = (byte) argumentsLength; for (int i = 0; i < argumentsLength; i++) { if (this.contentsOffset + 2 >= this.contents.length) { resizeContents(2); } if (invisibleParametersAnnotationsCounter == 0) { this.contents[this.contentsOffset++] = (byte) 0; this.contents[this.contentsOffset++] = (byte) 0; } else { final int numberOfInvisibleAnnotations = annotationsCounters[i][INVISIBLE_INDEX]; int invisibleAnnotationsOffset = this.contentsOffset; // leave space for number of annotations this.contentsOffset += 2; int counter = 0; if (numberOfInvisibleAnnotations != 0) { Argument argument = arguments[i]; Annotation[] annotations = argument.annotations; for (int j = 0, max = annotations.length; j < max; j++) { Annotation annotation; if ((annotation = annotations[j].getPersistibleAnnotation()) == null) continue; // already packaged into container. long annotationMask = annotation.resolvedType != null ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK : 0; if (annotationMask != 0 && (annotationMask & TagBits.AnnotationForParameter) == 0) continue; if (annotation.isRuntimeInvisible()) { int currentAnnotationOffset = this.contentsOffset; generateAnnotation(annotation, currentAnnotationOffset); if (this.contentsOffset != currentAnnotationOffset) { counter++; globalCounter++; } invisibleParametersAnnotationsCounter--; } } } this.contents[invisibleAnnotationsOffset++] = (byte) (counter >> 8); this.contents[invisibleAnnotationsOffset] = (byte) counter; } } if (globalCounter != 0) { int attributeLength = this.contentsOffset - attributeLengthOffset - 4; this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); this.contents[attributeLengthOffset++] = (byte) attributeLength; attributesNumber++; } else { // if globalCounter is 0, this means that the code generation for all visible annotations failed this.contentsOffset = annotationAttributeOffset; } } if (visibleParametersAnnotationsCounter != 0) { int globalCounter = 0; if (this.contentsOffset + 7 >= this.contents.length) { resizeContents(7); } int attributeNameIndex = this.constantPool .literalIndex(AttributeNamesConstants.RuntimeVisibleParameterAnnotationsName); this.contents[this.contentsOffset++] = (byte) (attributeNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) attributeNameIndex; int attributeLengthOffset = this.contentsOffset; this.contentsOffset += 4; // leave space for the attribute length this.contents[this.contentsOffset++] = (byte) argumentsLength; for (int i = 0; i < argumentsLength; i++) { if (this.contentsOffset + 2 >= this.contents.length) { resizeContents(2); } if (visibleParametersAnnotationsCounter == 0) { this.contents[this.contentsOffset++] = (byte) 0; this.contents[this.contentsOffset++] = (byte) 0; } else { final int numberOfVisibleAnnotations = annotationsCounters[i][VISIBLE_INDEX]; int visibleAnnotationsOffset = this.contentsOffset; // leave space for number of annotations this.contentsOffset += 2; int counter = 0; if (numberOfVisibleAnnotations != 0) { Argument argument = arguments[i]; Annotation[] annotations = argument.annotations; for (int j = 0, max = annotations.length; j < max; j++) { Annotation annotation; if ((annotation = annotations[j].getPersistibleAnnotation()) == null) continue; // already packaged into container. long annotationMask = annotation.resolvedType != null ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK : 0; if (annotationMask != 0 && (annotationMask & TagBits.AnnotationForParameter) == 0) continue; if (annotation.isRuntimeVisible()) { int currentAnnotationOffset = this.contentsOffset; generateAnnotation(annotation, currentAnnotationOffset); if (this.contentsOffset != currentAnnotationOffset) { counter++; globalCounter++; } visibleParametersAnnotationsCounter--; } } } this.contents[visibleAnnotationsOffset++] = (byte) (counter >> 8); this.contents[visibleAnnotationsOffset] = (byte) counter; } } if (globalCounter != 0) { int attributeLength = this.contentsOffset - attributeLengthOffset - 4; this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); this.contents[attributeLengthOffset++] = (byte) attributeLength; attributesNumber++; } else { // if globalCounter is 0, this means that the code generation for all visible annotations failed this.contentsOffset = annotationAttributeOffset; } } return attributesNumber; } /** * @param annotationContexts the given annotation contexts * @param visibleTypeAnnotationsNumber the given number of visible type annotations * @param invisibleTypeAnnotationsNumber the given number of invisible type annotations * @return the number of attributes created while dumping the annotations in the .class file */ private int generateRuntimeTypeAnnotations(final AnnotationContext[] annotationContexts, int visibleTypeAnnotationsNumber, int invisibleTypeAnnotationsNumber) { int attributesNumber = 0; final int length = annotationContexts.length; int visibleTypeAnnotationsCounter = visibleTypeAnnotationsNumber; int invisibleTypeAnnotationsCounter = invisibleTypeAnnotationsNumber; int annotationAttributeOffset = this.contentsOffset; if (invisibleTypeAnnotationsCounter != 0) { if (this.contentsOffset + 10 >= this.contents.length) { resizeContents(10); } int runtimeInvisibleAnnotationsAttributeNameIndex = this.constantPool .literalIndex(AttributeNamesConstants.RuntimeInvisibleTypeAnnotationsName); this.contents[this.contentsOffset++] = (byte) (runtimeInvisibleAnnotationsAttributeNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) runtimeInvisibleAnnotationsAttributeNameIndex; int attributeLengthOffset = this.contentsOffset; this.contentsOffset += 4; // leave space for the attribute length int annotationsLengthOffset = this.contentsOffset; this.contentsOffset += 2; // leave space for the annotations length int counter = 0; loop: for (int i = 0; i < length; i++) { if (invisibleTypeAnnotationsCounter == 0) break loop; AnnotationContext annotationContext = annotationContexts[i]; if ((annotationContext.visibility & AnnotationContext.INVISIBLE) != 0) { int currentAnnotationOffset = this.contentsOffset; generateTypeAnnotation(annotationContext, currentAnnotationOffset); invisibleTypeAnnotationsCounter--; if (this.contentsOffset != currentAnnotationOffset) { counter++; } } } if (counter != 0) { this.contents[annotationsLengthOffset++] = (byte) (counter >> 8); this.contents[annotationsLengthOffset++] = (byte) counter; int attributeLength = this.contentsOffset - attributeLengthOffset - 4; this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); this.contents[attributeLengthOffset++] = (byte) attributeLength; attributesNumber++; } else { this.contentsOffset = annotationAttributeOffset; } } annotationAttributeOffset = this.contentsOffset; if (visibleTypeAnnotationsCounter != 0) { if (this.contentsOffset + 10 >= this.contents.length) { resizeContents(10); } int runtimeVisibleAnnotationsAttributeNameIndex = this.constantPool .literalIndex(AttributeNamesConstants.RuntimeVisibleTypeAnnotationsName); this.contents[this.contentsOffset++] = (byte) (runtimeVisibleAnnotationsAttributeNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) runtimeVisibleAnnotationsAttributeNameIndex; int attributeLengthOffset = this.contentsOffset; this.contentsOffset += 4; // leave space for the attribute length int annotationsLengthOffset = this.contentsOffset; this.contentsOffset += 2; // leave space for the annotations length int counter = 0; loop: for (int i = 0; i < length; i++) { if (visibleTypeAnnotationsCounter == 0) break loop; AnnotationContext annotationContext = annotationContexts[i]; if ((annotationContext.visibility & AnnotationContext.VISIBLE) != 0) { visibleTypeAnnotationsCounter--; int currentAnnotationOffset = this.contentsOffset; generateTypeAnnotation(annotationContext, currentAnnotationOffset); if (this.contentsOffset != currentAnnotationOffset) { counter++; } } } if (counter != 0) { this.contents[annotationsLengthOffset++] = (byte) (counter >> 8); this.contents[annotationsLengthOffset++] = (byte) counter; int attributeLength = this.contentsOffset - attributeLengthOffset - 4; this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); this.contents[attributeLengthOffset++] = (byte) attributeLength; attributesNumber++; } else { this.contentsOffset = annotationAttributeOffset; } } return attributesNumber; } /** * @param binding the given method binding * @return the number of attributes created while dumping he method's parameters in the .class file (0 or 1) */ private int generateMethodParameters(final MethodBinding binding) { if (binding.sourceLambda() != null) return 0; int initialContentsOffset = this.contentsOffset; int length = 0; // count of actual parameters AbstractMethodDeclaration methodDeclaration = binding.sourceMethod(); boolean isConstructor = binding.isConstructor(); TypeBinding[] targetParameters = binding.parameters; ReferenceBinding declaringClass = binding.declaringClass; if (declaringClass.isEnum()) { if (isConstructor) { // insert String name,int ordinal length = writeArgumentName(ConstantPool.EnumName, ClassFileConstants.AccSynthetic, length); length = writeArgumentName(ConstantPool.EnumOrdinal, ClassFileConstants.AccSynthetic, length); } else if (binding instanceof SyntheticMethodBinding && CharOperation.equals(ConstantPool.ValueOf, binding.selector)) { // insert String name length = writeArgumentName(ConstantPool.Name, ClassFileConstants.AccMandated, length); targetParameters = Binding.NO_PARAMETERS; // Override "unknown" synthetics below } } boolean needSynthetics = isConstructor && declaringClass.isNestedType(); if (needSynthetics) { // Take into account the synthetic argument names // This tracks JLS8, paragraph 8.8.9 boolean anonymousWithLocalSuper = declaringClass.isAnonymousType() && declaringClass.superclass().isLocalType(); boolean anonymousWithNestedSuper = declaringClass.isAnonymousType() && declaringClass.superclass().isNestedType(); boolean isImplicitlyDeclared = ((!declaringClass.isPrivate()) || declaringClass.isAnonymousType()) && !anonymousWithLocalSuper; ReferenceBinding[] syntheticArgumentTypes = declaringClass.syntheticEnclosingInstanceTypes(); if (syntheticArgumentTypes != null) { for (int i = 0, count = syntheticArgumentTypes.length; i < count; i++) { // This behaviour tracks JLS 15.9.5.1 // This covers that the parameter ending up in a nested class must be mandated "on the way in", even if it // isn't the first. The practical relevance of this is questionable, since the constructor call will be // generated by the same constructor. boolean couldForwardToMandated = anonymousWithNestedSuper ? declaringClass.superclass().enclosingType().equals(syntheticArgumentTypes[i]) : true; int modifier = couldForwardToMandated && isImplicitlyDeclared ? ClassFileConstants.AccMandated : ClassFileConstants.AccSynthetic; char[] name = CharOperation.concat(TypeConstants.SYNTHETIC_ENCLOSING_INSTANCE_PREFIX, String.valueOf(i).toCharArray()); // cannot use depth, can be identical length = writeArgumentName(name, modifier | ClassFileConstants.AccFinal, length); } } if (binding instanceof SyntheticMethodBinding) { targetParameters = ((SyntheticMethodBinding) binding).targetMethod.parameters; methodDeclaration = ((SyntheticMethodBinding) binding).targetMethod.sourceMethod(); } } if (targetParameters != Binding.NO_PARAMETERS) { Argument[] arguments = null; if (methodDeclaration != null && methodDeclaration.arguments != null) { arguments = methodDeclaration.arguments; } for (int i = 0, max = targetParameters.length, argumentsLength = arguments != null ? arguments.length : 0; i < max; i++) { if (argumentsLength > i && arguments[i] != null) { Argument argument = arguments[i]; length = writeArgumentName(argument.name, argument.binding.modifiers, length); } else { length = writeArgumentName(null, ClassFileConstants.AccSynthetic, length); } } } if (needSynthetics) { SyntheticArgumentBinding[] syntheticOuterArguments = declaringClass.syntheticOuterLocalVariables(); int count = syntheticOuterArguments == null ? 0 : syntheticOuterArguments.length; for (int i = 0; i < count; i++) { length = writeArgumentName(syntheticOuterArguments[i].name, syntheticOuterArguments[i].modifiers | ClassFileConstants.AccSynthetic, length); } // move the extra padding arguments of the synthetic constructor invocation to the end for (int i = targetParameters.length, extraLength = binding.parameters.length; i < extraLength; i++) { TypeBinding parameter = binding.parameters[i]; length = writeArgumentName(parameter.constantPoolName(), ClassFileConstants.AccSynthetic, length); } } if (length > 0) { // so we actually output the parameter int attributeLength = 1 + 4 * length; // u1 for count, u2+u2 per parameter if (this.contentsOffset + 6 + attributeLength >= this.contents.length) { resizeContents(6 + attributeLength); } int methodParametersNameIndex = this.constantPool .literalIndex(AttributeNamesConstants.MethodParametersName); this.contents[initialContentsOffset++] = (byte) (methodParametersNameIndex >> 8); this.contents[initialContentsOffset++] = (byte) methodParametersNameIndex; this.contents[initialContentsOffset++] = (byte) (attributeLength >> 24); this.contents[initialContentsOffset++] = (byte) (attributeLength >> 16); this.contents[initialContentsOffset++] = (byte) (attributeLength >> 8); this.contents[initialContentsOffset++] = (byte) attributeLength; this.contents[initialContentsOffset++] = (byte) length; return 1; } else { return 0; } } private int writeArgumentName(char[] name, int modifiers, int oldLength) { int ensureRoomForBytes = 4; if (oldLength == 0) { // Make room for ensureRoomForBytes += 7; this.contentsOffset += 7; // Make room for attribute header + count byte } if (this.contentsOffset + ensureRoomForBytes > this.contents.length) { resizeContents(ensureRoomForBytes); } int parameterNameIndex = name == null ? 0 : this.constantPool.literalIndex(name); this.contents[this.contentsOffset++] = (byte) (parameterNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) parameterNameIndex; int flags = modifiers & (ClassFileConstants.AccFinal | ClassFileConstants.AccSynthetic | ClassFileConstants.AccMandated); this.contents[this.contentsOffset++] = (byte) (flags >> 8); this.contents[this.contentsOffset++] = (byte) flags; return oldLength + 1; } private int generateSignatureAttribute(char[] genericSignature) { int localContentsOffset = this.contentsOffset; if (localContentsOffset + 8 >= this.contents.length) { resizeContents(8); } int signatureAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.SignatureName); this.contents[localContentsOffset++] = (byte) (signatureAttributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) signatureAttributeNameIndex; // the length of a signature attribute is equals to 2 this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 2; int signatureIndex = this.constantPool.literalIndex(genericSignature); this.contents[localContentsOffset++] = (byte) (signatureIndex >> 8); this.contents[localContentsOffset++] = (byte) signatureIndex; this.contentsOffset = localContentsOffset; return 1; } private int generateSourceAttribute(String fullFileName) { int localContentsOffset = this.contentsOffset; // check that there is enough space to write all the bytes for the field info corresponding // to the @fieldBinding if (localContentsOffset + 8 >= this.contents.length) { resizeContents(8); } int sourceAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.SourceName); this.contents[localContentsOffset++] = (byte) (sourceAttributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) sourceAttributeNameIndex; // The length of a source file attribute is 2. This is a fixed-length // attribute this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 2; // write the source file name int fileNameIndex = this.constantPool.literalIndex(fullFileName.toCharArray()); this.contents[localContentsOffset++] = (byte) (fileNameIndex >> 8); this.contents[localContentsOffset++] = (byte) fileNameIndex; this.contentsOffset = localContentsOffset; return 1; } private int generateStackMapAttribute(MethodBinding methodBinding, int code_length, int codeAttributeOffset, int max_locals, boolean isClinit, Scope scope) { int attributesNumber = 0; int localContentsOffset = this.contentsOffset; StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream; stackMapFrameCodeStream.removeFramePosition(code_length); if (stackMapFrameCodeStream.hasFramePositions()) { Map frames = new HashMap(); List realFrames = traverse(isClinit ? null : methodBinding, max_locals, this.contents, codeAttributeOffset + 14, code_length, frames, isClinit, scope); int numberOfFrames = realFrames.size(); if (numberOfFrames > 1) { int stackMapTableAttributeOffset = localContentsOffset; // add the stack map table attribute if (localContentsOffset + 8 >= this.contents.length) { resizeContents(8); } int stackMapAttributeNameIndex = this.constantPool .literalIndex(AttributeNamesConstants.StackMapName); this.contents[localContentsOffset++] = (byte) (stackMapAttributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) stackMapAttributeNameIndex; int stackMapAttributeLengthOffset = localContentsOffset; // generate the attribute localContentsOffset += 4; if (localContentsOffset + 4 >= this.contents.length) { resizeContents(4); } int numberOfFramesOffset = localContentsOffset; localContentsOffset += 2; if (localContentsOffset + 2 >= this.contents.length) { resizeContents(2); } StackMapFrame currentFrame = (StackMapFrame) realFrames.get(0); for (int j = 1; j < numberOfFrames; j++) { // select next frame currentFrame = (StackMapFrame) realFrames.get(j); // generate current frame // need to find differences between the current frame and the previous frame int frameOffset = currentFrame.pc; // FULL_FRAME if (localContentsOffset + 5 >= this.contents.length) { resizeContents(5); } this.contents[localContentsOffset++] = (byte) (frameOffset >> 8); this.contents[localContentsOffset++] = (byte) frameOffset; int numberOfLocalOffset = localContentsOffset; localContentsOffset += 2; // leave two spots for number of locals int numberOfLocalEntries = 0; int numberOfLocals = currentFrame.getNumberOfLocals(); int numberOfEntries = 0; int localsLength = currentFrame.locals == null ? 0 : currentFrame.locals.length; for (int i = 0; i < localsLength && numberOfLocalEntries < numberOfLocals; i++) { if (localContentsOffset + 3 >= this.contents.length) { resizeContents(3); } VerificationTypeInfo info = currentFrame.locals[i]; if (info == null) { this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP; } else { switch (info.id()) { case T_boolean: case T_byte: case T_char: case T_int: case T_short: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER; break; case T_float: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT; break; case T_long: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG; i++; break; case T_double: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE; i++; break; case T_null: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL; break; default: this.contents[localContentsOffset++] = (byte) info.tag; switch (info.tag) { case VerificationTypeInfo.ITEM_UNINITIALIZED: int offset = info.offset; this.contents[localContentsOffset++] = (byte) (offset >> 8); this.contents[localContentsOffset++] = (byte) offset; break; case VerificationTypeInfo.ITEM_OBJECT: int indexForType = this.constantPool .literalIndexForType(info.constantPoolName()); this.contents[localContentsOffset++] = (byte) (indexForType >> 8); this.contents[localContentsOffset++] = (byte) indexForType; } } numberOfLocalEntries++; } numberOfEntries++; } if (localContentsOffset + 4 >= this.contents.length) { resizeContents(4); } this.contents[numberOfLocalOffset++] = (byte) (numberOfEntries >> 8); this.contents[numberOfLocalOffset] = (byte) numberOfEntries; int numberOfStackItems = currentFrame.numberOfStackItems; this.contents[localContentsOffset++] = (byte) (numberOfStackItems >> 8); this.contents[localContentsOffset++] = (byte) numberOfStackItems; for (int i = 0; i < numberOfStackItems; i++) { if (localContentsOffset + 3 >= this.contents.length) { resizeContents(3); } VerificationTypeInfo info = currentFrame.stackItems[i]; if (info == null) { this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP; } else { switch (info.id()) { case T_boolean: case T_byte: case T_char: case T_int: case T_short: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER; break; case T_float: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT; break; case T_long: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG; break; case T_double: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE; break; case T_null: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL; break; default: this.contents[localContentsOffset++] = (byte) info.tag; switch (info.tag) { case VerificationTypeInfo.ITEM_UNINITIALIZED: int offset = info.offset; this.contents[localContentsOffset++] = (byte) (offset >> 8); this.contents[localContentsOffset++] = (byte) offset; break; case VerificationTypeInfo.ITEM_OBJECT: int indexForType = this.constantPool .literalIndexForType(info.constantPoolName()); this.contents[localContentsOffset++] = (byte) (indexForType >> 8); this.contents[localContentsOffset++] = (byte) indexForType; } } } } } numberOfFrames--; if (numberOfFrames != 0) { this.contents[numberOfFramesOffset++] = (byte) (numberOfFrames >> 8); this.contents[numberOfFramesOffset] = (byte) numberOfFrames; int attributeLength = localContentsOffset - stackMapAttributeLengthOffset - 4; this.contents[stackMapAttributeLengthOffset++] = (byte) (attributeLength >> 24); this.contents[stackMapAttributeLengthOffset++] = (byte) (attributeLength >> 16); this.contents[stackMapAttributeLengthOffset++] = (byte) (attributeLength >> 8); this.contents[stackMapAttributeLengthOffset] = (byte) attributeLength; attributesNumber++; } else { localContentsOffset = stackMapTableAttributeOffset; } } } this.contentsOffset = localContentsOffset; return attributesNumber; } private int generateStackMapTableAttribute(MethodBinding methodBinding, int code_length, int codeAttributeOffset, int max_locals, boolean isClinit, Scope scope) { int attributesNumber = 0; int localContentsOffset = this.contentsOffset; StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream; stackMapFrameCodeStream.removeFramePosition(code_length); if (stackMapFrameCodeStream.hasFramePositions()) { Map frames = new HashMap(); List realFrames = traverse(isClinit ? null : methodBinding, max_locals, this.contents, codeAttributeOffset + 14, code_length, frames, isClinit, scope); int numberOfFrames = realFrames.size(); if (numberOfFrames > 1) { int stackMapTableAttributeOffset = localContentsOffset; // add the stack map table attribute if (localContentsOffset + 8 >= this.contents.length) { resizeContents(8); } int stackMapTableAttributeNameIndex = this.constantPool .literalIndex(AttributeNamesConstants.StackMapTableName); this.contents[localContentsOffset++] = (byte) (stackMapTableAttributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) stackMapTableAttributeNameIndex; int stackMapTableAttributeLengthOffset = localContentsOffset; // generate the attribute localContentsOffset += 4; if (localContentsOffset + 4 >= this.contents.length) { resizeContents(4); } int numberOfFramesOffset = localContentsOffset; localContentsOffset += 2; if (localContentsOffset + 2 >= this.contents.length) { resizeContents(2); } StackMapFrame currentFrame = (StackMapFrame) realFrames.get(0); StackMapFrame prevFrame = null; for (int j = 1; j < numberOfFrames; j++) { // select next frame prevFrame = currentFrame; currentFrame = (StackMapFrame) realFrames.get(j); // generate current frame // need to find differences between the current frame and the previous frame int offsetDelta = currentFrame.getOffsetDelta(prevFrame); switch (currentFrame.getFrameType(prevFrame)) { case StackMapFrame.APPEND_FRAME: if (localContentsOffset + 3 >= this.contents.length) { resizeContents(3); } int numberOfDifferentLocals = currentFrame.numberOfDifferentLocals(prevFrame); this.contents[localContentsOffset++] = (byte) (251 + numberOfDifferentLocals); this.contents[localContentsOffset++] = (byte) (offsetDelta >> 8); this.contents[localContentsOffset++] = (byte) offsetDelta; int index = currentFrame.getIndexOfDifferentLocals(numberOfDifferentLocals); int numberOfLocals = currentFrame.getNumberOfLocals(); for (int i = index; i < currentFrame.locals.length && numberOfDifferentLocals > 0; i++) { if (localContentsOffset + 6 >= this.contents.length) { resizeContents(6); } VerificationTypeInfo info = currentFrame.locals[i]; if (info == null) { this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP; } else { switch (info.id()) { case T_boolean: case T_byte: case T_char: case T_int: case T_short: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER; break; case T_float: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT; break; case T_long: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG; i++; break; case T_double: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE; i++; break; case T_null: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL; break; default: this.contents[localContentsOffset++] = (byte) info.tag; switch (info.tag) { case VerificationTypeInfo.ITEM_UNINITIALIZED: int offset = info.offset; this.contents[localContentsOffset++] = (byte) (offset >> 8); this.contents[localContentsOffset++] = (byte) offset; break; case VerificationTypeInfo.ITEM_OBJECT: int indexForType = this.constantPool .literalIndexForType(info.constantPoolName()); this.contents[localContentsOffset++] = (byte) (indexForType >> 8); this.contents[localContentsOffset++] = (byte) indexForType; } } numberOfDifferentLocals--; } } break; case StackMapFrame.SAME_FRAME: if (localContentsOffset + 1 >= this.contents.length) { resizeContents(1); } this.contents[localContentsOffset++] = (byte) offsetDelta; break; case StackMapFrame.SAME_FRAME_EXTENDED: if (localContentsOffset + 3 >= this.contents.length) { resizeContents(3); } this.contents[localContentsOffset++] = (byte) 251; this.contents[localContentsOffset++] = (byte) (offsetDelta >> 8); this.contents[localContentsOffset++] = (byte) offsetDelta; break; case StackMapFrame.CHOP_FRAME: if (localContentsOffset + 3 >= this.contents.length) { resizeContents(3); } numberOfDifferentLocals = -currentFrame.numberOfDifferentLocals(prevFrame); this.contents[localContentsOffset++] = (byte) (251 - numberOfDifferentLocals); this.contents[localContentsOffset++] = (byte) (offsetDelta >> 8); this.contents[localContentsOffset++] = (byte) offsetDelta; break; case StackMapFrame.SAME_LOCALS_1_STACK_ITEMS: if (localContentsOffset + 4 >= this.contents.length) { resizeContents(4); } this.contents[localContentsOffset++] = (byte) (offsetDelta + 64); if (currentFrame.stackItems[0] == null) { this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP; } else { switch (currentFrame.stackItems[0].id()) { case T_boolean: case T_byte: case T_char: case T_int: case T_short: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER; break; case T_float: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT; break; case T_long: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG; break; case T_double: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE; break; case T_null: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL; break; default: VerificationTypeInfo info = currentFrame.stackItems[0]; byte tag = (byte) info.tag; this.contents[localContentsOffset++] = tag; switch (tag) { case VerificationTypeInfo.ITEM_UNINITIALIZED: int offset = info.offset; this.contents[localContentsOffset++] = (byte) (offset >> 8); this.contents[localContentsOffset++] = (byte) offset; break; case VerificationTypeInfo.ITEM_OBJECT: int indexForType = this.constantPool .literalIndexForType(info.constantPoolName()); this.contents[localContentsOffset++] = (byte) (indexForType >> 8); this.contents[localContentsOffset++] = (byte) indexForType; } } } break; case StackMapFrame.SAME_LOCALS_1_STACK_ITEMS_EXTENDED: if (localContentsOffset + 6 >= this.contents.length) { resizeContents(6); } this.contents[localContentsOffset++] = (byte) 247; this.contents[localContentsOffset++] = (byte) (offsetDelta >> 8); this.contents[localContentsOffset++] = (byte) offsetDelta; if (currentFrame.stackItems[0] == null) { this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP; } else { switch (currentFrame.stackItems[0].id()) { case T_boolean: case T_byte: case T_char: case T_int: case T_short: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER; break; case T_float: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT; break; case T_long: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG; break; case T_double: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE; break; case T_null: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL; break; default: VerificationTypeInfo info = currentFrame.stackItems[0]; byte tag = (byte) info.tag; this.contents[localContentsOffset++] = tag; switch (tag) { case VerificationTypeInfo.ITEM_UNINITIALIZED: int offset = info.offset; this.contents[localContentsOffset++] = (byte) (offset >> 8); this.contents[localContentsOffset++] = (byte) offset; break; case VerificationTypeInfo.ITEM_OBJECT: int indexForType = this.constantPool .literalIndexForType(info.constantPoolName()); this.contents[localContentsOffset++] = (byte) (indexForType >> 8); this.contents[localContentsOffset++] = (byte) indexForType; } } } break; default: // FULL_FRAME if (localContentsOffset + 5 >= this.contents.length) { resizeContents(5); } this.contents[localContentsOffset++] = (byte) 255; this.contents[localContentsOffset++] = (byte) (offsetDelta >> 8); this.contents[localContentsOffset++] = (byte) offsetDelta; int numberOfLocalOffset = localContentsOffset; localContentsOffset += 2; // leave two spots for number of locals int numberOfLocalEntries = 0; numberOfLocals = currentFrame.getNumberOfLocals(); int numberOfEntries = 0; int localsLength = currentFrame.locals == null ? 0 : currentFrame.locals.length; for (int i = 0; i < localsLength && numberOfLocalEntries < numberOfLocals; i++) { if (localContentsOffset + 3 >= this.contents.length) { resizeContents(3); } VerificationTypeInfo info = currentFrame.locals[i]; if (info == null) { this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP; } else { switch (info.id()) { case T_boolean: case T_byte: case T_char: case T_int: case T_short: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER; break; case T_float: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT; break; case T_long: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG; i++; break; case T_double: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE; i++; break; case T_null: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL; break; default: this.contents[localContentsOffset++] = (byte) info.tag; switch (info.tag) { case VerificationTypeInfo.ITEM_UNINITIALIZED: int offset = info.offset; this.contents[localContentsOffset++] = (byte) (offset >> 8); this.contents[localContentsOffset++] = (byte) offset; break; case VerificationTypeInfo.ITEM_OBJECT: int indexForType = this.constantPool .literalIndexForType(info.constantPoolName()); this.contents[localContentsOffset++] = (byte) (indexForType >> 8); this.contents[localContentsOffset++] = (byte) indexForType; } } numberOfLocalEntries++; } numberOfEntries++; } if (localContentsOffset + 4 >= this.contents.length) { resizeContents(4); } this.contents[numberOfLocalOffset++] = (byte) (numberOfEntries >> 8); this.contents[numberOfLocalOffset] = (byte) numberOfEntries; int numberOfStackItems = currentFrame.numberOfStackItems; this.contents[localContentsOffset++] = (byte) (numberOfStackItems >> 8); this.contents[localContentsOffset++] = (byte) numberOfStackItems; for (int i = 0; i < numberOfStackItems; i++) { if (localContentsOffset + 3 >= this.contents.length) { resizeContents(3); } VerificationTypeInfo info = currentFrame.stackItems[i]; if (info == null) { this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP; } else { switch (info.id()) { case T_boolean: case T_byte: case T_char: case T_int: case T_short: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER; break; case T_float: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT; break; case T_long: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG; break; case T_double: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE; break; case T_null: this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL; break; default: this.contents[localContentsOffset++] = (byte) info.tag; switch (info.tag) { case VerificationTypeInfo.ITEM_UNINITIALIZED: int offset = info.offset; this.contents[localContentsOffset++] = (byte) (offset >> 8); this.contents[localContentsOffset++] = (byte) offset; break; case VerificationTypeInfo.ITEM_OBJECT: int indexForType = this.constantPool .literalIndexForType(info.constantPoolName()); this.contents[localContentsOffset++] = (byte) (indexForType >> 8); this.contents[localContentsOffset++] = (byte) indexForType; } } } } } } numberOfFrames--; if (numberOfFrames != 0) { this.contents[numberOfFramesOffset++] = (byte) (numberOfFrames >> 8); this.contents[numberOfFramesOffset] = (byte) numberOfFrames; int attributeLength = localContentsOffset - stackMapTableAttributeLengthOffset - 4; this.contents[stackMapTableAttributeLengthOffset++] = (byte) (attributeLength >> 24); this.contents[stackMapTableAttributeLengthOffset++] = (byte) (attributeLength >> 16); this.contents[stackMapTableAttributeLengthOffset++] = (byte) (attributeLength >> 8); this.contents[stackMapTableAttributeLengthOffset] = (byte) attributeLength; attributesNumber++; } else { localContentsOffset = stackMapTableAttributeOffset; } } } this.contentsOffset = localContentsOffset; return attributesNumber; } private int generateSyntheticAttribute() { int localContentsOffset = this.contentsOffset; if (localContentsOffset + 6 >= this.contents.length) { resizeContents(6); } int syntheticAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.SyntheticName); this.contents[localContentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) syntheticAttributeNameIndex; // the length of a synthetic attribute is equals to 0 this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contentsOffset = localContentsOffset; return 1; } private void generateTypeAnnotation(AnnotationContext annotationContext, int currentOffset) { Annotation annotation = annotationContext.annotation.getPersistibleAnnotation(); if (annotation == null || annotation.resolvedType == null) return; int targetType = annotationContext.targetType; int[] locations = Annotation.getLocations(annotationContext.typeReference, annotationContext.annotation); if (this.contentsOffset + 5 >= this.contents.length) { resizeContents(5); } this.contents[this.contentsOffset++] = (byte) targetType; dumpTargetTypeContents(targetType, annotationContext); dumpLocations(locations); generateAnnotation(annotation, currentOffset); } private int generateTypeAnnotationAttributeForTypeDeclaration() { TypeDeclaration typeDeclaration = this.referenceBinding.scope.referenceContext; if ((typeDeclaration.bits & ASTNode.HasTypeAnnotations) == 0) { return 0; } int attributesNumber = 0; int visibleTypeAnnotationsCounter = 0; int invisibleTypeAnnotationsCounter = 0; TypeReference superclass = typeDeclaration.superclass; List allTypeAnnotationContexts = new ArrayList(); if (superclass != null && (superclass.bits & ASTNode.HasTypeAnnotations) != 0) { superclass.getAllAnnotationContexts(AnnotationTargetTypeConstants.CLASS_EXTENDS, -1, allTypeAnnotationContexts); } TypeReference[] superInterfaces = typeDeclaration.superInterfaces; if (superInterfaces != null) { for (int i = 0; i < superInterfaces.length; i++) { TypeReference superInterface = superInterfaces[i]; if ((superInterface.bits & ASTNode.HasTypeAnnotations) == 0) { continue; } superInterface.getAllAnnotationContexts(AnnotationTargetTypeConstants.CLASS_EXTENDS, i, allTypeAnnotationContexts); } } TypeParameter[] typeParameters = typeDeclaration.typeParameters; if (typeParameters != null) { for (int i = 0, max = typeParameters.length; i < max; i++) { TypeParameter typeParameter = typeParameters[i]; if ((typeParameter.bits & ASTNode.HasTypeAnnotations) != 0) { typeParameter.getAllAnnotationContexts(AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER, i, allTypeAnnotationContexts); } } } int size = allTypeAnnotationContexts.size(); if (size != 0) { AnnotationContext[] allTypeAnnotationContextsArray = new AnnotationContext[size]; allTypeAnnotationContexts.toArray(allTypeAnnotationContextsArray); for (int j = 0, max = allTypeAnnotationContextsArray.length; j < max; j++) { AnnotationContext annotationContext = allTypeAnnotationContextsArray[j]; if ((annotationContext.visibility & AnnotationContext.INVISIBLE) != 0) { invisibleTypeAnnotationsCounter++; allTypeAnnotationContexts.add(annotationContext); } else { visibleTypeAnnotationsCounter++; allTypeAnnotationContexts.add(annotationContext); } } attributesNumber += generateRuntimeTypeAnnotations(allTypeAnnotationContextsArray, visibleTypeAnnotationsCounter, invisibleTypeAnnotationsCounter); } return attributesNumber; } private int generateVarargsAttribute() { int localContentsOffset = this.contentsOffset; /* * handle of the target jsr14 for varargs in the source * Varargs attribute * Check that there is enough space to write the attribute */ if (localContentsOffset + 6 >= this.contents.length) { resizeContents(6); } int varargsAttributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.VarargsName); this.contents[localContentsOffset++] = (byte) (varargsAttributeNameIndex >> 8); this.contents[localContentsOffset++] = (byte) varargsAttributeNameIndex; // the length of a varargs attribute is equals to 0 this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; this.contentsOffset = localContentsOffset; return 1; } /** * EXTERNAL API * Answer the actual bytes of the class file * * This method encodes the receiver structure into a byte array which is the content of the classfile. * Returns the byte array that represents the encoded structure of the receiver. * * @return byte[] */ public byte[] getBytes() { if (this.bytes == null) { this.bytes = new byte[this.headerOffset + this.contentsOffset]; System.arraycopy(this.header, 0, this.bytes, 0, this.headerOffset); System.arraycopy(this.contents, 0, this.bytes, this.headerOffset, this.contentsOffset); } return this.bytes; } /** * EXTERNAL API * Answer the compound name of the class file. * @return char[][] * e.g. {{java}, {util}, {Hashtable}}. */ public char[][] getCompoundName() { return CharOperation.splitOn('/', fileName()); } private int getParametersCount(char[] methodSignature) { int i = CharOperation.indexOf('(', methodSignature); i++; char currentCharacter = methodSignature[i]; if (currentCharacter == ')') { return 0; } int result = 0; while (true) { currentCharacter = methodSignature[i]; if (currentCharacter == ')') { return result; } switch (currentCharacter) { case '[': // array type int scanType = scanType(methodSignature, i + 1); result++; i = scanType + 1; break; case 'L': scanType = CharOperation.indexOf(';', methodSignature, i + 1); result++; i = scanType + 1; break; case 'Z': case 'B': case 'C': case 'D': case 'F': case 'I': case 'J': case 'S': result++; i++; break; default: throw new IllegalArgumentException("Invalid starting type character : " + currentCharacter); //$NON-NLS-1$ } } } private char[] getReturnType(char[] methodSignature) { // skip type parameters int paren = CharOperation.lastIndexOf(')', methodSignature); // there could be thrown exceptions behind, thus scan one type exactly return CharOperation.subarray(methodSignature, paren + 1, methodSignature.length); } private final int i4At(byte[] reference, int relativeOffset, int structOffset) { int position = relativeOffset + structOffset; return ((reference[position++] & 0xFF) << 24) + ((reference[position++] & 0xFF) << 16) + ((reference[position++] & 0xFF) << 8) + (reference[position] & 0xFF); } protected void initByteArrays(int members) { this.header = new byte[INITIAL_HEADER_SIZE]; this.contents = new byte[members < 15 ? INITIAL_CONTENTS_SIZE : INITIAL_HEADER_SIZE]; } private void initializeHeader(ClassFile parentClassFile, int accessFlags) { // generate the magic numbers inside the header this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 24); this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 16); this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 8); this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 0); long targetVersion = this.targetJDK; this.header[this.headerOffset++] = (byte) (targetVersion >> 8); // minor high this.header[this.headerOffset++] = (byte) (targetVersion >> 0); // minor low this.header[this.headerOffset++] = (byte) (targetVersion >> 24); // major high this.header[this.headerOffset++] = (byte) (targetVersion >> 16); // major low this.constantPoolOffset = this.headerOffset; this.headerOffset += 2; this.constantPool.initialize(this); this.enclosingClassFile = parentClassFile; // now we continue to generate the bytes inside the contents array this.contents[this.contentsOffset++] = (byte) (accessFlags >> 8); this.contents[this.contentsOffset++] = (byte) accessFlags; } public void initialize(SourceTypeBinding aType, ClassFile parentClassFile, boolean createProblemType) { // 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; } if (aType.isAnonymousType()) { accessFlags &= ~ClassFileConstants.AccFinal; } int finalAbstract = ClassFileConstants.AccFinal | ClassFileConstants.AccAbstract; if ((accessFlags & finalAbstract) == finalAbstract) { accessFlags &= ~finalAbstract; } initializeHeader(parentClassFile, accessFlags); // innerclasses get their names computed at code gen time int classNameIndex = this.constantPool.literalIndexForType(aType); this.contents[this.contentsOffset++] = (byte) (classNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) classNameIndex; int superclassNameIndex; if (aType.isInterface()) { superclassNameIndex = this.constantPool .literalIndexForType(ConstantPool.JavaLangObjectConstantPoolName); } else { if (aType.superclass != null) { if ((aType.superclass.tagBits & TagBits.HasMissingType) != 0) { superclassNameIndex = this.constantPool .literalIndexForType(ConstantPool.JavaLangObjectConstantPoolName); } else { superclassNameIndex = this.constantPool.literalIndexForType(aType.superclass); } } else { superclassNameIndex = 0; } } this.contents[this.contentsOffset++] = (byte) (superclassNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) superclassNameIndex; ReferenceBinding[] superInterfacesBinding = aType.superInterfaces(); int interfacesCount = superInterfacesBinding.length; int interfacesCountPosition = this.contentsOffset; this.contentsOffset += 2; int interfaceCounter = 0; for (int i = 0; i < interfacesCount; i++) { ReferenceBinding binding = superInterfacesBinding[i]; if ((binding.tagBits & TagBits.HasMissingType) != 0) { continue; } interfaceCounter++; int interfaceIndex = this.constantPool.literalIndexForType(binding); this.contents[this.contentsOffset++] = (byte) (interfaceIndex >> 8); this.contents[this.contentsOffset++] = (byte) interfaceIndex; } this.contents[interfacesCountPosition++] = (byte) (interfaceCounter >> 8); this.contents[interfacesCountPosition] = (byte) interfaceCounter; this.creatingProblemType = createProblemType; // retrieve the enclosing one guaranteed to be the one matching the propagated flow info // 1FF9ZBU: LFCOM:ALL - Local variable attributes busted (Sanity check) this.codeStream.maxFieldCount = aType.scope.outerMostClassScope().referenceType().maxFieldCount; } public void initializeForModule(ModuleBinding module) { initializeHeader(null, ClassFileConstants.AccModule); int classNameIndex = this.constantPool.literalIndexForType(TypeConstants.MODULE_INFO_NAME); this.contents[this.contentsOffset++] = (byte) (classNameIndex >> 8); this.contents[this.contentsOffset++] = (byte) classNameIndex; this.codeStream.maxFieldCount = 0; // superclass: this.contents[this.contentsOffset++] = 0; this.contents[this.contentsOffset++] = 0; // superInterfacesCount this.contents[this.contentsOffset++] = 0; this.contents[this.contentsOffset++] = 0; // fieldsCount this.contents[this.contentsOffset++] = 0; this.contents[this.contentsOffset++] = 0; // methodsCount this.contents[this.contentsOffset++] = 0; this.contents[this.contentsOffset++] = 0; } private void initializeDefaultLocals(StackMapFrame frame, MethodBinding methodBinding, int maxLocals, int codeLength) { if (maxLocals != 0) { int resolvedPosition = 0; // take into account enum constructor synthetic name+ordinal final boolean isConstructor = methodBinding.isConstructor(); if (isConstructor || !methodBinding.isStatic()) { LocalVariableBinding localVariableBinding = new LocalVariableBinding(ConstantPool.This, methodBinding.declaringClass, 0, false); localVariableBinding.resolvedPosition = 0; this.codeStream.record(localVariableBinding); localVariableBinding.recordInitializationStartPC(0); localVariableBinding.recordInitializationEndPC(codeLength); frame.putLocal(resolvedPosition, new VerificationTypeInfo(isConstructor ? VerificationTypeInfo.ITEM_UNINITIALIZED_THIS : VerificationTypeInfo.ITEM_OBJECT, methodBinding.declaringClass)); resolvedPosition++; } if (isConstructor) { if (methodBinding.declaringClass.isEnum()) { LocalVariableBinding localVariableBinding = new LocalVariableBinding(" name".toCharArray(), //$NON-NLS-1$ this.referenceBinding.scope.getJavaLangString(), 0, false); localVariableBinding.resolvedPosition = resolvedPosition; this.codeStream.record(localVariableBinding); localVariableBinding.recordInitializationStartPC(0); localVariableBinding.recordInitializationEndPC(codeLength); frame.putLocal(resolvedPosition, new VerificationTypeInfo(this.referenceBinding.scope.getJavaLangString())); resolvedPosition++; localVariableBinding = new LocalVariableBinding(" ordinal".toCharArray(), TypeBinding.INT, 0, //$NON-NLS-1$ false); localVariableBinding.resolvedPosition = resolvedPosition; this.codeStream.record(localVariableBinding); localVariableBinding.recordInitializationStartPC(0); localVariableBinding.recordInitializationEndPC(codeLength); frame.putLocal(resolvedPosition, new VerificationTypeInfo(TypeBinding.INT)); resolvedPosition++; } // take into account the synthetic parameters if (methodBinding.declaringClass.isNestedType()) { ReferenceBinding enclosingInstanceTypes[]; if ((enclosingInstanceTypes = methodBinding.declaringClass .syntheticEnclosingInstanceTypes()) != null) { for (int i = 0, max = enclosingInstanceTypes.length; i < max; i++) { // an enclosingInstanceType can only be a reference // binding. It cannot be // LongBinding or DoubleBinding LocalVariableBinding localVariableBinding = new LocalVariableBinding( (" enclosingType" + i).toCharArray(), enclosingInstanceTypes[i], 0, false); //$NON-NLS-1$ localVariableBinding.resolvedPosition = resolvedPosition; this.codeStream.record(localVariableBinding); localVariableBinding.recordInitializationStartPC(0); localVariableBinding.recordInitializationEndPC(codeLength); frame.putLocal(resolvedPosition, new VerificationTypeInfo(enclosingInstanceTypes[i])); resolvedPosition++; } } TypeBinding[] arguments; if ((arguments = methodBinding.parameters) != null) { for (int i = 0, max = arguments.length; i < max; i++) { final TypeBinding typeBinding = arguments[i]; frame.putLocal(resolvedPosition, new VerificationTypeInfo(typeBinding)); switch (typeBinding.id) { case TypeIds.T_double: case TypeIds.T_long: resolvedPosition += 2; break; default: resolvedPosition++; } } } SyntheticArgumentBinding syntheticArguments[]; if ((syntheticArguments = methodBinding.declaringClass .syntheticOuterLocalVariables()) != null) { for (int i = 0, max = syntheticArguments.length; i < max; i++) { final TypeBinding typeBinding = syntheticArguments[i].type; LocalVariableBinding localVariableBinding = new LocalVariableBinding( (" synthetic" + i).toCharArray(), typeBinding, 0, false); //$NON-NLS-1$ localVariableBinding.resolvedPosition = resolvedPosition; this.codeStream.record(localVariableBinding); localVariableBinding.recordInitializationStartPC(0); localVariableBinding.recordInitializationEndPC(codeLength); frame.putLocal(resolvedPosition, new VerificationTypeInfo(typeBinding)); switch (typeBinding.id) { case TypeIds.T_double: case TypeIds.T_long: resolvedPosition += 2; break; default: resolvedPosition++; } } } } else { TypeBinding[] arguments; if ((arguments = methodBinding.parameters) != null) { for (int i = 0, max = arguments.length; i < max; i++) { final TypeBinding typeBinding = arguments[i]; frame.putLocal(resolvedPosition, new VerificationTypeInfo(typeBinding)); switch (typeBinding.id) { case TypeIds.T_double: case TypeIds.T_long: resolvedPosition += 2; break; default: resolvedPosition++; } } } } } else { TypeBinding[] arguments; if ((arguments = methodBinding.parameters) != null) { for (int i = 0, max = arguments.length; i < max; i++) { final TypeBinding typeBinding = arguments[i]; // For the branching complexities in the generated $deserializeLambda$ we need the local variable LocalVariableBinding localVariableBinding = new LocalVariableBinding( (" synthetic" + i).toCharArray(), typeBinding, 0, true); //$NON-NLS-1$ localVariableBinding.resolvedPosition = i; this.codeStream.record(localVariableBinding); localVariableBinding.recordInitializationStartPC(0); localVariableBinding.recordInitializationEndPC(codeLength); frame.putLocal(resolvedPosition, new VerificationTypeInfo(typeBinding)); switch (typeBinding.id) { case TypeIds.T_double: case TypeIds.T_long: resolvedPosition += 2; break; default: resolvedPosition++; } } } } } } private void initializeLocals(boolean isStatic, int currentPC, StackMapFrame currentFrame) { VerificationTypeInfo[] locals = currentFrame.locals; int localsLength = locals.length; int i = 0; if (!isStatic) { // we don't want to reset the first local if the method is not static i = 1; } for (; i < localsLength; i++) { locals[i] = null; } i = 0; locals: for (int max = this.codeStream.allLocalsCounter; i < max; i++) { LocalVariableBinding localVariable = this.codeStream.locals[i]; if (localVariable == null) continue; int resolvedPosition = localVariable.resolvedPosition; final TypeBinding localVariableTypeBinding = localVariable.type; inits: for (int j = 0; j < localVariable.initializationCount; j++) { int startPC = localVariable.initializationPCs[j << 1]; int endPC = localVariable.initializationPCs[(j << 1) + 1]; if (currentPC < startPC) { continue inits; } else if (currentPC < endPC) { // the current local is an active local if (currentFrame.locals[resolvedPosition] == null) { currentFrame.locals[resolvedPosition] = new VerificationTypeInfo(localVariableTypeBinding); } continue locals; } } } } /** * 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; } public void recordInnerClasses(TypeBinding binding) { recordInnerClasses(binding, false); } public void recordInnerClasses(TypeBinding binding, boolean onBottomForBug445231) { if (this.innerClassesBindings == null) { this.innerClassesBindings = new HashMap(INNER_CLASSES_SIZE); } ReferenceBinding innerClass = (ReferenceBinding) binding; this.innerClassesBindings.put(innerClass.erasure().unannotated(), onBottomForBug445231); // should not emit yet another inner class for Outer.@Inner Inner. ReferenceBinding enclosingType = innerClass.enclosingType(); while (enclosingType != null && enclosingType.isNestedType()) { this.innerClassesBindings.put(enclosingType.erasure().unannotated(), onBottomForBug445231); enclosingType = enclosingType.enclosingType(); } } public int recordBootstrapMethod(FunctionalExpression expression) { if (this.bootstrapMethods == null) { this.bootstrapMethods = new ArrayList(); } if (expression instanceof ReferenceExpression) { for (int i = 0; i < this.bootstrapMethods.size(); i++) { FunctionalExpression fexp = (FunctionalExpression) this.bootstrapMethods.get(i); if (fexp.binding == expression.binding && TypeBinding.equalsEquals(fexp.expectedType(), expression.expectedType())) return expression.bootstrapMethodNumber = i; } } this.bootstrapMethods.add(expression); // Record which bootstrap method was assigned to the expression return expression.bootstrapMethodNumber = this.bootstrapMethods.size() - 1; } public void reset(/*@Nullable*/SourceTypeBinding typeBinding, CompilerOptions options) { // the code stream is reinitialized for each method if (typeBinding != null) { this.referenceBinding = typeBinding; this.isNestedType = typeBinding.isNestedType(); } else { this.referenceBinding = null; this.isNestedType = false; } this.targetJDK = options.targetJDK; this.produceAttributes = options.produceDebugAttributes; if (this.targetJDK >= ClassFileConstants.JDK1_6) { this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP_TABLE; if (this.targetJDK >= ClassFileConstants.JDK1_8) { this.produceAttributes |= ClassFileConstants.ATTR_TYPE_ANNOTATION; if (!(this.codeStream instanceof TypeAnnotationCodeStream) && this.referenceBinding != null) this.codeStream = new TypeAnnotationCodeStream(this); if (options.produceMethodParameters) { this.produceAttributes |= ClassFileConstants.ATTR_METHOD_PARAMETERS; } } } else if (this.targetJDK == ClassFileConstants.CLDC_1_1) { this.targetJDK = ClassFileConstants.JDK1_1; // put back 45.3 this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP; } this.bytes = null; this.constantPool.reset(); this.codeStream.reset(this); this.constantPoolOffset = 0; this.contentsOffset = 0; this.creatingProblemType = false; this.enclosingClassFile = null; this.headerOffset = 0; this.methodCount = 0; this.methodCountOffset = 0; if (this.innerClassesBindings != null) { this.innerClassesBindings.clear(); } if (this.bootstrapMethods != null) { this.bootstrapMethods.clear(); } this.missingTypes = null; this.visitedTypes = null; } /** * 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); } private VerificationTypeInfo retrieveLocal(int currentPC, int resolvedPosition) { for (int i = 0, max = this.codeStream.allLocalsCounter; i < max; i++) { LocalVariableBinding localVariable = this.codeStream.locals[i]; if (localVariable == null) continue; if (resolvedPosition == localVariable.resolvedPosition) { inits: for (int j = 0; j < localVariable.initializationCount; j++) { int startPC = localVariable.initializationPCs[j << 1]; int endPC = localVariable.initializationPCs[(j << 1) + 1]; if (currentPC < startPC) { continue inits; } else if (currentPC < endPC) { // the current local is an active local return new VerificationTypeInfo(localVariable.type); } } } } return null; } private int scanType(char[] methodSignature, int index) { switch (methodSignature[index]) { case '[': // array type return scanType(methodSignature, index + 1); case 'L': return CharOperation.indexOf(';', methodSignature, index + 1); case 'Z': case 'B': case 'C': case 'D': case 'F': case 'I': case 'J': case 'S': return index; default: throw newIllegalArgumentException(methodSignature, index); } } private static IllegalArgumentException newIllegalArgumentException(char[] string, int index) { return new IllegalArgumentException("\"" + String.valueOf(string) + "\" at " + index); //$NON-NLS-1$ //$NON-NLS-2$ } /** * INTERNAL USE-ONLY * This methods leaves the space for method counts recording. */ public void setForMethodInfos() { // leave some space for the methodCount this.methodCountOffset = this.contentsOffset; this.contentsOffset += 2; } private List filterFakeFrames(Set realJumpTargets, Map frames, int codeLength) { // no more frame to generate // filter out "fake" frames realJumpTargets.remove(Integer.valueOf(codeLength)); List result = new ArrayList(); for (Iterator iterator = realJumpTargets.iterator(); iterator.hasNext();) { Integer jumpTarget = (Integer) iterator.next(); StackMapFrame frame = (StackMapFrame) frames.get(jumpTarget); if (frame != null) { result.add(frame); } } Collections.sort(result, new Comparator() { @Override public int compare(Object o1, Object o2) { StackMapFrame frame = (StackMapFrame) o1; StackMapFrame frame2 = (StackMapFrame) o2; return frame.pc - frame2.pc; } }); return result; } private TypeBinding getTypeBinding(char[] typeConstantPoolName, Scope scope, boolean checkcast) { if (typeConstantPoolName.length == 1) { // base type switch (typeConstantPoolName[0]) { case 'Z': return TypeBinding.BOOLEAN; case 'B': return TypeBinding.BYTE; case 'C': return TypeBinding.CHAR; case 'D': return TypeBinding.DOUBLE; case 'F': return TypeBinding.FLOAT; case 'I': return TypeBinding.INT; case 'J': return TypeBinding.LONG; case 'S': return TypeBinding.SHORT; default: return null; } } else if (typeConstantPoolName[0] == '[') { int dimensions = getDimensions(typeConstantPoolName); if (typeConstantPoolName.length - dimensions == 1) { // array of base types TypeBinding baseType = null; switch (typeConstantPoolName[typeConstantPoolName.length - 1]) { case 'Z': baseType = TypeBinding.BOOLEAN; break; case 'B': baseType = TypeBinding.BYTE; break; case 'C': baseType = TypeBinding.CHAR; break; case 'D': baseType = TypeBinding.DOUBLE; break; case 'F': baseType = TypeBinding.FLOAT; break; case 'I': baseType = TypeBinding.INT; break; case 'J': baseType = TypeBinding.LONG; break; case 'S': baseType = TypeBinding.SHORT; break; case 'V': baseType = TypeBinding.VOID; } return scope.createArrayType(baseType, dimensions); } else { // array of object types char[] typeName = CharOperation.subarray(typeConstantPoolName, dimensions + 1, typeConstantPoolName.length - 1); TypeBinding type = (TypeBinding) scope.getTypeOrPackage(CharOperation.splitOn('/', typeName)); if (!type.isValidBinding()) { ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type; if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0) { type = problemReferenceBinding.closestMatch(); } else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0 && this.innerClassesBindings != null) { // check local inner types to see if this is a anonymous type Set<TypeBinding> innerTypeBindings = this.innerClassesBindings.keySet(); for (TypeBinding binding : innerTypeBindings) { if (CharOperation.equals(binding.constantPoolName(), typeName)) { type = binding; break; } } } } return scope.createArrayType(type, dimensions); } } else { char[] typeName = checkcast ? typeConstantPoolName : CharOperation.subarray(typeConstantPoolName, 1, typeConstantPoolName.length - 1); TypeBinding type = (TypeBinding) scope.getTypeOrPackage(CharOperation.splitOn('/', typeName)); if (!type.isValidBinding()) { ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type; if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0) { type = problemReferenceBinding.closestMatch(); } else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0 && this.innerClassesBindings != null) { // check local inner types to see if this is a anonymous type Set<TypeBinding> innerTypeBindings = this.innerClassesBindings.keySet(); for (TypeBinding binding : innerTypeBindings) { if (CharOperation.equals(binding.constantPoolName(), typeName)) { type = binding; break; } } } } return type; } } private TypeBinding getNewTypeBinding(char[] typeConstantPoolName, Scope scope) { char[] typeName = typeConstantPoolName; TypeBinding type = (TypeBinding) scope.getTypeOrPackage(CharOperation.splitOn('/', typeName)); if (!type.isValidBinding()) { ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type; if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0) { type = problemReferenceBinding.closestMatch(); } else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0 && this.innerClassesBindings != null) { // check local inner types to see if this is a anonymous type Set<TypeBinding> innerTypeBindings = this.innerClassesBindings.keySet(); for (TypeBinding binding : innerTypeBindings) { if (CharOperation.equals(binding.constantPoolName(), typeName)) { type = binding; break; } } } } return type; } private TypeBinding getANewArrayTypeBinding(char[] typeConstantPoolName, Scope scope) { if (typeConstantPoolName[0] == '[') { int dimensions = getDimensions(typeConstantPoolName); if (typeConstantPoolName.length - dimensions == 1) { // array of base types TypeBinding baseType = null; switch (typeConstantPoolName[typeConstantPoolName.length - 1]) { case 'Z': baseType = TypeBinding.BOOLEAN; break; case 'B': baseType = TypeBinding.BYTE; break; case 'C': baseType = TypeBinding.CHAR; break; case 'D': baseType = TypeBinding.DOUBLE; break; case 'F': baseType = TypeBinding.FLOAT; break; case 'I': baseType = TypeBinding.INT; break; case 'J': baseType = TypeBinding.LONG; break; case 'S': baseType = TypeBinding.SHORT; break; case 'V': baseType = TypeBinding.VOID; } return scope.createArrayType(baseType, dimensions); } else { // array of object types char[] elementTypeClassName = CharOperation.subarray(typeConstantPoolName, dimensions + 1, typeConstantPoolName.length - 1); TypeBinding type = (TypeBinding) scope .getTypeOrPackage(CharOperation.splitOn('/', elementTypeClassName)); if (!type.isValidBinding()) { ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type; if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0) { type = problemReferenceBinding.closestMatch(); } else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0 && this.innerClassesBindings != null) { // check local inner types to see if this is a anonymous type Set<TypeBinding> innerTypeBindings = this.innerClassesBindings.keySet(); for (TypeBinding binding : innerTypeBindings) { if (CharOperation.equals(binding.constantPoolName(), elementTypeClassName)) { type = binding; break; } } } } return scope.createArrayType(type, dimensions); } } else { TypeBinding type = (TypeBinding) scope .getTypeOrPackage(CharOperation.splitOn('/', typeConstantPoolName)); if (!type.isValidBinding()) { ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type; if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0) { type = problemReferenceBinding.closestMatch(); } else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0 && this.innerClassesBindings != null) { // check local inner types to see if this is a anonymous type Set<TypeBinding> innerTypeBindings = this.innerClassesBindings.keySet(); for (TypeBinding binding : innerTypeBindings) { if (CharOperation.equals(binding.constantPoolName(), typeConstantPoolName)) { type = binding; break; } } } } return type; } } public List traverse(MethodBinding methodBinding, int maxLocals, byte[] bytecodes, int codeOffset, int codeLength, Map<Integer, StackMapFrame> frames, boolean isClinit, Scope scope) { Set realJumpTarget = new HashSet(); StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream; int[] framePositions = stackMapFrameCodeStream.getFramePositions(); int pc = codeOffset; int index; int[] constantPoolOffsets = this.constantPool.offsets; byte[] poolContents = this.constantPool.poolContent; // set initial values for frame positions int indexInFramePositions = 0; int framePositionsLength = framePositions.length; int currentFramePosition = framePositions[0]; // set initial values for exception markers int indexInExceptionMarkers = 0; ExceptionMarker[] exceptionMarkers = stackMapFrameCodeStream.getExceptionMarkers(); int exceptionsMarkersLength = exceptionMarkers == null ? 0 : exceptionMarkers.length; boolean hasExceptionMarkers = exceptionsMarkersLength != 0; ExceptionMarker exceptionMarker = null; if (hasExceptionMarkers) { exceptionMarker = exceptionMarkers[0]; } StackMapFrame frame = new StackMapFrame(maxLocals); if (!isClinit) { initializeDefaultLocals(frame, methodBinding, maxLocals, codeLength); } frame.pc = -1; add(frames, frame.duplicate(), scope); addRealJumpTarget(realJumpTarget, -1); for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) { ExceptionLabel exceptionLabel = this.codeStream.exceptionLabels[i]; if (exceptionLabel != null) { addRealJumpTarget(realJumpTarget, exceptionLabel.position); } } while (true) { int currentPC = pc - codeOffset; if (hasExceptionMarkers && exceptionMarker.pc == currentPC) { frame.numberOfStackItems = 0; frame.addStackItem(new VerificationTypeInfo(exceptionMarker.getBinding())); indexInExceptionMarkers++; if (indexInExceptionMarkers < exceptionsMarkersLength) { exceptionMarker = exceptionMarkers[indexInExceptionMarkers]; } else { hasExceptionMarkers = false; } } if (currentFramePosition < currentPC) { do { indexInFramePositions++; if (indexInFramePositions < framePositionsLength) { currentFramePosition = framePositions[indexInFramePositions]; } else { currentFramePosition = Integer.MAX_VALUE; } } while (currentFramePosition < currentPC); } if (currentFramePosition == currentPC) { // need to build a new frame and create a stack map attribute entry StackMapFrame currentFrame = frames.get(Integer.valueOf(currentPC)); if (currentFrame == null) { currentFrame = createNewFrame(currentPC, frame, isClinit, methodBinding); add(frames, currentFrame, scope); } else { frame = currentFrame.merge(frame.duplicate(), scope).duplicate(); } indexInFramePositions++; if (indexInFramePositions < framePositionsLength) { currentFramePosition = framePositions[indexInFramePositions]; } else { currentFramePosition = Integer.MAX_VALUE; } } byte opcode = (byte) u1At(bytecodes, 0, pc); switch (opcode) { case Opcodes.OPC_nop: pc++; break; case Opcodes.OPC_aconst_null: frame.addStackItem(new VerificationTypeInfo(TypeBinding.NULL)); pc++; break; case Opcodes.OPC_iconst_m1: case Opcodes.OPC_iconst_0: case Opcodes.OPC_iconst_1: case Opcodes.OPC_iconst_2: case Opcodes.OPC_iconst_3: case Opcodes.OPC_iconst_4: case Opcodes.OPC_iconst_5: frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT)); pc++; break; case Opcodes.OPC_lconst_0: case Opcodes.OPC_lconst_1: frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG)); pc++; break; case Opcodes.OPC_fconst_0: case Opcodes.OPC_fconst_1: case Opcodes.OPC_fconst_2: frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT)); pc++; break; case Opcodes.OPC_dconst_0: case Opcodes.OPC_dconst_1: frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE)); pc++; break; case Opcodes.OPC_bipush: frame.addStackItem(new VerificationTypeInfo(TypeBinding.BYTE)); pc += 2; break; case Opcodes.OPC_sipush: frame.addStackItem(new VerificationTypeInfo(TypeBinding.SHORT)); pc += 3; break; case Opcodes.OPC_ldc: index = u1At(bytecodes, 1, pc); switch (u1At(poolContents, 0, constantPoolOffsets[index])) { case ClassFileConstants.StringTag: frame.addStackItem(new VerificationTypeInfo(scope.getJavaLangString())); break; case ClassFileConstants.IntegerTag: frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT)); break; case ClassFileConstants.FloatTag: frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT)); break; case ClassFileConstants.ClassTag: frame.addStackItem(new VerificationTypeInfo(scope.getJavaLangClass())); } pc += 2; break; case Opcodes.OPC_ldc_w: index = u2At(bytecodes, 1, pc); switch (u1At(poolContents, 0, constantPoolOffsets[index])) { case ClassFileConstants.StringTag: frame.addStackItem(new VerificationTypeInfo(scope.getJavaLangString())); break; case ClassFileConstants.IntegerTag: frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT)); break; case ClassFileConstants.FloatTag: frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT)); break; case ClassFileConstants.ClassTag: frame.addStackItem(new VerificationTypeInfo(scope.getJavaLangClass())); } pc += 3; break; case Opcodes.OPC_ldc2_w: index = u2At(bytecodes, 1, pc); switch (u1At(poolContents, 0, constantPoolOffsets[index])) { case ClassFileConstants.DoubleTag: frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE)); break; case ClassFileConstants.LongTag: frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG)); break; } pc += 3; break; case Opcodes.OPC_iload: frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT)); pc += 2; break; case Opcodes.OPC_lload: frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG)); pc += 2; break; case Opcodes.OPC_fload: frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT)); pc += 2; break; case Opcodes.OPC_dload: frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE)); pc += 2; break; case Opcodes.OPC_aload: index = u1At(bytecodes, 1, pc); VerificationTypeInfo localsN = retrieveLocal(currentPC, index); frame.addStackItem(localsN); pc += 2; break; case Opcodes.OPC_iload_0: case Opcodes.OPC_iload_1: case Opcodes.OPC_iload_2: case Opcodes.OPC_iload_3: frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT)); pc++; break; case Opcodes.OPC_lload_0: case Opcodes.OPC_lload_1: case Opcodes.OPC_lload_2: case Opcodes.OPC_lload_3: frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG)); pc++; break; case Opcodes.OPC_fload_0: case Opcodes.OPC_fload_1: case Opcodes.OPC_fload_2: case Opcodes.OPC_fload_3: frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT)); pc++; break; case Opcodes.OPC_dload_0: case Opcodes.OPC_dload_1: case Opcodes.OPC_dload_2: case Opcodes.OPC_dload_3: frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE)); pc++; break; case Opcodes.OPC_aload_0: VerificationTypeInfo locals0 = frame.locals[0]; if (locals0 == null || locals0.tag != VerificationTypeInfo.ITEM_UNINITIALIZED_THIS) { // special case to handle uninitialized object locals0 = retrieveLocal(currentPC, 0); } frame.addStackItem(locals0); pc++; break; case Opcodes.OPC_aload_1: VerificationTypeInfo locals1 = retrieveLocal(currentPC, 1); frame.addStackItem(locals1); pc++; break; case Opcodes.OPC_aload_2: VerificationTypeInfo locals2 = retrieveLocal(currentPC, 2); frame.addStackItem(locals2); pc++; break; case Opcodes.OPC_aload_3: VerificationTypeInfo locals3 = retrieveLocal(currentPC, 3); frame.addStackItem(locals3); pc++; break; case Opcodes.OPC_iaload: frame.numberOfStackItems -= 2; frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT)); pc++; break; case Opcodes.OPC_laload: frame.numberOfStackItems -= 2; frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG)); pc++; break; case Opcodes.OPC_faload: frame.numberOfStackItems -= 2; frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT)); pc++; break; case Opcodes.OPC_daload: frame.numberOfStackItems -= 2; frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE)); pc++; break; case Opcodes.OPC_aaload: frame.numberOfStackItems--; frame.replaceWithElementType(); pc++; break; case Opcodes.OPC_baload: frame.numberOfStackItems -= 2; frame.addStackItem(new VerificationTypeInfo(TypeBinding.BYTE)); pc++; break; case Opcodes.OPC_caload: frame.numberOfStackItems -= 2; frame.addStackItem(new VerificationTypeInfo(TypeBinding.CHAR)); pc++; break; case Opcodes.OPC_saload: frame.numberOfStackItems -= 2; frame.addStackItem(new VerificationTypeInfo(TypeBinding.SHORT)); pc++; break; case Opcodes.OPC_istore: case Opcodes.OPC_lstore: case Opcodes.OPC_fstore: case Opcodes.OPC_dstore: frame.numberOfStackItems--; pc += 2; break; case Opcodes.OPC_astore: index = u1At(bytecodes, 1, pc); frame.numberOfStackItems--; pc += 2; break; case Opcodes.OPC_astore_0: frame.locals[0] = frame.stackItems[frame.numberOfStackItems - 1]; frame.numberOfStackItems--; pc++; break; case Opcodes.OPC_astore_1: case Opcodes.OPC_astore_2: case Opcodes.OPC_astore_3: case Opcodes.OPC_istore_0: case Opcodes.OPC_istore_1: case Opcodes.OPC_istore_2: case Opcodes.OPC_istore_3: case Opcodes.OPC_lstore_0: case Opcodes.OPC_lstore_1: case Opcodes.OPC_lstore_2: case Opcodes.OPC_lstore_3: case Opcodes.OPC_fstore_0: case Opcodes.OPC_fstore_1: case Opcodes.OPC_fstore_2: case Opcodes.OPC_fstore_3: case Opcodes.OPC_dstore_0: case Opcodes.OPC_dstore_1: case Opcodes.OPC_dstore_2: case Opcodes.OPC_dstore_3: frame.numberOfStackItems--; pc++; break; case Opcodes.OPC_iastore: case Opcodes.OPC_lastore: case Opcodes.OPC_fastore: case Opcodes.OPC_dastore: case Opcodes.OPC_aastore: case Opcodes.OPC_bastore: case Opcodes.OPC_castore: case Opcodes.OPC_sastore: frame.numberOfStackItems -= 3; pc++; break; case Opcodes.OPC_pop: frame.numberOfStackItems--; pc++; break; case Opcodes.OPC_pop2: int numberOfStackItems = frame.numberOfStackItems; switch (frame.stackItems[numberOfStackItems - 1].id()) { case TypeIds.T_long: case TypeIds.T_double: frame.numberOfStackItems--; break; default: frame.numberOfStackItems -= 2; } pc++; break; case Opcodes.OPC_dup: frame.addStackItem(frame.stackItems[frame.numberOfStackItems - 1]); pc++; break; case Opcodes.OPC_dup_x1: VerificationTypeInfo info = frame.stackItems[frame.numberOfStackItems - 1]; frame.numberOfStackItems--; VerificationTypeInfo info2 = frame.stackItems[frame.numberOfStackItems - 1]; frame.numberOfStackItems--; frame.addStackItem(info); frame.addStackItem(info2); frame.addStackItem(info); pc++; break; case Opcodes.OPC_dup_x2: info = frame.stackItems[frame.numberOfStackItems - 1]; frame.numberOfStackItems--; info2 = frame.stackItems[frame.numberOfStackItems - 1]; frame.numberOfStackItems--; switch (info2.id()) { case TypeIds.T_long: case TypeIds.T_double: frame.addStackItem(info); frame.addStackItem(info2); frame.addStackItem(info); break; default: numberOfStackItems = frame.numberOfStackItems; VerificationTypeInfo info3 = frame.stackItems[numberOfStackItems - 1]; frame.numberOfStackItems--; frame.addStackItem(info); frame.addStackItem(info3); frame.addStackItem(info2); frame.addStackItem(info); } pc++; break; case Opcodes.OPC_dup2: info = frame.stackItems[frame.numberOfStackItems - 1]; frame.numberOfStackItems--; switch (info.id()) { case TypeIds.T_double: case TypeIds.T_long: frame.addStackItem(info); frame.addStackItem(info); break; default: info2 = frame.stackItems[frame.numberOfStackItems - 1]; frame.numberOfStackItems--; frame.addStackItem(info2); frame.addStackItem(info); frame.addStackItem(info2); frame.addStackItem(info); } pc++; break; case Opcodes.OPC_dup2_x1: info = frame.stackItems[frame.numberOfStackItems - 1]; frame.numberOfStackItems--; info2 = frame.stackItems[frame.numberOfStackItems - 1]; frame.numberOfStackItems--; switch (info.id()) { case TypeIds.T_double: case TypeIds.T_long: frame.addStackItem(info); frame.addStackItem(info2); frame.addStackItem(info); break; default: VerificationTypeInfo info3 = frame.stackItems[frame.numberOfStackItems - 1]; frame.numberOfStackItems--; frame.addStackItem(info2); frame.addStackItem(info); frame.addStackItem(info3); frame.addStackItem(info2); frame.addStackItem(info); } pc++; break; case Opcodes.OPC_dup2_x2: numberOfStackItems = frame.numberOfStackItems; info = frame.stackItems[numberOfStackItems - 1]; frame.numberOfStackItems--; info2 = frame.stackItems[frame.numberOfStackItems - 1]; frame.numberOfStackItems--; switch (info.id()) { case TypeIds.T_long: case TypeIds.T_double: switch (info2.id()) { case TypeIds.T_long: case TypeIds.T_double: // form 4 frame.addStackItem(info); frame.addStackItem(info2); frame.addStackItem(info); break; default: // form 2 numberOfStackItems = frame.numberOfStackItems; VerificationTypeInfo info3 = frame.stackItems[numberOfStackItems - 1]; frame.numberOfStackItems--; frame.addStackItem(info); frame.addStackItem(info3); frame.addStackItem(info2); frame.addStackItem(info); } break; default: numberOfStackItems = frame.numberOfStackItems; VerificationTypeInfo info3 = frame.stackItems[numberOfStackItems - 1]; frame.numberOfStackItems--; switch (info3.id()) { case TypeIds.T_long: case TypeIds.T_double: // form 3 frame.addStackItem(info2); frame.addStackItem(info); frame.addStackItem(info3); frame.addStackItem(info2); frame.addStackItem(info); break; default: // form 1 numberOfStackItems = frame.numberOfStackItems; VerificationTypeInfo info4 = frame.stackItems[numberOfStackItems - 1]; frame.numberOfStackItems--; frame.addStackItem(info2); frame.addStackItem(info); frame.addStackItem(info4); frame.addStackItem(info3); frame.addStackItem(info2); frame.addStackItem(info); } } pc++; break; case Opcodes.OPC_swap: numberOfStackItems = frame.numberOfStackItems; info = frame.stackItems[numberOfStackItems - 1]; info2 = frame.stackItems[numberOfStackItems - 2]; frame.stackItems[numberOfStackItems - 1] = info2; frame.stackItems[numberOfStackItems - 2] = info; pc++; break; case Opcodes.OPC_iadd: case Opcodes.OPC_ladd: case Opcodes.OPC_fadd: case Opcodes.OPC_dadd: case Opcodes.OPC_isub: case Opcodes.OPC_lsub: case Opcodes.OPC_fsub: case Opcodes.OPC_dsub: case Opcodes.OPC_imul: case Opcodes.OPC_lmul: case Opcodes.OPC_fmul: case Opcodes.OPC_dmul: case Opcodes.OPC_idiv: case Opcodes.OPC_ldiv: case Opcodes.OPC_fdiv: case Opcodes.OPC_ddiv: case Opcodes.OPC_irem: case Opcodes.OPC_lrem: case Opcodes.OPC_frem: case Opcodes.OPC_drem: case Opcodes.OPC_ishl: case Opcodes.OPC_lshl: case Opcodes.OPC_ishr: case Opcodes.OPC_lshr: case Opcodes.OPC_iushr: case Opcodes.OPC_lushr: case Opcodes.OPC_iand: case Opcodes.OPC_land: case Opcodes.OPC_ior: case Opcodes.OPC_lor: case Opcodes.OPC_ixor: case Opcodes.OPC_lxor: frame.numberOfStackItems--; pc++; break; case Opcodes.OPC_ineg: case Opcodes.OPC_lneg: case Opcodes.OPC_fneg: case Opcodes.OPC_dneg: pc++; break; case Opcodes.OPC_iinc: pc += 3; break; case Opcodes.OPC_i2l: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.LONG); pc++; break; case Opcodes.OPC_i2f: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.FLOAT); pc++; break; case Opcodes.OPC_i2d: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.DOUBLE); pc++; break; case Opcodes.OPC_l2i: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT); pc++; break; case Opcodes.OPC_l2f: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.FLOAT); pc++; break; case Opcodes.OPC_l2d: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.DOUBLE); pc++; break; case Opcodes.OPC_f2i: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT); pc++; break; case Opcodes.OPC_f2l: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.LONG); pc++; break; case Opcodes.OPC_f2d: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.DOUBLE); pc++; break; case Opcodes.OPC_d2i: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT); pc++; break; case Opcodes.OPC_d2l: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.LONG); pc++; break; case Opcodes.OPC_d2f: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.FLOAT); pc++; break; case Opcodes.OPC_i2b: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.BYTE); pc++; break; case Opcodes.OPC_i2c: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.CHAR); pc++; break; case Opcodes.OPC_i2s: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.SHORT); pc++; break; case Opcodes.OPC_lcmp: case Opcodes.OPC_fcmpl: case Opcodes.OPC_fcmpg: case Opcodes.OPC_dcmpl: case Opcodes.OPC_dcmpg: frame.numberOfStackItems -= 2; frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT)); pc++; break; case Opcodes.OPC_ifeq: case Opcodes.OPC_ifne: case Opcodes.OPC_iflt: case Opcodes.OPC_ifge: case Opcodes.OPC_ifgt: case Opcodes.OPC_ifle: frame.numberOfStackItems--; int jumpPC = currentPC + i2At(bytecodes, 1, pc); addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope); pc += 3; break; case Opcodes.OPC_if_icmpeq: case Opcodes.OPC_if_icmpne: case Opcodes.OPC_if_icmplt: case Opcodes.OPC_if_icmpge: case Opcodes.OPC_if_icmpgt: case Opcodes.OPC_if_icmple: case Opcodes.OPC_if_acmpeq: case Opcodes.OPC_if_acmpne: frame.numberOfStackItems -= 2; jumpPC = currentPC + i2At(bytecodes, 1, pc); addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope); pc += 3; break; case Opcodes.OPC_goto: jumpPC = currentPC + i2At(bytecodes, 1, pc); addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope); pc += 3; addRealJumpTarget(realJumpTarget, pc - codeOffset); break; case Opcodes.OPC_tableswitch: frame.numberOfStackItems--; pc++; while (((pc - codeOffset) & 0x03) != 0) { pc++; } // default offset jumpPC = currentPC + i4At(bytecodes, 0, pc); addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope); pc += 4; // default int low = i4At(bytecodes, 0, pc); pc += 4; int high = i4At(bytecodes, 0, pc); pc += 4; int length = high - low + 1; for (int i = 0; i < length; i++) { // pair offset jumpPC = currentPC + i4At(bytecodes, 0, pc); addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope); pc += 4; } break; case Opcodes.OPC_lookupswitch: frame.numberOfStackItems--; pc++; while (((pc - codeOffset) & 0x03) != 0) { pc++; } jumpPC = currentPC + i4At(bytecodes, 0, pc); addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope); pc += 4; // default offset int npairs = (int) u4At(bytecodes, 0, pc); pc += 4; // npair value for (int i = 0; i < npairs; i++) { pc += 4; // case value // pair offset jumpPC = currentPC + i4At(bytecodes, 0, pc); addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope); pc += 4; } break; case Opcodes.OPC_ireturn: case Opcodes.OPC_lreturn: case Opcodes.OPC_freturn: case Opcodes.OPC_dreturn: case Opcodes.OPC_areturn: frame.numberOfStackItems--; pc++; addRealJumpTarget(realJumpTarget, pc - codeOffset); break; case Opcodes.OPC_return: pc++; addRealJumpTarget(realJumpTarget, pc - codeOffset); break; case Opcodes.OPC_getstatic: index = u2At(bytecodes, 1, pc); int nameAndTypeIndex = u2At(poolContents, 3, constantPoolOffsets[index]); int utf8index = u2At(poolContents, 3, constantPoolOffsets[nameAndTypeIndex]); char[] descriptor = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); TypeBinding typeBinding = getTypeBinding(descriptor, scope, false); if (typeBinding != null) { frame.addStackItem(new VerificationTypeInfo(typeBinding)); } pc += 3; break; case Opcodes.OPC_putstatic: frame.numberOfStackItems--; pc += 3; break; case Opcodes.OPC_getfield: index = u2At(bytecodes, 1, pc); nameAndTypeIndex = u2At(poolContents, 3, constantPoolOffsets[index]); utf8index = u2At(poolContents, 3, constantPoolOffsets[nameAndTypeIndex]); descriptor = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); frame.numberOfStackItems--; typeBinding = getTypeBinding(descriptor, scope, false); if (typeBinding != null) { frame.addStackItem(new VerificationTypeInfo(typeBinding)); } pc += 3; break; case Opcodes.OPC_putfield: frame.numberOfStackItems -= 2; pc += 3; break; case Opcodes.OPC_invokevirtual: index = u2At(bytecodes, 1, pc); nameAndTypeIndex = u2At(poolContents, 3, constantPoolOffsets[index]); utf8index = u2At(poolContents, 3, constantPoolOffsets[nameAndTypeIndex]); descriptor = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); utf8index = u2At(poolContents, 1, constantPoolOffsets[nameAndTypeIndex]); char[] name = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); frame.numberOfStackItems -= (getParametersCount(descriptor) + 1); char[] returnType = getReturnType(descriptor); typeBinding = getTypeBinding(returnType, scope, false); if (typeBinding != null) { frame.addStackItem(new VerificationTypeInfo(typeBinding)); } pc += 3; break; case Opcodes.OPC_invokedynamic: index = u2At(bytecodes, 1, pc); nameAndTypeIndex = u2At(poolContents, 3, constantPoolOffsets[index]); utf8index = u2At(poolContents, 3, constantPoolOffsets[nameAndTypeIndex]); descriptor = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); frame.numberOfStackItems -= getParametersCount(descriptor); returnType = getReturnType(descriptor); typeBinding = getTypeBinding(returnType, scope, false); if (typeBinding != null) { frame.addStackItem(new VerificationTypeInfo(typeBinding)); } pc += 5; break; case Opcodes.OPC_invokespecial: index = u2At(bytecodes, 1, pc); nameAndTypeIndex = u2At(poolContents, 3, constantPoolOffsets[index]); utf8index = u2At(poolContents, 3, constantPoolOffsets[nameAndTypeIndex]); descriptor = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); utf8index = u2At(poolContents, 1, constantPoolOffsets[nameAndTypeIndex]); name = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); frame.numberOfStackItems -= getParametersCount(descriptor); if (CharOperation.equals(ConstantPool.Init, name)) { // constructor frame.stackItems[frame.numberOfStackItems - 1].tag = VerificationTypeInfo.ITEM_OBJECT; } frame.numberOfStackItems--; returnType = getReturnType(descriptor); typeBinding = getTypeBinding(returnType, scope, false); if (typeBinding != null) { frame.addStackItem(new VerificationTypeInfo(typeBinding)); } pc += 3; break; case Opcodes.OPC_invokestatic: index = u2At(bytecodes, 1, pc); nameAndTypeIndex = u2At(poolContents, 3, constantPoolOffsets[index]); utf8index = u2At(poolContents, 3, constantPoolOffsets[nameAndTypeIndex]); descriptor = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); utf8index = u2At(poolContents, 1, constantPoolOffsets[nameAndTypeIndex]); name = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); frame.numberOfStackItems -= getParametersCount(descriptor); returnType = getReturnType(descriptor); typeBinding = getTypeBinding(returnType, scope, false); if (typeBinding != null) { frame.addStackItem(new VerificationTypeInfo(typeBinding)); } pc += 3; break; case Opcodes.OPC_invokeinterface: index = u2At(bytecodes, 1, pc); nameAndTypeIndex = u2At(poolContents, 3, constantPoolOffsets[index]); utf8index = u2At(poolContents, 3, constantPoolOffsets[nameAndTypeIndex]); descriptor = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); utf8index = u2At(poolContents, 1, constantPoolOffsets[nameAndTypeIndex]); name = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); // we don't need count and args // u1At(bytecodes, 3, pc); // count // u1At(bytecodes, 4, pc); // extra args frame.numberOfStackItems -= (getParametersCount(descriptor) + 1); returnType = getReturnType(descriptor); typeBinding = getTypeBinding(returnType, scope, false); if (typeBinding != null) { frame.addStackItem(new VerificationTypeInfo(typeBinding)); } pc += 5; break; case Opcodes.OPC_new: index = u2At(bytecodes, 1, pc); utf8index = u2At(poolContents, 1, constantPoolOffsets[index]); char[] className = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); typeBinding = getNewTypeBinding(className, scope); VerificationTypeInfo verificationTypeInfo = new VerificationTypeInfo( VerificationTypeInfo.ITEM_UNINITIALIZED, typeBinding); verificationTypeInfo.offset = currentPC; frame.addStackItem(verificationTypeInfo); pc += 3; break; case Opcodes.OPC_newarray: TypeBinding arrayType = null; switch (u1At(bytecodes, 1, pc)) { case ClassFileConstants.INT_ARRAY: arrayType = scope.createArrayType(TypeBinding.INT, 1); break; case ClassFileConstants.BYTE_ARRAY: arrayType = scope.createArrayType(TypeBinding.BYTE, 1); break; case ClassFileConstants.BOOLEAN_ARRAY: arrayType = scope.createArrayType(TypeBinding.BOOLEAN, 1); break; case ClassFileConstants.SHORT_ARRAY: arrayType = scope.createArrayType(TypeBinding.SHORT, 1); break; case ClassFileConstants.CHAR_ARRAY: arrayType = scope.createArrayType(TypeBinding.CHAR, 1); break; case ClassFileConstants.LONG_ARRAY: arrayType = scope.createArrayType(TypeBinding.LONG, 1); break; case ClassFileConstants.FLOAT_ARRAY: arrayType = scope.createArrayType(TypeBinding.FLOAT, 1); break; case ClassFileConstants.DOUBLE_ARRAY: arrayType = scope.createArrayType(TypeBinding.DOUBLE, 1); break; } frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(arrayType); pc += 2; break; case Opcodes.OPC_anewarray: index = u2At(bytecodes, 1, pc); utf8index = u2At(poolContents, 1, constantPoolOffsets[index]); className = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); frame.numberOfStackItems--; typeBinding = getANewArrayTypeBinding(className, scope); if (typeBinding != null) { if (typeBinding.isArrayType()) { ArrayBinding arrayBinding = (ArrayBinding) typeBinding; frame.addStackItem(new VerificationTypeInfo(scope .createArrayType(arrayBinding.leafComponentType(), arrayBinding.dimensions + 1))); } else { frame.addStackItem(new VerificationTypeInfo(scope.createArrayType(typeBinding, 1))); } } pc += 3; break; case Opcodes.OPC_arraylength: frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT); pc++; break; case Opcodes.OPC_athrow: frame.numberOfStackItems--; pc++; addRealJumpTarget(realJumpTarget, pc - codeOffset); break; case Opcodes.OPC_checkcast: index = u2At(bytecodes, 1, pc); utf8index = u2At(poolContents, 1, constantPoolOffsets[index]); className = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); typeBinding = getTypeBinding(className, scope, true); if (typeBinding != null) { frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(typeBinding); } pc += 3; break; case Opcodes.OPC_instanceof: // no need to know the class index = u2At(bytecodes, 1, pc); frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT); pc += 3; break; case Opcodes.OPC_monitorenter: case Opcodes.OPC_monitorexit: frame.numberOfStackItems--; pc++; break; case Opcodes.OPC_wide: opcode = (byte) u1At(bytecodes, 1, pc); if (opcode == Opcodes.OPC_iinc) { // index = u2At(bytecodes, 2, pc); // i2At(bytecodes, 4, pc); // const // we don't need the index and the const value pc += 6; } else { index = u2At(bytecodes, 2, pc); // need to handle iload, fload, aload, lload, dload, istore, fstore, astore, lstore or dstore switch (opcode) { case Opcodes.OPC_iload: frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT)); break; case Opcodes.OPC_fload: frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT)); break; case Opcodes.OPC_aload: localsN = frame.locals[index]; if (localsN == null) { localsN = retrieveLocal(currentPC, index); } frame.addStackItem(localsN); break; case Opcodes.OPC_lload: frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG)); break; case Opcodes.OPC_dload: frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE)); break; case Opcodes.OPC_istore: frame.numberOfStackItems--; break; case Opcodes.OPC_fstore: frame.numberOfStackItems--; break; case Opcodes.OPC_astore: frame.locals[index] = frame.stackItems[frame.numberOfStackItems - 1]; frame.numberOfStackItems--; break; case Opcodes.OPC_lstore: frame.numberOfStackItems--; break; case Opcodes.OPC_dstore: frame.numberOfStackItems--; break; } pc += 4; } break; case Opcodes.OPC_multianewarray: index = u2At(bytecodes, 1, pc); utf8index = u2At(poolContents, 1, constantPoolOffsets[index]); className = utf8At(poolContents, constantPoolOffsets[utf8index] + 3, u2At(poolContents, 1, constantPoolOffsets[utf8index])); int dimensions = u1At(bytecodes, 3, pc); // dimensions frame.numberOfStackItems -= dimensions; // class name is already the name of the right array type with all dimensions typeBinding = getTypeBinding(className, scope, false); if (typeBinding != null) { frame.addStackItem(new VerificationTypeInfo(typeBinding)); } pc += 4; break; case Opcodes.OPC_ifnull: case Opcodes.OPC_ifnonnull: frame.numberOfStackItems--; jumpPC = currentPC + i2At(bytecodes, 1, pc); addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope); pc += 3; break; case Opcodes.OPC_goto_w: jumpPC = currentPC + i4At(bytecodes, 1, pc); addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope); pc += 5; addRealJumpTarget(realJumpTarget, pc - codeOffset); // handle infinite loop break; default: // should not occur if (this.codeStream.methodDeclaration != null) { this.codeStream.methodDeclaration.scope.problemReporter().abortDueToInternalError( Messages.bind(Messages.abort_invalidOpcode, new Object[] { Byte.valueOf(opcode), Integer.valueOf(pc), new String(methodBinding.shortReadableName()), }), this.codeStream.methodDeclaration); } else { this.codeStream.lambdaExpression.scope.problemReporter().abortDueToInternalError( Messages.bind(Messages.abort_invalidOpcode, new Object[] { Byte.valueOf(opcode), Integer.valueOf(pc), new String(methodBinding.shortReadableName()), }), this.codeStream.lambdaExpression); } break; } if (pc >= (codeLength + codeOffset)) { break; } } return filterFakeFrames(realJumpTarget, frames, codeLength); } private StackMapFrame createNewFrame(int currentPC, StackMapFrame frame, boolean isClinit, MethodBinding methodBinding) { StackMapFrame newFrame = frame.duplicate(); newFrame.pc = currentPC; // initialize locals initializeLocals(isClinit ? true : methodBinding.isStatic(), currentPC, newFrame); return newFrame; } private int getDimensions(char[] returnType) { int dimensions = 0; while (returnType[dimensions] == '[') { dimensions++; } return dimensions; } private void addRealJumpTarget(Set realJumpTarget, int pc) { realJumpTarget.add(Integer.valueOf(pc)); } private void addRealJumpTarget(Set realJumpTarget, int pc, Map frames, StackMapFrame frame, Scope scope) { realJumpTarget.add(Integer.valueOf(pc)); add(frames, frame, scope); } private void add(Map<Integer, StackMapFrame> frames, StackMapFrame frame, Scope scope) { Integer key = Integer.valueOf(frame.pc); StackMapFrame existingFrame = frames.get(key); if (existingFrame == null) { frames.put(key, frame); } else { // we need to merge frames.put(key, existingFrame.merge(frame, scope)); } } private final int u1At(byte[] reference, int relativeOffset, int structOffset) { return (reference[relativeOffset + structOffset] & 0xFF); } private final int u2At(byte[] reference, int relativeOffset, int structOffset) { int position = relativeOffset + structOffset; return ((reference[position++] & 0xFF) << 8) + (reference[position] & 0xFF); } private final long u4At(byte[] reference, int relativeOffset, int structOffset) { int position = relativeOffset + structOffset; return (((reference[position++] & 0xFFL) << 24) + ((reference[position++] & 0xFF) << 16) + ((reference[position++] & 0xFF) << 8) + (reference[position] & 0xFF)); } private final int i2At(byte[] reference, int relativeOffset, int structOffset) { int position = relativeOffset + structOffset; return (reference[position++] << 8) + (reference[position] & 0xFF); } public char[] utf8At(byte[] reference, int absoluteOffset, int bytesAvailable) { int length = bytesAvailable; char outputBuf[] = new char[bytesAvailable]; int outputPos = 0; int readOffset = absoluteOffset; while (length != 0) { int x = reference[readOffset++] & 0xFF; length--; if ((0x80 & x) != 0) { if ((x & 0x20) != 0) { length -= 2; x = ((x & 0xF) << 12) | ((reference[readOffset++] & 0x3F) << 6) | (reference[readOffset++] & 0x3F); } else { length--; x = ((x & 0x1F) << 6) | (reference[readOffset++] & 0x3F); } } outputBuf[outputPos++] = (char) x; } if (outputPos != bytesAvailable) { System.arraycopy(outputBuf, 0, (outputBuf = new char[outputPos]), 0, outputPos); } return outputBuf; } }