diff options
author | Andy Clement <aclement@gopivotal.com> | 2015-03-04 16:05:27 -0800 |
---|---|---|
committer | Andy Clement <aclement@gopivotal.com> | 2015-03-04 16:05:27 -0800 |
commit | c4d7b61ef3f9e5b54f3216b049a106f2523a60a4 (patch) | |
tree | c1602049db800a33b25ac873bd04c3c0a6383ea8 /org.eclipse.jdt.core | |
parent | 3869f363bc6e473f4063faf2c296825283a21f1a (diff) | |
download | aspectj-c4d7b61ef3f9e5b54f3216b049a106f2523a60a4.tar.gz aspectj-c4d7b61ef3f9e5b54f3216b049a106f2523a60a4.zip |
very early java9 support - can resolve classes in jimages
Diffstat (limited to 'org.eclipse.jdt.core')
-rw-r--r-- | org.eclipse.jdt.core/jdtcore-for-aspectj-src.zip | bin | 4519762 -> 4524499 bytes | |||
-rw-r--r-- | org.eclipse.jdt.core/jdtcore-for-aspectj.jar | bin | 8336436 -> 8406024 bytes | |||
-rw-r--r-- | org.eclipse.jdt.core/src/org/aspectj/org/eclipse/jdt/internal/compiler/ClassFile.java | 6784 |
3 files changed, 6784 insertions, 0 deletions
diff --git a/org.eclipse.jdt.core/jdtcore-for-aspectj-src.zip b/org.eclipse.jdt.core/jdtcore-for-aspectj-src.zip Binary files differindex c35fb62cd..f619bc2d0 100644 --- a/org.eclipse.jdt.core/jdtcore-for-aspectj-src.zip +++ b/org.eclipse.jdt.core/jdtcore-for-aspectj-src.zip diff --git a/org.eclipse.jdt.core/jdtcore-for-aspectj.jar b/org.eclipse.jdt.core/jdtcore-for-aspectj.jar Binary files differindex cd33770a9..779300088 100644 --- a/org.eclipse.jdt.core/jdtcore-for-aspectj.jar +++ b/org.eclipse.jdt.core/jdtcore-for-aspectj.jar diff --git a/org.eclipse.jdt.core/src/org/aspectj/org/eclipse/jdt/internal/compiler/ClassFile.java b/org.eclipse.jdt.core/src/org/aspectj/org/eclipse/jdt/internal/compiler/ClassFile.java new file mode 100644 index 000000000..e3d1c415c --- /dev/null +++ b/org.eclipse.jdt.core/src/org/aspectj/org/eclipse/jdt/internal/compiler/ClassFile.java @@ -0,0 +1,6784 @@ +/******************************************************************************* + * Copyright (c) 2000, 2014 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * 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 + * Stephan Herrmann - Contribution for + * Bug 438458 - [1.8][null] clean up handling of null type annotations wrt type variables + *******************************************************************************/ +package org.aspectj.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.aspectj.org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation; +import org.aspectj.org.eclipse.jdt.core.compiler.IProblem; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Expression; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.FunctionalExpression; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.LambdaExpression; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MemberValuePair; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NormalAnnotation; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Receiver; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleNameReference; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeParameter; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.AnnotationContext; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.CodeStream; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.ConstantPool; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.ExceptionLabel; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.Opcodes; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.StackMapFrame; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.TypeAnnotationCodeStream; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream.ExceptionMarker; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream.StackDepthMarker; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream.StackMarker; +import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.VerificationTypeInfo; +import org.aspectj.org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.aspectj.org.eclipse.jdt.internal.compiler.impl.Constant; +import org.aspectj.org.eclipse.jdt.internal.compiler.impl.StringConstant; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.FieldBinding; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TagBits; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeConstants; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeIds; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; +import org.aspectj.org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; +import org.aspectj.org.eclipse.jdt.internal.compiler.problem.ShouldNotImplement; +import org.aspectj.org.eclipse.jdt.internal.compiler.util.Messages; +import org.aspectj.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 Set 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 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; + + /** + * INTERNAL USE-ONLY + * Request the creation of a ClassFile compatible representation of a problematic type + * + * @param typeDeclaration org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration + * @param unitResult org.aspectj.org.eclipse.jdt.internal.compiler.CompilationUnitResult + */ + public static void createProblemType(TypeDeclaration typeDeclaration, CompilationResult unitResult) { + SourceTypeBinding typeBinding = typeDeclaration.binding; + ClassFile classFile = ClassFile.getNewInstance(typeBinding); + classFile.initialize(typeBinding, null, 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 + 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, 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(); + } + + /** + * INTERNAL USE-ONLY + * Generate the byte for a problem method info that correspond to a bogus method. + * + * @param method org.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration + * @param methodBinding org.aspectj.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) { + // AspectJ Extension - use the original array if its set + // original code: + // final Annotation[] annotations = typeDeclaration.annotations; + // new code: + Annotation[] annotations = typeDeclaration.originalAnnotations; + if (annotations == null) annotations = typeDeclaration.annotations; + // End AspectJ Extension + 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.toArray(innerClasses); + Arrays.sort(innerClasses, new Comparator() { + public int compare(Object o1, Object o2) { + TypeBinding binding1 = (TypeBinding) o1; + TypeBinding binding2 = (TypeBinding) o2; + return CharOperation.compareTo(binding1.constantPoolName(), binding2.constantPoolName()); + } + }); + attributesNumber += generateInnerClassAttribute(numberOfInnerClasses, innerClasses); + } + if (this.missingTypes != null) { + generateMissingTypesAttribute(); + attributesNumber++; + } + + attributesNumber += generateTypeAnnotationAttributeForTypeDeclaration(); + + // AspectJ Extension + // write any "extraAttributes" + if (extraAttributes != null) { + for (int i=0, len=extraAttributes.size(); i < len; i++) { + IAttribute attribute = (IAttribute)extraAttributes.get(i); + short nameIndex = (short)constantPool.literalIndex(attribute.getNameChars()); + writeToContents(attribute.getAllBytes(nameIndex,constantPool)); + attributesNumber++; + } + } + // End AspectJ Extension + + // 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; + } + + // AspectJ Extension + public List/*<IAttribute>*/ extraAttributes = new ArrayList(1); + // End AspectJ Extension + + /** + * 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()); + } + 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.aspectj.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); + 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.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration + * @param methodBinding org.aspectj.org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding + * @param problems org.aspectj.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.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration + * @param methodBinding org.aspectj.org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding + * @param problems org.aspectj.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.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration + * @param methodBinding org.aspectj.org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding + * @param problems org.aspectj.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.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration + * @param methodBinding org.aspectj.org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding + * @param problems org.aspectj.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; + 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: + // TODO [andy] do we need to do this after the loop to ensure it is done last? + addSyntheticDeserializeLambda(syntheticMethod,this.referenceBinding.syntheticMethods()); + break; + } + } + emittedSyntheticsCount = currentSyntheticsCount; + } + } + } + + 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.aspectj.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.aspectj.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.aspectj.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.aspectj.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.aspectj.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.aspectj.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); + completeCodeAttributeForSyntheticMethod( + true, + 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 + * That method completes the creation of the code attribute by setting + * - the attribute_length + * - max_stack + * - max_locals + * - code_length + * - exception table + * - and debug attributes if necessary. + * + * @param codeAttributeOffset <CODE>int</CODE> + */ + public void completeCodeAttribute(int codeAttributeOffset) { + // reinitialize the localContents with the byte modified by the code stream + this.contents = 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); + } + + 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); + } + + 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) { + // 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); + } + + if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) { + attributesNumber += generateStackMapAttribute( + null, + code_length, + codeAttributeOffset, + max_locals, + true); + } + + 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) { + // 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); + } + + if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) { + attributesNumber += generateStackMapAttribute( + null, + code_length, + codeAttributeOffset, + max_locals, + true); + } + + 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); + } + + if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) { + attributesNumber += generateStackMapAttribute( + binding, + code_length, + codeAttributeOffset, + max_locals, + false); + } + + // 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); + } + + if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) { + attributesNumber += generateStackMapAttribute( + binding, + code_length, + codeAttributeOffset, + max_locals, + false); + } + + // 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.aspectj.org.eclipse.jdt.internal.compiler.lookup.SyntheticAccessMethodBinding + * @param codeAttributeOffset <CODE>int</CODE> + */ + public void completeCodeAttributeForSyntheticMethod( + boolean hasExceptionHandlers, + SyntheticMethodBinding binding, + int codeAttributeOffset, + int[] startLineIndexes) { + // reinitialize the contents with the byte modified by the code stream + this.contents = 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); + } + + if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) { + attributesNumber += generateStackMapAttribute( + binding, + code_length, + codeAttributeOffset, + max_locals, + false); + } + + // 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.aspectj.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); + } + + 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 += 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, startingContentsOffset); + if (this.contentsOffset == memberValuePairOffset) { + // ignore all annotation values +// this.contentsOffset = resetPosition; + this.contents[this.contentsOffset++]=0; + this.contents[this.contentsOffset++]=0; + break loop; + } + memberValuePairsCount++; + resetPosition = this.contentsOffset; + } catch(ClassCastException e) { + this.contentsOffset = resetPosition; + } catch(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) + int memberValuePairCount = 0; // will not get to 1 if there is a problem with the annotation value + int memberValuePairLengthPosition = this.contentsOffset; + this.contentsOffset+=2;// leave space to fill in the pair count later + if (this.contentsOffset + 2 >= this.contents.length) { + resizeContents(2); + } + final int elementNameIndex = this.constantPool.literalIndex(VALUE); + int resetPosition = this.contentsOffset; + this.contents[this.contentsOffset++] = (byte) (elementNameIndex >> 8); + this.contents[this.contentsOffset++] = (byte) elementNameIndex; + MethodBinding methodBinding = singleMemberAnnotation.memberValuePairs()[0].binding; + if (methodBinding == null) { + this.contentsOffset = resetPosition; + } else { + int memberValuePairOffset = this.contentsOffset; + try { + generateElementValue(singleMemberAnnotation.memberValue, methodBinding.returnType, memberValuePairOffset); + if (this.contentsOffset == memberValuePairOffset) { + // ignore annotation value + this.contentsOffset = resetPosition; + } else { + memberValuePairCount++; + resetPosition = this.contentsOffset; + } + } catch(ClassCastException e) { + this.contentsOffset = resetPosition; + } catch(ShouldNotImplement e) { + this.contentsOffset = resetPosition; + } + } + this.contents[memberValuePairLengthPosition++] = (byte) (memberValuePairCount >> 8); + this.contents[memberValuePairLengthPosition++] = (byte) memberValuePairCount; + } 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 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) || + ((LambdaExpression)functional).isSerializable) || + bridges != null) { + + LambdaExpression lambdaEx = (LambdaExpression)functional; + // 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 (lambdaEx.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 (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.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.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodBinding + * @return <CODE>int</CODE> + */ + public int generateMethodInfoAttributes(MethodBinding methodBinding, List extraAttributes) { // AspectJ: extra parameter + // 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); + } + // AspectJ Extension + if (extraAttributes != null) { + for (int i=0, len = extraAttributes.size(); i < len; i++) { + IAttribute attribute = (IAttribute)extraAttributes.get(i); + short nameIndex = (short)constantPool.literalIndex(attribute.getNameChars()); + writeToContents(attribute.getAllBytes(nameIndex,constantPool)); + attributesNumber++; + } + } + // End AspectJ Extension + return attributesNumber; + } + // AspectJ Extension - new method stub that can pass 3rd param + public int generateMethodInfoAttributes(MethodBinding methodBinding) { + return generateMethodInfoAttributes(methodBinding,(List)null); + } + // End AspectJ Extension + 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.aspectj.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.aspectj.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); + 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 + * 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() { + 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((TypeBinding) 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; + } + } + + /** + * @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; + // AspectJ Extension: this prevents a Type targeting annotation being stashed on a + // method representing an 'declare @type'. So don't enforce this restriction +// if (annotationMask != 0 && (annotationMask & targetMask) == 0) continue; + // AspectJ Extension: End + if (annotation.isRuntimeInvisible() || annotation.isRuntimeTypeInvisible()) { + invisibleAnnotationsCounter++; + } else if (annotation.isRuntimeVisible() || annotation.isRuntimeTypeVisible()) { + visibleAnnotationsCounter++; + } + } + + int annotationAttributeOffset = this.contentsOffset; + int constantPOffset = this.constantPool.currentOffset; + int constantPoolIndex = this.constantPool.currentIndex; + 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; + // AspectJ Extension: this prevents a Type targeting annotation being stashed on a + // method representing an 'declare @type'. So don't enforce this restriction +// if (annotationMask != 0 && (annotationMask & targetMask) == 0) continue; + // AspectJ Extension: end + 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; + // reset the constant pool to its state before the clinit + this.constantPool.resetForAttributeName(AttributeNamesConstants.RuntimeInvisibleAnnotationsName, constantPoolIndex, constantPOffset); + } + } + + annotationAttributeOffset = this.contentsOffset; + constantPOffset = this.constantPool.currentOffset; + constantPoolIndex = this.constantPool.currentIndex; + 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; + // AspectJ Extension: this prevents a Type targeting annotation being stashed on a + // method representing an 'declare @type'. So don't enforce this restriction +// if (annotationMask != 0 && (annotationMask & targetMask) == 0) continue; + // AspectJ Extension: end + 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; + this.constantPool.resetForAttributeName(AttributeNamesConstants.RuntimeVisibleAnnotationsName, constantPoolIndex, constantPOffset); + } + } + 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; + int constantPOffset = this.constantPool.currentOffset; + int constantPoolIndex = this.constantPool.currentIndex; + 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; + // reset the constant pool to its state before the clinit + this.constantPool.resetForAttributeName(AttributeNamesConstants.RuntimeInvisibleTypeAnnotationsName, constantPoolIndex, constantPOffset); + } + } + + annotationAttributeOffset = this.contentsOffset; + constantPOffset = this.constantPool.currentOffset; + constantPoolIndex = this.constantPool.currentIndex; + 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; + this.constantPool.resetForAttributeName(AttributeNamesConstants.RuntimeVisibleTypeAnnotationsName, constantPoolIndex, constantPOffset); + } + } + 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) { + + 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 (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; + } else if (binding.sourceLambda() != null) { // SyntheticMethodBinding, purpose : LambdaMethod. + arguments = binding.sourceLambda().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) { + 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); + 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) { + 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); + 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.referenceBinding.methods().length + this.referenceBinding.fields().length; + this.header = new byte[INITIAL_HEADER_SIZE]; + this.contents = new byte[members < 15 ? INITIAL_CONTENTS_SIZE : INITIAL_HEADER_SIZE]; + } + + public void initialize(SourceTypeBinding aType, ClassFile parentClassFile, boolean createProblemType) { + // 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); + + // 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; + } + this.enclosingClassFile = parentClassFile; + // innerclasses get their names computed at code gen time + + // now we continue to generate the bytes inside the contents array + this.contents[this.contentsOffset++] = (byte) (accessFlags >> 8); + this.contents[this.contentsOffset++] = (byte) accessFlags; + int classNameIndex = this.constantPool.literalIndexForType(aType); + this.contents[this.contentsOffset++] = (byte) (classNameIndex >> 8); + this.contents[this.contentsOffset++] = (byte) classNameIndex; + // AspectJ Extension - don't include result of decp weaving in the class created by compilation + /*old{ + 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; + } + } + }new:*/ + ReferenceBinding superclass = aType.superclass; + int superclassNameIndex; + if (aType.originalSuperclass!=null) { + superclass = aType.originalSuperclass; + } + if (aType.isInterface()) { + superclassNameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangObjectConstantPoolName); + } else { + if (superclass != null) { + if ((superclass.tagBits & TagBits.HasMissingType) != 0) { + superclassNameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangObjectConstantPoolName); + } else { + superclassNameIndex = this.constantPool.literalIndexForType(superclass); + } + } else { + superclassNameIndex = 0; + } + } + // AspectJ Extension end + this.contents[this.contentsOffset++] = (byte) (superclassNameIndex >> 8); + this.contents[this.contentsOffset++] = (byte) superclassNameIndex; + ReferenceBinding[] superInterfacesBinding = aType.superInterfaces(); + // AspectJ Extension - don't include result of decp weaving in the class created by compilaton + if (aType.originalSuperInterfaces!=null) { + superInterfacesBinding = aType.originalSuperInterfaces; + } + // AspectJ Extension end + 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; + } + + 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(), this.referenceBinding.scope.getJavaLangString(), 0, false); //$NON-NLS-1$ + localVariableBinding.resolvedPosition = resolvedPosition; + this.codeStream.record(localVariableBinding); + localVariableBinding.recordInitializationStartPC(0); + localVariableBinding.recordInitializationEndPC(codeLength); + + frame.putLocal(resolvedPosition, new VerificationTypeInfo( + TypeIds.T_JavaLangString, + ConstantPool.JavaLangStringConstantPoolName)); + resolvedPosition++; + + localVariableBinding = new LocalVariableBinding(" ordinal".toCharArray(), TypeBinding.INT, 0, false); //$NON-NLS-1$ + 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.aspectj.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) { + if (this.innerClassesBindings == null) { + this.innerClassesBindings = new HashSet(INNER_CLASSES_SIZE); + } + ReferenceBinding innerClass = (ReferenceBinding) binding; + this.innerClassesBindings.add(innerClass.erasure().unannotated(false)); // should not emit yet another inner class for Outer.@Inner Inner. + ReferenceBinding enclosingType = innerClass.enclosingType(); + while (enclosingType != null + && enclosingType.isNestedType()) { + this.innerClassesBindings.add(enclosingType.erasure().unannotated(false)); + enclosingType = enclosingType.enclosingType(); + } + } + + public int recordBootstrapMethod(FunctionalExpression expression) { + if (this.bootstrapMethods == null) { + this.bootstrapMethods = new ArrayList(); + } + this.bootstrapMethods.add(expression); + // Record which bootstrap method was assigned to the expression + expression.bootstrapMethodNumber = this.bootstrapMethods.size() - 1; + return this.bootstrapMethods.size() - 1; + } + + public void reset(SourceTypeBinding typeBinding) { + // the code stream is reinitialized for each method + final CompilerOptions options = typeBinding.scope.compilerOptions(); + this.referenceBinding = typeBinding; + this.isNestedType = typeBinding.isNestedType(); + 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 (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 new IllegalArgumentException(); + } + } + + /** + * 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(new Integer(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() { + public int compare(Object o1, Object o2) { + StackMapFrame frame = (StackMapFrame) o1; + StackMapFrame frame2 = (StackMapFrame) o2; + return frame.pc - frame2.pc; + } + }); + return result; + } + + public List traverse(MethodBinding methodBinding, int maxLocals, byte[] bytecodes, int codeOffset, int codeLength, Map frames, boolean isClinit) { + 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 stack depth markers + int indexInStackDepthMarkers = 0; + StackDepthMarker[] stackDepthMarkers = stackMapFrameCodeStream.getStackDepthMarkers(); + int stackDepthMarkersLength = stackDepthMarkers == null ? 0 : stackDepthMarkers.length; + boolean hasStackDepthMarkers = stackDepthMarkersLength != 0; + StackDepthMarker stackDepthMarker = null; + if (hasStackDepthMarkers) { + stackDepthMarker = stackDepthMarkers[0]; + } + + // set initial values for stack markers (used only in cldc mode) + int indexInStackMarkers = 0; + StackMarker[] stackMarkers = stackMapFrameCodeStream.getStackMarkers(); + int stackMarkersLength = stackMarkers == null ? 0 : stackMarkers.length; + boolean hasStackMarkers = stackMarkersLength != 0; + StackMarker stackMarker = null; + if (hasStackMarkers) { + stackMarker = stackMarkers[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()); + 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 (hasStackMarkers && stackMarker.pc == currentPC) { + VerificationTypeInfo[] infos = frame.stackItems; + VerificationTypeInfo[] tempInfos = new VerificationTypeInfo[frame.numberOfStackItems]; + System.arraycopy(infos, 0, tempInfos, 0, frame.numberOfStackItems); + stackMarker.setInfos(tempInfos); + } else if (hasStackMarkers && stackMarker.destinationPC == currentPC) { + VerificationTypeInfo[] infos = stackMarker.infos; + frame.stackItems = infos; + frame.numberOfStackItems = infos.length; + indexInStackMarkers++; + if (indexInStackMarkers < stackMarkersLength) { + stackMarker = stackMarkers[indexInStackMarkers]; + } else { + hasStackMarkers = false; + } + } + if (hasStackDepthMarkers && stackDepthMarker.pc == currentPC) { + TypeBinding typeBinding = stackDepthMarker.typeBinding; + if (typeBinding != null) { + if (stackDepthMarker.delta > 0) { + frame.addStackItem(new VerificationTypeInfo(typeBinding)); + } else { + frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(typeBinding); + } + } else { + frame.numberOfStackItems--; + } + indexInStackDepthMarkers++; + if (indexInStackDepthMarkers < stackDepthMarkersLength) { + stackDepthMarker = stackDepthMarkers[indexInStackDepthMarkers]; + } else { + hasStackDepthMarkers = false; + } + } + if (hasExceptionMarkers && exceptionMarker.pc == currentPC) { + frame.numberOfStackItems = 0; + frame.addStackItem(new VerificationTypeInfo(0, VerificationTypeInfo.ITEM_OBJECT, exceptionMarker.constantPoolName)); + 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 = frame.duplicate(); + currentFrame.pc = currentPC; + // initialize locals + initializeLocals(isClinit ? true : methodBinding.isStatic(), currentPC, currentFrame); + // insert a new frame + add(frames, currentFrame); + 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(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(TypeBinding.INT); + pc++; + break; + case Opcodes.OPC_lconst_0: + case Opcodes.OPC_lconst_1: + frame.addStackItem(TypeBinding.LONG); + pc++; + break; + case Opcodes.OPC_fconst_0: + case Opcodes.OPC_fconst_1: + case Opcodes.OPC_fconst_2: + frame.addStackItem(TypeBinding.FLOAT); + pc++; + break; + case Opcodes.OPC_dconst_0: + case Opcodes.OPC_dconst_1: + frame.addStackItem(TypeBinding.DOUBLE); + pc++; + break; + case Opcodes.OPC_bipush: + frame.addStackItem(TypeBinding.BYTE); + pc += 2; + break; + case Opcodes.OPC_sipush: + frame.addStackItem(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( + TypeIds.T_JavaLangString, + ConstantPool.JavaLangStringConstantPoolName)); + break; + case ClassFileConstants.IntegerTag: + frame.addStackItem(TypeBinding.INT); + break; + case ClassFileConstants.FloatTag: + frame.addStackItem(TypeBinding.FLOAT); + break; + case ClassFileConstants.ClassTag: + frame.addStackItem(new VerificationTypeInfo( + TypeIds.T_JavaLangClass, + ConstantPool.JavaLangClassConstantPoolName)); + } + 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( + TypeIds.T_JavaLangString, + ConstantPool.JavaLangStringConstantPoolName)); + break; + case ClassFileConstants.IntegerTag: + frame.addStackItem(TypeBinding.INT); + break; + case ClassFileConstants.FloatTag: + frame.addStackItem(TypeBinding.FLOAT); + break; + case ClassFileConstants.ClassTag: + frame.addStackItem(new VerificationTypeInfo( + TypeIds.T_JavaLangClass, + ConstantPool.JavaLangClassConstantPoolName)); + } + pc += 3; + break; + case Opcodes.OPC_ldc2_w: + index = u2At(bytecodes, 1, pc); + switch (u1At(poolContents, 0, constantPoolOffsets[index])) { + case ClassFileConstants.DoubleTag: + frame.addStackItem(TypeBinding.DOUBLE); + break; + case ClassFileConstants.LongTag: + frame.addStackItem(TypeBinding.LONG); + break; + } + pc += 3; + break; + case Opcodes.OPC_iload: + frame.addStackItem(TypeBinding.INT); + pc += 2; + break; + case Opcodes.OPC_lload: + frame.addStackItem(TypeBinding.LONG); + pc += 2; + break; + case Opcodes.OPC_fload: + frame.addStackItem(TypeBinding.FLOAT); + pc += 2; + break; + case Opcodes.OPC_dload: + frame.addStackItem(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(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(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(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(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(TypeBinding.INT); + pc++; + break; + case Opcodes.OPC_laload: + frame.numberOfStackItems -=2; + frame.addStackItem(TypeBinding.LONG); + pc++; + break; + case Opcodes.OPC_faload: + frame.numberOfStackItems -=2; + frame.addStackItem(TypeBinding.FLOAT); + pc++; + break; + case Opcodes.OPC_daload: + frame.numberOfStackItems -=2; + frame.addStackItem(TypeBinding.DOUBLE); + pc++; + break; + case Opcodes.OPC_aaload: + frame.numberOfStackItems--; + frame.replaceWithElementType(); + pc++; + break; + case Opcodes.OPC_baload: + frame.numberOfStackItems -=2; + frame.addStackItem(TypeBinding.BYTE); + pc++; + break; + case Opcodes.OPC_caload: + frame.numberOfStackItems -=2; + frame.addStackItem(TypeBinding.CHAR); + pc++; + break; + case Opcodes.OPC_saload: + frame.numberOfStackItems -=2; + frame.addStackItem(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(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--; + addRealJumpTarget(realJumpTarget, currentPC + i2At(bytecodes, 1, pc)); + 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; + addRealJumpTarget(realJumpTarget, currentPC + i2At(bytecodes, 1, pc)); + pc += 3; + break; + case Opcodes.OPC_goto: + addRealJumpTarget(realJumpTarget, currentPC + i2At(bytecodes, 1, pc)); + pc += 3; + addRealJumpTarget(realJumpTarget, pc - codeOffset); + break; + case Opcodes.OPC_tableswitch: + pc++; + while (((pc - codeOffset) & 0x03) != 0) { + pc++; + } + // default offset + addRealJumpTarget(realJumpTarget, currentPC + i4At(bytecodes, 0, pc)); + 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 + addRealJumpTarget(realJumpTarget, currentPC + i4At(bytecodes, 0, pc)); + pc += 4; + } + frame.numberOfStackItems--; + break; + case Opcodes.OPC_lookupswitch: + pc++; + while (((pc - codeOffset) & 0x03) != 0) { + pc++; + } + addRealJumpTarget(realJumpTarget, currentPC + i4At(bytecodes, 0, pc)); + 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 + addRealJumpTarget(realJumpTarget, currentPC + i4At(bytecodes, 0, pc)); + pc += 4; + } + frame.numberOfStackItems--; + 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])); + if (descriptor.length == 1) { + // base type + switch(descriptor[0]) { + case 'Z': + frame.addStackItem(TypeBinding.BOOLEAN); + break; + case 'B': + frame.addStackItem(TypeBinding.BYTE); + break; + case 'C': + frame.addStackItem(TypeBinding.CHAR); + break; + case 'D': + frame.addStackItem(TypeBinding.DOUBLE); + break; + case 'F': + frame.addStackItem(TypeBinding.FLOAT); + break; + case 'I': + frame.addStackItem(TypeBinding.INT); + break; + case 'J': + frame.addStackItem(TypeBinding.LONG); + break; + case 'S': + frame.addStackItem(TypeBinding.SHORT); + break; + } + } else if (descriptor[0] == '[') { + frame.addStackItem(new VerificationTypeInfo(0, descriptor)); + } else { + frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(descriptor, 1, descriptor.length - 1))); + } + 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--; + if (descriptor.length == 1) { + // base type + switch(descriptor[0]) { + case 'Z': + frame.addStackItem(TypeBinding.BOOLEAN); + break; + case 'B': + frame.addStackItem(TypeBinding.BYTE); + break; + case 'C': + frame.addStackItem(TypeBinding.CHAR); + break; + case 'D': + frame.addStackItem(TypeBinding.DOUBLE); + break; + case 'F': + frame.addStackItem(TypeBinding.FLOAT); + break; + case 'I': + frame.addStackItem(TypeBinding.INT); + break; + case 'J': + frame.addStackItem(TypeBinding.LONG); + break; + case 'S': + frame.addStackItem(TypeBinding.SHORT); + break; + } + } else if (descriptor[0] == '[') { + frame.addStackItem(new VerificationTypeInfo(0, descriptor)); + } else { + frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(descriptor, 1, descriptor.length - 1))); + } + 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); + if (returnType.length == 1) { + // base type + switch(returnType[0]) { + case 'Z': + frame.addStackItem(TypeBinding.BOOLEAN); + break; + case 'B': + frame.addStackItem(TypeBinding.BYTE); + break; + case 'C': + frame.addStackItem(TypeBinding.CHAR); + break; + case 'D': + frame.addStackItem(TypeBinding.DOUBLE); + break; + case 'F': + frame.addStackItem(TypeBinding.FLOAT); + break; + case 'I': + frame.addStackItem(TypeBinding.INT); + break; + case 'J': + frame.addStackItem(TypeBinding.LONG); + break; + case 'S': + frame.addStackItem(TypeBinding.SHORT); + break; + } + } else { + if (returnType[0] == '[') { + frame.addStackItem(new VerificationTypeInfo(0, returnType)); + } else { + frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(returnType, 1, returnType.length - 1))); + } + } + 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); + if (returnType.length == 1) { + // base type + switch(returnType[0]) { + case 'Z': + frame.addStackItem(TypeBinding.BOOLEAN); + break; + case 'B': + frame.addStackItem(TypeBinding.BYTE); + break; + case 'C': + frame.addStackItem(TypeBinding.CHAR); + break; + case 'D': + frame.addStackItem(TypeBinding.DOUBLE); + break; + case 'F': + frame.addStackItem(TypeBinding.FLOAT); + break; + case 'I': + frame.addStackItem(TypeBinding.INT); + break; + case 'J': + frame.addStackItem(TypeBinding.LONG); + break; + case 'S': + frame.addStackItem(TypeBinding.SHORT); + break; + } + } else { + if (returnType[0] == '[') { + frame.addStackItem(new VerificationTypeInfo(0, returnType)); + } else { + frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(returnType, 1, returnType.length - 1))); + } + } + 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); + if (returnType.length == 1) { + // base type + switch(returnType[0]) { + case 'Z': + frame.addStackItem(TypeBinding.BOOLEAN); + break; + case 'B': + frame.addStackItem(TypeBinding.BYTE); + break; + case 'C': + frame.addStackItem(TypeBinding.CHAR); + break; + case 'D': + frame.addStackItem(TypeBinding.DOUBLE); + break; + case 'F': + frame.addStackItem(TypeBinding.FLOAT); + break; + case 'I': + frame.addStackItem(TypeBinding.INT); + break; + case 'J': + frame.addStackItem(TypeBinding.LONG); + break; + case 'S': + frame.addStackItem(TypeBinding.SHORT); + break; + } + } else { + if (returnType[0] == '[') { + frame.addStackItem(new VerificationTypeInfo(0, returnType)); + } else { + frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(returnType, 1, returnType.length - 1))); + } + } + 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); + if (returnType.length == 1) { + // base type + switch(returnType[0]) { + case 'Z': + frame.addStackItem(TypeBinding.BOOLEAN); + break; + case 'B': + frame.addStackItem(TypeBinding.BYTE); + break; + case 'C': + frame.addStackItem(TypeBinding.CHAR); + break; + case 'D': + frame.addStackItem(TypeBinding.DOUBLE); + break; + case 'F': + frame.addStackItem(TypeBinding.FLOAT); + break; + case 'I': + frame.addStackItem(TypeBinding.INT); + break; + case 'J': + frame.addStackItem(TypeBinding.LONG); + break; + case 'S': + frame.addStackItem(TypeBinding.SHORT); + break; + } + } else { + if (returnType[0] == '[') { + frame.addStackItem(new VerificationTypeInfo(0, returnType)); + } else { + frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(returnType, 1, returnType.length - 1))); + } + } + 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); + if (returnType.length == 1) { + // base type + switch(returnType[0]) { + case 'Z': + frame.addStackItem(TypeBinding.BOOLEAN); + break; + case 'B': + frame.addStackItem(TypeBinding.BYTE); + break; + case 'C': + frame.addStackItem(TypeBinding.CHAR); + break; + case 'D': + frame.addStackItem(TypeBinding.DOUBLE); + break; + case 'F': + frame.addStackItem(TypeBinding.FLOAT); + break; + case 'I': + frame.addStackItem(TypeBinding.INT); + break; + case 'J': + frame.addStackItem(TypeBinding.LONG); + break; + case 'S': + frame.addStackItem(TypeBinding.SHORT); + break; + } + } else { + if (returnType[0] == '[') { + frame.addStackItem(new VerificationTypeInfo(0, returnType)); + } else { + frame.addStackItem(new VerificationTypeInfo(0, CharOperation.subarray(returnType, 1, returnType.length - 1))); + } + } + 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])); + VerificationTypeInfo verificationTypeInfo = new VerificationTypeInfo(0, VerificationTypeInfo.ITEM_UNINITIALIZED, className); + verificationTypeInfo.offset = currentPC; + frame.addStackItem(verificationTypeInfo); + pc += 3; + break; + case Opcodes.OPC_newarray: + char[] constantPoolName = null; + switch (u1At(bytecodes, 1, pc)) { + case ClassFileConstants.INT_ARRAY : + constantPoolName = new char[] { '[', 'I' }; + break; + case ClassFileConstants.BYTE_ARRAY : + constantPoolName = new char[] { '[', 'B' }; + break; + case ClassFileConstants.BOOLEAN_ARRAY : + constantPoolName = new char[] { '[', 'Z' }; + break; + case ClassFileConstants.SHORT_ARRAY : + constantPoolName = new char[] { '[', 'S' }; + break; + case ClassFileConstants.CHAR_ARRAY : + constantPoolName = new char[] { '[', 'C' }; + break; + case ClassFileConstants.LONG_ARRAY : + constantPoolName = new char[] { '[', 'J' }; + break; + case ClassFileConstants.FLOAT_ARRAY : + constantPoolName = new char[] { '[', 'F' }; + break; + case ClassFileConstants.DOUBLE_ARRAY : + constantPoolName = new char[] { '[', 'D' }; + break; + } + frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeIds.T_JavaLangObject, constantPoolName); + 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])); + int classNameLength = className.length; + if (className[0] != '[') { + // this is a type name (class or interface). So we add appropriate '[', 'L' and ';'. + System.arraycopy(className, 0, (constantPoolName = new char[classNameLength + 3]), 2, classNameLength); + constantPoolName[0] = '['; + constantPoolName[1] = 'L'; + constantPoolName[classNameLength + 2] = ';'; + } else { + // if class name is already an array, we just need to add one dimension + System.arraycopy(className, 0, (constantPoolName = new char[classNameLength + 1]), 1, classNameLength); + constantPoolName[0] = '['; + } + frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(0, constantPoolName); + 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])); + frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(0, className); + 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(TypeBinding.INT); + break; + case Opcodes.OPC_fload : + frame.addStackItem(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(TypeBinding.LONG); + break; + case Opcodes.OPC_dload : + frame.addStackItem(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; + classNameLength = className.length; + constantPoolName = new char[classNameLength + dimensions]; + for (int i = 0; i < dimensions; i++) { + constantPoolName[i] = '['; + } + System.arraycopy(className, 0, constantPoolName, dimensions, classNameLength); + frame.addStackItem(new VerificationTypeInfo(0, constantPoolName)); + pc += 4; + break; + case Opcodes.OPC_ifnull: + case Opcodes.OPC_ifnonnull: + frame.numberOfStackItems--; + addRealJumpTarget(realJumpTarget, currentPC + i2At(bytecodes, 1, pc)); + pc += 3; + break; + case Opcodes.OPC_goto_w: + addRealJumpTarget(realJumpTarget, currentPC + i4At(bytecodes, 1, pc)); + 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[] { + new Byte(opcode), + new Integer(pc), + new String(methodBinding.shortReadableName()), + }), + this.codeStream.methodDeclaration); + } else { + this.codeStream.lambdaExpression.scope.problemReporter().abortDueToInternalError( + Messages.bind( + Messages.abort_invalidOpcode, + new Object[] { + new Byte(opcode), + new Integer(pc), + new String(methodBinding.shortReadableName()), + }), + this.codeStream.lambdaExpression); + } + break; + } + if (pc >= (codeLength + codeOffset)) { + break; + } + } + return filterFakeFrames(realJumpTarget, frames, codeLength); + } + + private void addRealJumpTarget(Set realJumpTarget, int pc) { + realJumpTarget.add(new Integer(pc)); + } + private void add(Map frames, StackMapFrame frame) { + frames.put(new Integer(frame.pc), frame); + } + 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)); + } + // AspectJ Extension + void writeToContents(byte[] data) { + int N = data.length; + if (contentsOffset + N >= this.contents.length) { + resizeContents(N); + } + System.arraycopy(data,0,contents,contentsOffset,N); + contentsOffset += N; + } + // End AspectJ Extension + + 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; + } +} |