Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.eclipse.jdt.internal.compiler.ClassFile Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2000, 2023 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Jesper S Moller - Contributions for
* Bug 405066 - [1.8][compiler][codegen] Implement code generation infrastructure for JSR335
* Bug 406982 - [1.8][compiler] Generation of MethodParameters Attribute in classfile
* Bug 416885 - [1.8][compiler]IncompatibleClassChange error (edit)
* Bug 412149 - [1.8][compiler] Emit repeated annotations into the designated container
* Andy Clement (GoPivotal, Inc) [email protected] - Contributions for
* Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
* Bug 409236 - [1.8][compiler] Type annotations on intersection cast types dropped by code generator
* Bug 409246 - [1.8][compiler] Type annotations on catch parameters not handled properly
* Bug 415541 - [1.8][compiler] Type annotations in the body of static initializer get dropped
* Bug 415399 - [1.8][compiler] Type annotations on constructor results dropped by the code generator
* Bug 415470 - [1.8][compiler] Type annotations on class declaration go vanishing
* Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas
* Bug 434556 - Broken class file generated for incorrect annotation usage
* Bug 442416 - $deserializeLambda$ missing cases for nested lambdas
* Stephan Herrmann - Contribution for
* Bug 438458 - [1.8][null] clean up handling of null type annotations wrt type variables
* Olivier Tardieu [email protected] - Contributions for
* Bug 442416 - $deserializeLambda$ missing cases for nested lambdas
*******************************************************************************/
package org.eclipse.jdt.internal.compiler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.CaseStatement;
import org.eclipse.jdt.internal.compiler.ast.CaseStatement.ResolvedCase;
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ExportsStatement;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FunctionalExpression;
import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.OpensStatement;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.Receiver;
import org.eclipse.jdt.internal.compiler.ast.RecordComponent;
import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression;
import org.eclipse.jdt.internal.compiler.ast.RequiresStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.StringTemplate;
import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.AnnotationContext;
import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants;
import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.codegen.ConstantPool;
import org.eclipse.jdt.internal.compiler.codegen.ExceptionLabel;
import org.eclipse.jdt.internal.compiler.codegen.Opcodes;
import org.eclipse.jdt.internal.compiler.codegen.StackMapFrame;
import org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream;
import org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream.ExceptionMarker;
import org.eclipse.jdt.internal.compiler.codegen.TypeAnnotationCodeStream;
import org.eclipse.jdt.internal.compiler.codegen.VerificationTypeInfo;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.StringConstant;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
import org.eclipse.jdt.internal.compiler.lookup.PolymorphicMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.RecordComponentBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.jdt.internal.compiler.problem.AbortMethod;
import org.eclipse.jdt.internal.compiler.problem.AbortType;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.problem.ShouldNotImplement;
import org.eclipse.jdt.internal.compiler.util.Messages;
import org.eclipse.jdt.internal.compiler.util.Util;
/**
* Represents a class file wrapper on bytes, it is aware of its actual
* type name.
*
* Public APIs are listed below:
*
* byte[] getBytes();
* Answer the actual bytes of the class file
*
* char[][] getCompoundName();
* Answer the compound name of the class file.
* For example, {{java}, {util}, {Hashtable}}.
*
* byte[] getReducedBytes();
* Answer a smaller byte format, which is only contains some structural
* information. Those bytes are decodable with a regular class file reader,
* such as DietClassFileReader
*/
public class ClassFile implements TypeConstants, TypeIds {
private byte[] bytes;
public CodeStream codeStream;
public ConstantPool constantPool;
public int constantPoolOffset;
// the header contains all the bytes till the end of the constant pool
public byte[] contents;
public int contentsOffset;
protected boolean creatingProblemType;
public ClassFile enclosingClassFile;
public byte[] header;
// that collection contains all the remaining bytes of the .class file
public int headerOffset;
public Map innerClassesBindings;
public Set nestMembers;
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;
public static final int NESTED_MEMBER_SIZE = 5;
// TODO: Move these to an enum?
public static final String ALTMETAFACTORY_STRING = new String(ConstantPool.ALTMETAFACTORY);
public static final String METAFACTORY_STRING = new String(ConstantPool.METAFACTORY);
public static final String BOOTSTRAP_STRING = new String(ConstantPool.BOOTSTRAP);
public static final String TYPESWITCH_STRING = new String(ConstantPool.TYPESWITCH);
public static final String ENUMSWITCH_STRING = new String(ConstantPool.ENUMSWITCH);
public static final String CONCAT_CONSTANTS = new String(ConstantPool.ConcatWithConstants);
public static final String INVOKE_STRING = new String(ConstantPool.INVOKE_METHOD_METHOD_NAME);
public static final String ENUMDESC_OF = "EnumDesc.of"; //$NON-NLS-1$
public static final String CLASSDESC = "ClassDesc"; //$NON-NLS-1$
public static final String CLASSDESC_OF = "ClassDesc.of"; //$NON-NLS-1$
public static final String PROCESS_STRING = "process"; //$NON-NLS-1$
public static final String NEW_STRING_TEMPLATE = "newStringTemplate"; //$NON-NLS-1$
public static final String[] BOOTSTRAP_METHODS = { ALTMETAFACTORY_STRING, METAFACTORY_STRING, BOOTSTRAP_STRING,
TYPESWITCH_STRING, ENUMSWITCH_STRING, CONCAT_CONSTANTS, INVOKE_STRING, ENUMDESC_OF, CLASSDESC, CLASSDESC_OF, PROCESS_STRING, NEW_STRING_TEMPLATE};
/**
* INTERNAL USE-ONLY
* Request the creation of a ClassFile compatible representation of a problematic type
*
* @param typeDeclaration org.eclipse.jdt.internal.compiler.ast.TypeDeclaration
* @param unitResult org.eclipse.jdt.internal.compiler.CompilationUnitResult
*/
public static void createProblemType(TypeDeclaration typeDeclaration, CompilationResult unitResult) {
createProblemType(typeDeclaration, null, unitResult);
}
private static void createProblemType(TypeDeclaration typeDeclaration, ClassFile parentClassFile, CompilationResult unitResult) {
SourceTypeBinding typeBinding = typeDeclaration.binding;
ClassFile classFile = ClassFile.getNewInstance(typeBinding);
classFile.initialize(typeBinding, parentClassFile, true);
if (typeBinding.hasMemberTypes()) {
// see bug 180109
ReferenceBinding[] members = typeBinding.memberTypes;
for (int i = 0, l = members.length; i < l; i++)
classFile.recordInnerClasses(members[i]);
}
// TODO (olivier) handle cases where a field cannot be generated (name too long)
// TODO (olivier) handle too many methods
// inner attributes
if (typeBinding.isNestedType()) {
classFile.recordInnerClasses(typeBinding);
}
TypeVariableBinding[] typeVariables = typeBinding.typeVariables();
for (int i = 0, max = typeVariables.length; i < max; i++) {
TypeVariableBinding typeVariableBinding = typeVariables[i];
if ((typeVariableBinding.tagBits & TagBits.ContainsNestedTypeReferences) != 0) {
Util.recordNestedType(classFile, typeVariableBinding);
}
}
// add its fields
FieldBinding[] fields = typeBinding.fields();
if ((fields != null) && (fields != Binding.NO_FIELDS)) {
classFile.addFieldInfos();
} else {
// we have to set the number of fields to be equals to 0
if (classFile.contentsOffset + 2 >= classFile.contents.length) {
classFile.resizeContents(2);
}
classFile.contents[classFile.contentsOffset++] = 0;
classFile.contents[classFile.contentsOffset++] = 0;
}
// leave some space for the methodCount
classFile.setForMethodInfos();
// add its user defined methods
int problemsLength;
CategorizedProblem[] problems = unitResult.getErrors();
if (problems == null) {
problems = new CategorizedProblem[0];
}
CategorizedProblem[] problemsCopy = new CategorizedProblem[problemsLength = problems.length];
System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
AbstractMethodDeclaration[] methodDecls = typeDeclaration.methods;
boolean abstractMethodsOnly = false;
if (methodDecls != null) {
if (typeBinding.isInterface()) {
if (typeBinding.scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8)
abstractMethodsOnly = true;
// We generate a clinit which contains all the problems, since we may not be able to generate problem methods (< 1.8) and problem constructors (all levels).
classFile.addProblemClinit(problemsCopy);
}
for (int i = 0, length = methodDecls.length; i < length; i++) {
AbstractMethodDeclaration methodDecl = methodDecls[i];
MethodBinding method = methodDecl.binding;
if (method == null) continue;
if (abstractMethodsOnly) {
method.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract;
}
if (method.isConstructor()) {
if (typeBinding.isInterface()) continue;
classFile.addProblemConstructor(methodDecl, method, problemsCopy);
} else if (method.isAbstract()) {
classFile.addAbstractMethod(methodDecl, method);
} else {
classFile.addProblemMethod(methodDecl, method, problemsCopy);
}
}
// add abstract methods
classFile.addDefaultAbstractMethods();
}
// propagate generation of (problem) member types
if (typeDeclaration.memberTypes != null) {
for (int i = 0, max = typeDeclaration.memberTypes.length; i < max; i++) {
TypeDeclaration memberType = typeDeclaration.memberTypes[i];
if (memberType.binding != null) {
ClassFile.createProblemType(memberType, classFile, unitResult);
}
}
}
classFile.addAttributes();
unitResult.record(typeBinding.constantPoolName(), classFile);
}
public static ClassFile getNewInstance(SourceTypeBinding typeBinding) {
LookupEnvironment env = typeBinding.scope.environment();
return env.classFilePool.acquire(typeBinding);
}
/**
* INTERNAL USE-ONLY
* This methods creates a new instance of the receiver.
*/
protected ClassFile() {
// default constructor for subclasses
}
public ClassFile(SourceTypeBinding typeBinding) {
// default constructor for subclasses
this.constantPool = new ConstantPool(this);
final CompilerOptions options = typeBinding.scope.compilerOptions();
this.targetJDK = options.targetJDK;
this.produceAttributes = options.produceDebugAttributes;
this.referenceBinding = typeBinding;
this.isNestedType = typeBinding.isNestedType();
if (this.targetJDK >= ClassFileConstants.JDK1_6) {
this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP_TABLE;
if (this.targetJDK >= ClassFileConstants.JDK1_8) {
this.produceAttributes |= ClassFileConstants.ATTR_TYPE_ANNOTATION;
this.codeStream = new TypeAnnotationCodeStream(this);
if (options.produceMethodParameters) {
this.produceAttributes |= ClassFileConstants.ATTR_METHOD_PARAMETERS;
}
} else {
this.codeStream = new StackMapFrameCodeStream(this);
}
} else if (this.targetJDK == ClassFileConstants.CLDC_1_1) {
this.targetJDK = ClassFileConstants.JDK1_1; // put back 45.3
this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP;
this.codeStream = new StackMapFrameCodeStream(this);
} else {
this.codeStream = new CodeStream(this);
}
initByteArrays(this.referenceBinding.methods().length + this.referenceBinding.fields().length);
}
public ClassFile(ModuleBinding moduleBinding, CompilerOptions options) {
this.constantPool = new ConstantPool(this);
this.targetJDK = options.targetJDK;
this.produceAttributes = ClassFileConstants.ATTR_SOURCE;
this.isNestedType = false;
this.codeStream = new StackMapFrameCodeStream(this);
initByteArrays(0);
}
/**
* INTERNAL USE-ONLY
* Generate the byte for a problem method info that correspond to a bogus method.
*
* @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding
*/
public void addAbstractMethod(
AbstractMethodDeclaration method,
MethodBinding methodBinding) {
this.generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
int attributeNumber = this.generateMethodInfoAttributes(methodBinding);
completeMethodInfo(methodBinding, methodAttributeOffset, attributeNumber);
}
/**
* INTERNAL USE-ONLY
* This methods generate all the attributes for the receiver.
* For a class they could be:
* - source file attribute
* - inner classes attribute
* - deprecated attribute
*/
public void addAttributes() {
// update the method count
this.contents[this.methodCountOffset++] = (byte) (this.methodCount >> 8);
this.contents[this.methodCountOffset] = (byte) this.methodCount;
int attributesNumber = 0;
// leave two bytes for the number of attributes and store the current offset
int attributeOffset = this.contentsOffset;
this.contentsOffset += 2;
// source attribute
if ((this.produceAttributes & ClassFileConstants.ATTR_SOURCE) != 0) {
String fullFileName =
new String(this.referenceBinding.scope.referenceCompilationUnit().getFileName());
fullFileName = fullFileName.replace('\\', '/');
int lastIndex = fullFileName.lastIndexOf('/');
if (lastIndex != -1) {
fullFileName = fullFileName.substring(lastIndex + 1, fullFileName.length());
}
attributesNumber += generateSourceAttribute(fullFileName);
}
// Deprecated attribute
if (this.referenceBinding.isDeprecated()) {
// check that there is enough space to write all the bytes for the field info corresponding
// to the @fieldBinding
attributesNumber += generateDeprecatedAttribute();
}
// add signature attribute
char[] genericSignature = this.referenceBinding.genericSignature();
if (genericSignature != null) {
attributesNumber += generateSignatureAttribute(genericSignature);
}
if (this.targetJDK >= ClassFileConstants.JDK1_5
&& this.referenceBinding.isNestedType()
&& !this.referenceBinding.isMemberType()) {
// add enclosing method attribute (1.5 mode only)
attributesNumber += generateEnclosingMethodAttribute();
}
if (this.targetJDK >= ClassFileConstants.JDK1_4) {
TypeDeclaration typeDeclaration = this.referenceBinding.scope.referenceContext;
if (typeDeclaration != null) {
final Annotation[] annotations = typeDeclaration.annotations;
if (annotations != null) {
long targetMask;
if (typeDeclaration.isPackageInfo())
targetMask = TagBits.AnnotationForPackage;
else if (this.referenceBinding.isAnnotationType())
targetMask = TagBits.AnnotationForType | TagBits.AnnotationForAnnotationType;
else
targetMask = TagBits.AnnotationForType | TagBits.AnnotationForTypeUse; // 9.7.4 ... applicable to type declarations or in type contexts
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, lambda bootstrap methods and record bootstrap methods
if (this.bootstrapMethods != null && !this.bootstrapMethods.isEmpty()) {
attributesNumber += generateBootstrapMethods(this.bootstrapMethods);
}
if (this.targetJDK >= ClassFileConstants.JDK17) {
// add record attributes
attributesNumber += generatePermittedTypeAttributes();
}
// Inner class attribute
int numberOfInnerClasses = this.innerClassesBindings == null ? 0 : this.innerClassesBindings.size();
if (numberOfInnerClasses != 0) {
ReferenceBinding[] innerClasses = new ReferenceBinding[numberOfInnerClasses];
this.innerClassesBindings.keySet().toArray(innerClasses);
Arrays.sort(innerClasses, new Comparator() {
@Override
public int compare(ReferenceBinding o1, ReferenceBinding o2) {
Boolean onBottom1 = ClassFile.this.innerClassesBindings.get(o1);
Boolean onBottom2 = ClassFile.this.innerClassesBindings.get(o2);
if (onBottom1) {
if (!onBottom2) {
return 1;
}
} else {
if (onBottom2) {
return -1;
}
}
return CharOperation.compareTo(o1.constantPoolName(), o2.constantPoolName());
}
});
attributesNumber += generateInnerClassAttribute(numberOfInnerClasses, innerClasses);
}
if (this.missingTypes != null) {
generateMissingTypesAttribute();
attributesNumber++;
}
attributesNumber += generateTypeAnnotationAttributeForTypeDeclaration();
if (this.targetJDK >= ClassFileConstants.JDK11) {
// add nestMember and nestHost attributes
attributesNumber += generateNestAttributes();
}
if (this.targetJDK >= ClassFileConstants.JDK14) {
// add record attributes
attributesNumber += generateRecordAttributes();
}
// update the number of attributes
if (attributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[attributeOffset++] = (byte) (attributesNumber >> 8);
this.contents[attributeOffset] = (byte) attributesNumber;
// resynchronize all offsets of the classfile
this.header = this.constantPool.poolContent;
this.headerOffset = this.constantPool.currentOffset;
int constantPoolCount = this.constantPool.currentIndex;
this.header[this.constantPoolOffset++] = (byte) (constantPoolCount >> 8);
this.header[this.constantPoolOffset] = (byte) constantPoolCount;
}
/**
* INTERNAL USE-ONLY
* This methods generate all the module attributes for the receiver.
*/
public void addModuleAttributes(ModuleBinding module, Annotation[] annotations, CompilationUnitDeclaration cud) {
int attributesNumber = 0;
// leave two bytes for the number of attributes and store the current offset
int attributeOffset = this.contentsOffset;
this.contentsOffset += 2;
// source attribute
if ((this.produceAttributes & ClassFileConstants.ATTR_SOURCE) != 0) {
String fullFileName =
new String(cud.getFileName());
fullFileName = fullFileName.replace('\\', '/');
int lastIndex = fullFileName.lastIndexOf('/');
if (lastIndex != -1) {
fullFileName = fullFileName.substring(lastIndex + 1, fullFileName.length());
}
attributesNumber += generateSourceAttribute(fullFileName);
}
attributesNumber += generateModuleAttribute(cud.moduleDeclaration);
if (annotations != null) {
long targetMask = TagBits.AnnotationForModule;
attributesNumber += generateRuntimeAnnotations(annotations, targetMask);
}
char[] mainClass = cud.moduleDeclaration.binding.mainClassName;
if (mainClass != null) {
attributesNumber += generateModuleMainClassAttribute(CharOperation.replaceOnCopy(mainClass, '.', '/'));
}
char[][] packageNames = cud.moduleDeclaration.binding.getPackageNamesForClassFile();
if (packageNames != null) {
attributesNumber += generateModulePackagesAttribute(packageNames);
}
// update the number of attributes
if (attributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[attributeOffset++] = (byte) (attributesNumber >> 8);
this.contents[attributeOffset] = (byte) attributesNumber;
// resynchronize all offsets of the classfile
this.header = this.constantPool.poolContent;
this.headerOffset = this.constantPool.currentOffset;
int constantPoolCount = this.constantPool.currentIndex;
this.header[this.constantPoolOffset++] = (byte) (constantPoolCount >> 8);
this.header[this.constantPoolOffset] = (byte) constantPoolCount;
}
/**
* INTERNAL USE-ONLY
* This methods generate all the default abstract method infos that correpond to
* the abstract methods inherited from superinterfaces.
*/
public void addDefaultAbstractMethods() { // default abstract methods
MethodBinding[] defaultAbstractMethods =
this.referenceBinding.getDefaultAbstractMethods();
for (int i = 0, max = defaultAbstractMethods.length; i < max; i++) {
MethodBinding methodBinding = defaultAbstractMethods[i];
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
int attributeNumber = generateMethodInfoAttributes(methodBinding);
completeMethodInfo(methodBinding, methodAttributeOffset, attributeNumber);
}
}
private int addFieldAttributes(FieldBinding fieldBinding, int fieldAttributeOffset) {
int attributesNumber = 0;
// 4.7.2 only static constant fields get a ConstantAttribute
// Generate the constantValueAttribute
Constant fieldConstant = fieldBinding.constant();
if (fieldConstant != Constant.NotAConstant){
attributesNumber += generateConstantValueAttribute(fieldConstant, fieldBinding, fieldAttributeOffset);
}
if (this.targetJDK < ClassFileConstants.JDK1_5 && fieldBinding.isSynthetic()) {
attributesNumber += generateSyntheticAttribute();
}
if (fieldBinding.isDeprecated()) {
attributesNumber += generateDeprecatedAttribute();
}
// add signature attribute
char[] genericSignature = fieldBinding.genericSignature();
if (genericSignature != null) {
attributesNumber += generateSignatureAttribute(genericSignature);
}
if (this.targetJDK >= ClassFileConstants.JDK1_4) {
FieldDeclaration fieldDeclaration = fieldBinding.sourceField();
if (fieldDeclaration != null) {
try {
if (fieldDeclaration.isARecordComponent) {
long rcMask = TagBits.AnnotationForField | TagBits.AnnotationForTypeUse;
RecordComponent comp = getRecordComponent(fieldBinding.declaringClass, fieldBinding.name);
if (comp != null)
fieldDeclaration.annotations = ASTNode.getRelevantAnnotations(comp.annotations, rcMask, 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);
}
TypeReference fieldType = fieldDeclaration.type;
if (fieldType != null && ((fieldType.bits & ASTNode.HasTypeAnnotations) != 0)) {
fieldType.getAllAnnotationContexts(AnnotationTargetTypeConstants.FIELD, allTypeAnnotationContexts);
}
int size = allTypeAnnotationContexts.size();
attributesNumber = completeRuntimeTypeAnnotations(attributesNumber,
null,
node -> size > 0,
() -> allTypeAnnotationContexts);
}
} finally {
if (fieldDeclaration.isARecordComponent) {
fieldDeclaration.annotations = null;
}
}
}
}
if ((fieldBinding.tagBits & TagBits.HasMissingType) != 0) {
this.missingTypes = fieldBinding.type.collectMissingTypes(this.missingTypes);
}
return attributesNumber;
}
private RecordComponent getRecordComponent(ReferenceBinding declaringClass, char[] name) {
if (declaringClass instanceof SourceTypeBinding) {
SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) declaringClass;
RecordComponentBinding rcb = sourceTypeBinding.getRecordComponent(name);
if (rcb != null) {
RecordComponent recordComponent = rcb.sourceRecordComponent();
return recordComponent;
}
}
return null;
}
private int addComponentAttributes(RecordComponentBinding recordComponentBinding, int componetAttributeOffset) {
// See JVMS 14 Table 4.7-C - Record Preview for allowed attributes
int attributesNumber = 0;
// add signature attribute
char[] genericSignature = recordComponentBinding.genericSignature();
if (genericSignature != null) {
attributesNumber += generateSignatureAttribute(genericSignature);
}
RecordComponent recordComponent = recordComponentBinding.sourceRecordComponent();
if (recordComponent != null) {
Annotation[] annotations = recordComponent.annotations;
if (annotations != null) {
attributesNumber += generateRuntimeAnnotations(annotations, TagBits.AnnotationForRecordComponent);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) {
List allTypeAnnotationContexts = new ArrayList<>();
if (annotations != null && (recordComponent.bits & ASTNode.HasTypeAnnotations) != 0) {
recordComponent.getAllAnnotationContexts(AnnotationTargetTypeConstants.RECORD_COMPONENT, allTypeAnnotationContexts);
}
TypeReference recordComponentType = recordComponent.type;
if (recordComponentType != null && ((recordComponentType.bits & ASTNode.HasTypeAnnotations) != 0)) {
recordComponentType.getAllAnnotationContexts(AnnotationTargetTypeConstants.RECORD_COMPONENT, allTypeAnnotationContexts);
}
int size = allTypeAnnotationContexts.size();
attributesNumber = completeRuntimeTypeAnnotations(attributesNumber,
null,
node -> size > 0,
() -> allTypeAnnotationContexts);
}
}
if ((recordComponentBinding.tagBits & TagBits.HasMissingType) != 0) {
this.missingTypes = recordComponentBinding.type.collectMissingTypes(this.missingTypes);
}
return attributesNumber;
}
private void addComponentInfo(RecordComponentBinding recordComponentBinding) {
// check that there is enough space to write all the bytes for the field info corresponding
// to the @fieldBinding sans accessflags for component
/* record_component_info {
* u2 name_index;
* u2 descriptor_index;
* u2 attributes_count;
* attribute_info attributes[attributes_count];
} */
if (this.contentsOffset + 6 >= this.contents.length) {
resizeContents(6);
}
// Now we can generate all entries into the byte array
int nameIndex = this.constantPool.literalIndex(recordComponentBinding.name);
this.contents[this.contentsOffset++] = (byte) (nameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) nameIndex;
// Then the descriptorIndex
int descriptorIndex = this.constantPool.literalIndex(recordComponentBinding.type);
this.contents[this.contentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[this.contentsOffset++] = (byte) descriptorIndex;
int componentAttributeOffset = this.contentsOffset;
int attributeNumber = 0;
// leave some space for the number of attributes
this.contentsOffset += 2;
attributeNumber += addComponentAttributes(recordComponentBinding, componentAttributeOffset);
if (this.contentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[componentAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[componentAttributeOffset] = (byte) attributeNumber;
}
/**
* INTERNAL USE-ONLY
* This methods generates the bytes for the given field binding
* @param fieldBinding the given field binding
*/
private void addFieldInfo(FieldBinding fieldBinding) {
// check that there is enough space to write all the bytes for the field info corresponding
// to the @fieldBinding
if (this.contentsOffset + 8 >= this.contents.length) {
resizeContents(8);
}
// Now we can generate all entries into the byte array
// First the accessFlags
int accessFlags = fieldBinding.getAccessFlags();
if (this.targetJDK < ClassFileConstants.JDK1_5) {
// pre 1.5, synthetic was an attribute, not a modifier
accessFlags &= ~ClassFileConstants.AccSynthetic;
}
this.contents[this.contentsOffset++] = (byte) (accessFlags >> 8);
this.contents[this.contentsOffset++] = (byte) accessFlags;
// Then the nameIndex
int nameIndex = this.constantPool.literalIndex(fieldBinding.name);
this.contents[this.contentsOffset++] = (byte) (nameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) nameIndex;
// Then the descriptorIndex
int descriptorIndex = this.constantPool.literalIndex(fieldBinding.type);
this.contents[this.contentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[this.contentsOffset++] = (byte) descriptorIndex;
int fieldAttributeOffset = this.contentsOffset;
int attributeNumber = 0;
// leave some space for the number of attributes
this.contentsOffset += 2;
attributeNumber += addFieldAttributes(fieldBinding, fieldAttributeOffset);
if (this.contentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[fieldAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[fieldAttributeOffset] = (byte) attributeNumber;
}
/**
* INTERNAL USE-ONLY
* This methods generate all the fields infos for the receiver.
* This includes:
* - a field info for each defined field of that class
* - a field info for each synthetic field (e.g. this$0)
*/
/**
* INTERNAL USE-ONLY
* This methods generate all the fields infos for the receiver.
* This includes:
* - a field info for each defined field of that class
* - a field info for each synthetic field (e.g. this$0)
*/
public void addFieldInfos() {
SourceTypeBinding currentBinding = this.referenceBinding;
FieldBinding[] syntheticFields = currentBinding.syntheticFields();
int fieldCount = currentBinding.fieldCount() + (syntheticFields == null ? 0 : syntheticFields.length);
// write the number of fields
if (fieldCount > 0xFFFF) {
this.referenceBinding.scope.problemReporter().tooManyFields(this.referenceBinding.scope.referenceType());
}
if (this.contentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[this.contentsOffset++] = (byte) (fieldCount >> 8);
this.contents[this.contentsOffset++] = (byte) fieldCount;
FieldDeclaration[] fieldDecls = currentBinding.scope.referenceContext.fields;
for (int i = 0, max = fieldDecls == null ? 0 : fieldDecls.length; i < max; i++) {
FieldDeclaration fieldDecl = fieldDecls[i];
if (fieldDecl.binding != null) {
addFieldInfo(fieldDecl.binding);
}
}
if (syntheticFields != null) {
for (int i = 0, max = syntheticFields.length; i < max; i++) {
addFieldInfo(syntheticFields[i]);
}
}
}
private void addMissingAbstractProblemMethod(MethodDeclaration methodDeclaration, MethodBinding methodBinding, CategorizedProblem problem, CompilationResult compilationResult) {
// always clear the strictfp/native/abstract bit for a problem method
generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(ClassFileConstants.AccStrictfp | ClassFileConstants.AccNative | ClassFileConstants.AccAbstract));
int methodAttributeOffset = this.contentsOffset;
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
attributeNumber++;
int codeAttributeOffset = this.contentsOffset;
generateCodeAttributeHeader();
StringBuilder buffer = new StringBuilder(25);
buffer.append("\t" + problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$
buffer.insert(0, Messages.compilation_unresolvedProblem);
String problemString = buffer.toString();
this.codeStream.init(this);
this.codeStream.preserveUnusedLocals = true;
this.codeStream.initializeMaxLocals(methodBinding);
// return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "")
this.codeStream.generateCodeAttributeForProblemMethod(problemString);
completeCodeAttributeForMissingAbstractProblemMethod(
methodBinding,
codeAttributeOffset,
compilationResult.getLineSeparatorPositions(),
problem.getSourceLineNumber());
completeMethodInfo(methodBinding, methodAttributeOffset, attributeNumber);
}
/**
* INTERNAL USE-ONLY
* Generate the byte for a problem clinit method info that correspond to a boggus method.
*
* @param problems org.eclipse.jdt.internal.compiler.problem.Problem[]
*/
public void addProblemClinit(CategorizedProblem[] problems) {
generateMethodInfoHeaderForClinit();
// leave two spaces for the number of attributes
this.contentsOffset -= 2;
int attributeOffset = this.contentsOffset;
this.contentsOffset += 2;
int attributeNumber = 0;
int codeAttributeOffset = this.contentsOffset;
generateCodeAttributeHeader();
this.codeStream.resetForProblemClinit(this);
String problemString = "" ; //$NON-NLS-1$
int problemLine = 0;
if (problems != null) {
int max = problems.length;
StringBuilder buffer = new StringBuilder(25);
int count = 0;
for (int i = 0; i < max; i++) {
CategorizedProblem problem = problems[i];
if ((problem != null) && (problem.isError())) {
buffer.append("\t" +problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$
count++;
if (problemLine == 0) {
problemLine = problem.getSourceLineNumber();
}
problems[i] = null;
}
} // insert the top line afterwards, once knowing how many problems we have to consider
if (count > 1) {
buffer.insert(0, Messages.compilation_unresolvedProblems);
} else {
buffer.insert(0, Messages.compilation_unresolvedProblem);
}
problemString = buffer.toString();
}
// return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "")
this.codeStream.generateCodeAttributeForProblemMethod(problemString);
attributeNumber++; // code attribute
completeCodeAttributeForClinit(
codeAttributeOffset,
problemLine,
null);
if (this.contentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[attributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[attributeOffset] = (byte) attributeNumber;
}
/**
* INTERNAL USE-ONLY
* Generate the byte for a problem method info that correspond to a boggus constructor.
*
* @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding
* @param problems org.eclipse.jdt.internal.compiler.problem.Problem[]
*/
public void addProblemConstructor(
AbstractMethodDeclaration method,
MethodBinding methodBinding,
CategorizedProblem[] problems) {
if (methodBinding.declaringClass.isInterface()) {
method.abort(ProblemSeverities.AbortType, null);
}
// always clear the strictfp/native/abstract bit for a problem method
generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(ClassFileConstants.AccStrictfp | ClassFileConstants.AccNative | ClassFileConstants.AccAbstract));
int methodAttributeOffset = this.contentsOffset;
int attributesNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
attributesNumber++;
int codeAttributeOffset = this.contentsOffset;
generateCodeAttributeHeader();
this.codeStream.reset(method, this);
String problemString = "" ; //$NON-NLS-1$
int problemLine = 0;
if (problems != null) {
int max = problems.length;
StringBuilder buffer = new StringBuilder(25);
int count = 0;
for (int i = 0; i < max; i++) {
CategorizedProblem problem = problems[i];
if ((problem != null) && (problem.isError())) {
buffer.append("\t" +problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$
count++;
if (problemLine == 0) {
problemLine = problem.getSourceLineNumber();
}
}
} // insert the top line afterwards, once knowing how many problems we have to consider
if (count > 1) {
buffer.insert(0, Messages.compilation_unresolvedProblems);
} else {
buffer.insert(0, Messages.compilation_unresolvedProblem);
}
problemString = buffer.toString();
}
// return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "")
this.codeStream.generateCodeAttributeForProblemMethod(problemString);
completeCodeAttributeForProblemMethod(
method,
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions(),
problemLine);
completeMethodInfo(methodBinding, methodAttributeOffset, attributesNumber);
}
/**
* INTERNAL USE-ONLY
* Generate the byte for a problem method info that correspond to a boggus constructor.
* Reset the position inside the contents byte array to the savedOffset.
*
* @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding
* @param problems org.eclipse.jdt.internal.compiler.problem.Problem[]
* @param savedOffset int
*/
public void addProblemConstructor(
AbstractMethodDeclaration method,
MethodBinding methodBinding,
CategorizedProblem[] problems,
int savedOffset) {
// we need to move back the contentsOffset to the value at the beginning of the method
this.contentsOffset = savedOffset;
this.methodCount--; // we need to remove the method that causes the problem
addProblemConstructor(method, methodBinding, problems);
}
/**
* INTERNAL USE-ONLY
* Generate the byte for a problem method info that correspond to a boggus method.
*
* @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding
* @param problems org.eclipse.jdt.internal.compiler.problem.Problem[]
*/
public void addProblemMethod(
AbstractMethodDeclaration method,
MethodBinding methodBinding,
CategorizedProblem[] problems) {
if (methodBinding.isAbstract() && methodBinding.declaringClass.isInterface()) {
method.abort(ProblemSeverities.AbortType, null);
}
// always clear the strictfp/native/abstract bit for a problem method
generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(ClassFileConstants.AccStrictfp | ClassFileConstants.AccNative | ClassFileConstants.AccAbstract));
int methodAttributeOffset = this.contentsOffset;
int attributesNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
attributesNumber++;
int codeAttributeOffset = this.contentsOffset;
generateCodeAttributeHeader();
this.codeStream.reset(method, this);
String problemString = "" ; //$NON-NLS-1$
int problemLine = 0;
if (problems != null) {
int max = problems.length;
StringBuilder buffer = new StringBuilder(25);
int count = 0;
for (int i = 0; i < max; i++) {
CategorizedProblem problem = problems[i];
if ((problem != null)
&& (problem.isError())
&& (problem.getSourceStart() >= method.declarationSourceStart)
&& (problem.getSourceEnd() <= method.declarationSourceEnd)) {
buffer.append("\t" +problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$
count++;
if (problemLine == 0) {
problemLine = problem.getSourceLineNumber();
}
problems[i] = null;
}
} // insert the top line afterwards, once knowing how many problems we have to consider
if (count > 1) {
buffer.insert(0, Messages.compilation_unresolvedProblems);
} else {
buffer.insert(0, Messages.compilation_unresolvedProblem);
}
problemString = buffer.toString();
}
// return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "")
this.codeStream.generateCodeAttributeForProblemMethod(problemString);
completeCodeAttributeForProblemMethod(
method,
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions(),
problemLine);
completeMethodInfo(methodBinding, methodAttributeOffset, attributesNumber);
}
/**
* INTERNAL USE-ONLY
* Generate the byte for a problem method info that correspond to a boggus method.
* Reset the position inside the contents byte array to the savedOffset.
*
* @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding
* @param problems org.eclipse.jdt.internal.compiler.problem.Problem[]
* @param savedOffset int
*/
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(TypeDeclaration typeDecl) {
// add all methods (default abstract methods and synthetic)
// default abstract methods
generateMissingAbstractMethods(this.referenceBinding.scope.referenceType().missingAbstractMethods, this.referenceBinding.scope.referenceCompilationUnit().compilationResult);
MethodBinding[] defaultAbstractMethods = this.referenceBinding.getDefaultAbstractMethods();
for (int i = 0, max = defaultAbstractMethods.length; i < max; i++) {
MethodBinding methodBinding = defaultAbstractMethods[i];
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
int attributeNumber = generateMethodInfoAttributes(methodBinding);
completeMethodInfo(methodBinding, methodAttributeOffset, attributeNumber);
}
// add synthetic methods infos
int emittedSyntheticsCount = 0;
SyntheticMethodBinding deserializeLambdaMethod = null;
boolean continueScanningSynthetics = true;
while (continueScanningSynthetics) {
continueScanningSynthetics = false;
SyntheticMethodBinding[] syntheticMethods = this.referenceBinding.syntheticMethods();
int currentSyntheticsCount = syntheticMethods == null ? 0: syntheticMethods.length;
if (emittedSyntheticsCount != currentSyntheticsCount) {
for (int i = emittedSyntheticsCount, max = currentSyntheticsCount; i < max; i++) {
SyntheticMethodBinding syntheticMethod = syntheticMethods[i];
switch (syntheticMethod.purpose) {
case SyntheticMethodBinding.FieldReadAccess :
case SyntheticMethodBinding.SuperFieldReadAccess :
// generate a method info to emulate an reading access to
// a non-accessible field
addSyntheticFieldReadAccessMethod(syntheticMethod);
break;
case SyntheticMethodBinding.FieldWriteAccess :
case SyntheticMethodBinding.SuperFieldWriteAccess :
// generate a method info to emulate an writing access to
// a non-accessible field
addSyntheticFieldWriteAccessMethod(syntheticMethod);
break;
case SyntheticMethodBinding.MethodAccess :
case SyntheticMethodBinding.SuperMethodAccess :
case SyntheticMethodBinding.BridgeMethod :
// generate a method info to emulate an access to a non-accessible method / super-method or bridge method
addSyntheticMethodAccessMethod(syntheticMethod);
break;
case SyntheticMethodBinding.ConstructorAccess :
// generate a method info to emulate an access to a non-accessible constructor
addSyntheticConstructorAccessMethod(syntheticMethod);
break;
case SyntheticMethodBinding.EnumValues :
// generate a method info to define #values()
addSyntheticEnumValuesMethod(syntheticMethod);
break;
case SyntheticMethodBinding.EnumValueOf :
// generate a method info to define #valueOf(String)
addSyntheticEnumValueOfMethod(syntheticMethod);
break;
case SyntheticMethodBinding.SwitchTable :
// generate a method info to define the switch table synthetic method
addSyntheticSwitchTable(syntheticMethod);
break;
case SyntheticMethodBinding.TooManyEnumsConstants :
addSyntheticEnumInitializationMethod(syntheticMethod);
break;
case SyntheticMethodBinding.LambdaMethod:
syntheticMethod.lambda.generateCode(this.referenceBinding.scope, this);
continueScanningSynthetics = true; // lambda code generation could schedule additional nested lambdas for code generation.
break;
case SyntheticMethodBinding.ArrayConstructor:
addSyntheticArrayConstructor(syntheticMethod);
break;
case SyntheticMethodBinding.ArrayClone:
addSyntheticArrayClone(syntheticMethod);
break;
case SyntheticMethodBinding.FactoryMethod:
addSyntheticFactoryMethod(syntheticMethod);
break;
case SyntheticMethodBinding.DeserializeLambda:
deserializeLambdaMethod = syntheticMethod; // delay processing
break;
case SyntheticMethodBinding.SerializableMethodReference:
// Nothing to be done
break;
case SyntheticMethodBinding.RecordCanonicalConstructor:
addSyntheticRecordCanonicalConstructor(typeDecl, syntheticMethod);
break;
case SyntheticMethodBinding.RecordOverrideEquals:
case SyntheticMethodBinding.RecordOverrideHashCode:
case SyntheticMethodBinding.RecordOverrideToString:
addSyntheticRecordOverrideMethods(typeDecl, syntheticMethod, syntheticMethod.purpose);
break;
}
}
emittedSyntheticsCount = currentSyntheticsCount;
}
}
if (deserializeLambdaMethod != null) {
int problemResetPC = 0;
this.codeStream.wideMode = false;
boolean restart = false;
do {
try {
problemResetPC = this.contentsOffset;
addSyntheticDeserializeLambda(deserializeLambdaMethod,this.referenceBinding.syntheticMethods());
restart = false;
} catch (AbortMethod e) {
// Restart code generation if possible ...
if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
// a branch target required a goto_w, restart code generation in wide mode.
this.contentsOffset = problemResetPC;
this.methodCount--;
this.codeStream.resetInWideMode(); // request wide mode
restart = true;
} else {
throw new AbortType(this.referenceBinding.scope.referenceContext.compilationResult, e.problem);
}
}
} while (restart);
}
}
private void addSyntheticRecordCanonicalConstructor(TypeDeclaration typeDecl, 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.generateSyntheticBodyForRecordCanonicalConstructor(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;
}
private void addSyntheticRecordOverrideMethods(TypeDeclaration typeDecl, SyntheticMethodBinding methodBinding, int purpose) {
if (this.bootstrapMethods == null)
this.bootstrapMethods = new ArrayList<>(3);
if (!this.bootstrapMethods.contains(typeDecl))
this.bootstrapMethods.add(typeDecl);
int index = this.bootstrapMethods.indexOf(typeDecl);
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);
switch (purpose) {
case SyntheticMethodBinding.RecordCanonicalConstructor:
this.codeStream.generateSyntheticBodyForRecordCanonicalConstructor(methodBinding);
break;
case SyntheticMethodBinding.RecordOverrideEquals:
this.codeStream.generateSyntheticBodyForRecordEquals(methodBinding, index);
break;
case SyntheticMethodBinding.RecordOverrideHashCode:
this.codeStream.generateSyntheticBodyForRecordHashCode(methodBinding, index);
break;
case SyntheticMethodBinding.RecordOverrideToString:
this.codeStream.generateSyntheticBodyForRecordToString(methodBinding, index);
break;
default:
break;
}
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 addSyntheticArrayConstructor(SyntheticMethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
// this will add exception attribute, synthetic attribute, deprecated attribute,...
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
int codeAttributeOffset = this.contentsOffset;
attributeNumber++; // add code attribute
generateCodeAttributeHeader();
this.codeStream.init(this);
this.codeStream.generateSyntheticBodyForArrayConstructor(methodBinding);
completeCodeAttributeForSyntheticMethod(
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions());
// update the number of attributes
this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributeNumber;
}
public void addSyntheticArrayClone(SyntheticMethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
// this will add exception attribute, synthetic attribute, deprecated attribute,...
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
int codeAttributeOffset = this.contentsOffset;
attributeNumber++; // add code attribute
generateCodeAttributeHeader();
this.codeStream.init(this);
this.codeStream.generateSyntheticBodyForArrayClone(methodBinding);
completeCodeAttributeForSyntheticMethod(
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions());
// update the number of attributes
this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributeNumber;
}
public void addSyntheticFactoryMethod(SyntheticMethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
// this will add exception attribute, synthetic attribute, deprecated attribute,...
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
int codeAttributeOffset = this.contentsOffset;
attributeNumber++; // add code attribute
generateCodeAttributeHeader();
this.codeStream.init(this);
this.codeStream.generateSyntheticBodyForFactoryMethod(methodBinding);
completeCodeAttributeForSyntheticMethod(
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions());
// update the number of attributes
this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributeNumber;
}
/**
* INTERNAL USE-ONLY
* Generate the bytes for a synthetic method that provides an access to a private constructor.
*
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
*/
public void addSyntheticConstructorAccessMethod(SyntheticMethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
// this will add exception attribute, synthetic attribute, deprecated attribute,...
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
int codeAttributeOffset = this.contentsOffset;
attributeNumber++; // add code attribute
generateCodeAttributeHeader();
this.codeStream.init(this);
this.codeStream.generateSyntheticBodyForConstructorAccess(methodBinding);
completeCodeAttributeForSyntheticMethod(
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions());
// update the number of attributes
this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributeNumber;
}
/**
* INTERNAL USE-ONLY
* Generate the bytes for a synthetic method that implements Enum#valueOf(String) for a given enum type
*
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
*/
public void addSyntheticEnumValueOfMethod(SyntheticMethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
// this will add exception attribute, synthetic attribute, deprecated attribute,...
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
int codeAttributeOffset = this.contentsOffset;
attributeNumber++; // add code attribute
generateCodeAttributeHeader();
this.codeStream.init(this);
this.codeStream.generateSyntheticBodyForEnumValueOf(methodBinding);
completeCodeAttributeForSyntheticMethod(
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions());
// update the number of attributes
if ((this.produceAttributes & ClassFileConstants.ATTR_METHOD_PARAMETERS) != 0) {
attributeNumber += generateMethodParameters(methodBinding);
}
this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributeNumber;
}
/**
* INTERNAL USE-ONLY
* Generate the bytes for a synthetic method that implements Enum#values() for a given enum type
*
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
*/
public void addSyntheticEnumValuesMethod(SyntheticMethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
// this will add exception attribute, synthetic attribute, deprecated attribute,...
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
int codeAttributeOffset = this.contentsOffset;
attributeNumber++; // add code attribute
generateCodeAttributeHeader();
this.codeStream.init(this);
this.codeStream.generateSyntheticBodyForEnumValues(methodBinding);
completeCodeAttributeForSyntheticMethod(
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions());
// update the number of attributes
this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributeNumber;
}
public void addSyntheticEnumInitializationMethod(SyntheticMethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
// this will add exception attribute, synthetic attribute, deprecated attribute,...
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
int codeAttributeOffset = this.contentsOffset;
attributeNumber++; // add code attribute
generateCodeAttributeHeader();
this.codeStream.init(this);
this.codeStream.generateSyntheticBodyForEnumInitializationMethod(methodBinding);
completeCodeAttributeForSyntheticMethod(
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions());
// update the number of attributes
this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributeNumber;
}
/**
* INTERNAL USE-ONLY
* Generate the byte for a problem method info that correspond to a synthetic method that
* generate an read access to a private field.
*
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
*/
public void addSyntheticFieldReadAccessMethod(SyntheticMethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
// this will add exception attribute, synthetic attribute, deprecated attribute,...
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
int codeAttributeOffset = this.contentsOffset;
attributeNumber++; // add code attribute
generateCodeAttributeHeader();
this.codeStream.init(this);
this.codeStream.generateSyntheticBodyForFieldReadAccess(methodBinding);
completeCodeAttributeForSyntheticMethod(
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions());
// update the number of attributes
this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributeNumber;
}
/**
* INTERNAL USE-ONLY
* Generate the byte for a problem method info that correspond to a synthetic method that
* generate an write access to a private field.
*
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
*/
public void addSyntheticFieldWriteAccessMethod(SyntheticMethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
// this will add exception attribute, synthetic attribute, deprecated attribute,...
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
int codeAttributeOffset = this.contentsOffset;
attributeNumber++; // add code attribute
generateCodeAttributeHeader();
this.codeStream.init(this);
this.codeStream.generateSyntheticBodyForFieldWriteAccess(methodBinding);
completeCodeAttributeForSyntheticMethod(
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions());
// update the number of attributes
this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributeNumber;
}
/**
* INTERNAL USE-ONLY
* Generate the bytes for a synthetic method that provides access to a private method.
*
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
*/
public void addSyntheticMethodAccessMethod(SyntheticMethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
// this will add exception attribute, synthetic attribute, deprecated attribute,...
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
int codeAttributeOffset = this.contentsOffset;
attributeNumber++; // add code attribute
generateCodeAttributeHeader();
this.codeStream.init(this);
this.codeStream.generateSyntheticBodyForMethodAccess(methodBinding);
completeCodeAttributeForSyntheticMethod(
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions());
// update the number of attributes
this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributeNumber;
}
public void addSyntheticSwitchTable(SyntheticMethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
// this will add exception attribute, synthetic attribute, deprecated attribute,...
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
int codeAttributeOffset = this.contentsOffset;
attributeNumber++; // add code attribute
generateCodeAttributeHeader();
this.codeStream.init(this);
this.codeStream.generateSyntheticBodyForSwitchTable(methodBinding);
int code_length = this.codeStream.position;
if (code_length > 65535) {
SwitchStatement switchStatement = methodBinding.switchStatement;
if (switchStatement != null) {
switchStatement.scope.problemReporter().bytecodeExceeds64KLimit(switchStatement);
}
}
completeCodeAttributeForSyntheticMethod(
true,
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions(),
((SourceTypeBinding) methodBinding.declaringClass)
.scope);
// update the number of attributes
this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributeNumber;
}
/**
* INTERNAL USE-ONLY
* That method completes the creation of the code attribute by setting
* - the attribute_length
* - max_stack
* - max_locals
* - code_length
* - exception table
* - and debug attributes if necessary.
*
* @param codeAttributeOffset int
*/
public void completeCodeAttribute(int codeAttributeOffset, MethodScope scope) {
// reinitialize the localContents with the byte modified by the code stream
this.contents = this.codeStream.bCodeStream;
int localContentsOffset = this.codeStream.classFileOffset;
// codeAttributeOffset is the position inside localContents byte array before we started to write
// any information about the codeAttribute
// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset
// to get the right position, 6 for the max_stack etc...
int code_length = this.codeStream.position;
if (code_length > 65535) {
if (this.codeStream.methodDeclaration != null) {
this.codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit(this.codeStream.methodDeclaration);
} else {
this.codeStream.lambdaExpression.scope.problemReporter().bytecodeExceeds64KLimit(this.codeStream.lambdaExpression);
}
}
if (localContentsOffset + 20 >= this.contents.length) {
resizeContents(20);
}
int max_stack = this.codeStream.stackMax;
this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
this.contents[codeAttributeOffset + 7] = (byte) max_stack;
int max_locals = this.codeStream.maxLocals;
this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
this.contents[codeAttributeOffset + 9] = (byte) max_locals;
this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
this.contents[codeAttributeOffset + 13] = (byte) code_length;
boolean addStackMaps = (this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0;
// write the exception table
ExceptionLabel[] exceptionLabels = this.codeStream.exceptionLabels;
int exceptionHandlersCount = 0; // each label holds one handler per range (start/end contiguous)
for (int i = 0, length = this.codeStream.exceptionLabelsCounter; i < length; i++) {
exceptionHandlersCount += this.codeStream.exceptionLabels[i].getCount() / 2;
}
int exSize = exceptionHandlersCount * 8 + 2;
if (exSize + localContentsOffset >= this.contents.length) {
resizeContents(exSize);
}
// there is no exception table, so we need to offset by 2 the current offset and move
// on the attribute generation
this.contents[localContentsOffset++] = (byte) (exceptionHandlersCount >> 8);
this.contents[localContentsOffset++] = (byte) exceptionHandlersCount;
for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) {
ExceptionLabel exceptionLabel = exceptionLabels[i];
if (exceptionLabel != null) {
int iRange = 0, maxRange = exceptionLabel.getCount();
if ((maxRange & 1) != 0) {
if (this.codeStream.methodDeclaration != null) {
this.codeStream.methodDeclaration.scope.problemReporter().abortDueToInternalError(
Messages.bind(Messages.abort_invalidExceptionAttribute, new String(this.codeStream.methodDeclaration.selector)),
this.codeStream.methodDeclaration);
} else {
this.codeStream.lambdaExpression.scope.problemReporter().abortDueToInternalError(
Messages.bind(Messages.abort_invalidExceptionAttribute, new String(this.codeStream.lambdaExpression.binding.selector)),
this.codeStream.lambdaExpression);
}
}
while (iRange < maxRange) {
int start = exceptionLabel.ranges[iRange++]; // even ranges are start positions
this.contents[localContentsOffset++] = (byte) (start >> 8);
this.contents[localContentsOffset++] = (byte) start;
int end = exceptionLabel.ranges[iRange++]; // odd ranges are end positions
this.contents[localContentsOffset++] = (byte) (end >> 8);
this.contents[localContentsOffset++] = (byte) end;
int handlerPC = exceptionLabel.position;
if (addStackMaps) {
StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
stackMapFrameCodeStream.addFramePosition(handlerPC);
// stackMapFrameCodeStream.addExceptionMarker(handlerPC, exceptionLabel.exceptionType);
}
this.contents[localContentsOffset++] = (byte) (handlerPC >> 8);
this.contents[localContentsOffset++] = (byte) handlerPC;
if (exceptionLabel.exceptionType == null) {
// any exception handler
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
} else {
int nameIndex;
if (exceptionLabel.exceptionType == TypeBinding.NULL) {
/* represents ClassNotFoundException, see class literal access*/
nameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName);
} else {
nameIndex = this.constantPool.literalIndexForType(exceptionLabel.exceptionType);
}
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
}
}
}
}
// debug attributes
int codeAttributeAttributeOffset = localContentsOffset;
int attributesNumber = 0;
// leave two bytes for the attribute_length
localContentsOffset += 2;
if (localContentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contentsOffset = localContentsOffset;
// first we handle the linenumber attribute
if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) {
attributesNumber += generateLineNumberAttribute();
}
// then we do the local variable attribute
if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) {
final boolean methodDeclarationIsStatic = this.codeStream.methodDeclaration != null ? this.codeStream.methodDeclaration.isStatic() : this.codeStream.lambdaExpression.binding.isStatic();
attributesNumber += generateLocalVariableTableAttribute(code_length, methodDeclarationIsStatic, false);
}
if (addStackMaps) {
attributesNumber += generateStackMapTableAttribute(
this.codeStream.methodDeclaration != null ? this.codeStream.methodDeclaration.binding : this.codeStream.lambdaExpression.binding,
code_length,
codeAttributeOffset,
max_locals,
false,
scope);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) {
attributesNumber += generateStackMapAttribute(
this.codeStream.methodDeclaration != null ? this.codeStream.methodDeclaration.binding : this.codeStream.lambdaExpression.binding,
code_length,
codeAttributeOffset,
max_locals,
false,
scope);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) {
attributesNumber += generateTypeAnnotationsOnCodeAttribute();
}
this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8);
this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber;
// update the attribute length
int codeAttributeLength = this.contentsOffset - (codeAttributeOffset + 6);
this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength;
}
public int generateTypeAnnotationsOnCodeAttribute() {
int attributesNumber = 0;
List allTypeAnnotationContexts = ((TypeAnnotationCodeStream) this.codeStream).allTypeAnnotationContexts;
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();
attributesNumber = completeRuntimeTypeAnnotations(attributesNumber,
null,
node -> size > 0,
() -> allTypeAnnotationContexts);
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 int
*/
public void completeCodeAttributeForClinit(int codeAttributeOffset, Scope scope) {
// reinitialize the contents with the byte modified by the code stream
this.contents = this.codeStream.bCodeStream;
int localContentsOffset = this.codeStream.classFileOffset;
// codeAttributeOffset is the position inside contents byte array before we started to write
// any information about the codeAttribute
// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset
// to get the right position, 6 for the max_stack etc...
int code_length = this.codeStream.position;
if (code_length > 65535) {
this.codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit(
this.codeStream.methodDeclaration.scope.referenceType());
}
if (localContentsOffset + 20 >= this.contents.length) {
resizeContents(20);
}
int max_stack = this.codeStream.stackMax;
this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
this.contents[codeAttributeOffset + 7] = (byte) max_stack;
int max_locals = this.codeStream.maxLocals;
this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
this.contents[codeAttributeOffset + 9] = (byte) max_locals;
this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
this.contents[codeAttributeOffset + 13] = (byte) code_length;
boolean addStackMaps = (this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0;
// write the exception table
ExceptionLabel[] exceptionLabels = this.codeStream.exceptionLabels;
int exceptionHandlersCount = 0; // each label holds one handler per range (start/end contiguous)
for (int i = 0, length = this.codeStream.exceptionLabelsCounter; i < length; i++) {
exceptionHandlersCount += this.codeStream.exceptionLabels[i].getCount() / 2;
}
int exSize = exceptionHandlersCount * 8 + 2;
if (exSize + localContentsOffset >= this.contents.length) {
resizeContents(exSize);
}
// there is no exception table, so we need to offset by 2 the current offset and move
// on the attribute generation
this.contents[localContentsOffset++] = (byte) (exceptionHandlersCount >> 8);
this.contents[localContentsOffset++] = (byte) exceptionHandlersCount;
for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) {
ExceptionLabel exceptionLabel = exceptionLabels[i];
if (exceptionLabel != null) {
int iRange = 0, maxRange = exceptionLabel.getCount();
if ((maxRange & 1) != 0) {
this.codeStream.methodDeclaration.scope.problemReporter().abortDueToInternalError(
Messages.bind(Messages.abort_invalidExceptionAttribute, new String(this.codeStream.methodDeclaration.selector)),
this.codeStream.methodDeclaration);
}
while (iRange < maxRange) {
int start = exceptionLabel.ranges[iRange++]; // even ranges are start positions
this.contents[localContentsOffset++] = (byte) (start >> 8);
this.contents[localContentsOffset++] = (byte) start;
int end = exceptionLabel.ranges[iRange++]; // odd ranges are end positions
this.contents[localContentsOffset++] = (byte) (end >> 8);
this.contents[localContentsOffset++] = (byte) end;
int handlerPC = exceptionLabel.position;
this.contents[localContentsOffset++] = (byte) (handlerPC >> 8);
this.contents[localContentsOffset++] = (byte) handlerPC;
if (addStackMaps) {
StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
stackMapFrameCodeStream.addFramePosition(handlerPC);
// stackMapFrameCodeStream.addExceptionMarker(handlerPC, exceptionLabel.exceptionType);
}
if (exceptionLabel.exceptionType == null) {
// any exception handler
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
} else {
int nameIndex;
if (exceptionLabel.exceptionType == TypeBinding.NULL) {
/* represents denote ClassNotFoundException, see class literal access*/
nameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName);
} else {
nameIndex = this.constantPool.literalIndexForType(exceptionLabel.exceptionType);
}
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
}
}
}
}
// debug attributes
int codeAttributeAttributeOffset = localContentsOffset;
int attributesNumber = 0;
// leave two bytes for the attribute_length
localContentsOffset += 2;
if (localContentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contentsOffset = localContentsOffset;
// first we handle the linenumber attribute
if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) {
attributesNumber += generateLineNumberAttribute();
}
// then we do the local variable attribute
if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) {
attributesNumber += generateLocalVariableTableAttribute(code_length, true, false);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0) {
attributesNumber += generateStackMapTableAttribute(
null,
code_length,
codeAttributeOffset,
max_locals,
true,
scope);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) {
attributesNumber += generateStackMapAttribute(
null,
code_length,
codeAttributeOffset,
max_locals,
true,
scope);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) {
attributesNumber += generateTypeAnnotationsOnCodeAttribute();
}
// update the number of attributes
// ensure first that there is enough space available inside the contents array
if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8);
this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber;
// update the attribute length
int codeAttributeLength = this.contentsOffset - (codeAttributeOffset + 6);
this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength;
}
/**
* INTERNAL USE-ONLY
* That method completes the creation of the code attribute by setting
* - the attribute_length
* - max_stack
* - max_locals
* - code_length
* - exception table
* - and debug attributes if necessary.
*/
public void completeCodeAttributeForClinit(
int codeAttributeOffset,
int problemLine,
MethodScope scope) {
// reinitialize the contents with the byte modified by the code stream
this.contents = this.codeStream.bCodeStream;
int localContentsOffset = this.codeStream.classFileOffset;
// codeAttributeOffset is the position inside contents byte array before we started to write
// any information about the codeAttribute
// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset
// to get the right position, 6 for the max_stack etc...
int code_length = this.codeStream.position;
if (code_length > 65535) {
this.codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit(
this.codeStream.methodDeclaration.scope.referenceType());
}
if (localContentsOffset + 20 >= this.contents.length) {
resizeContents(20);
}
int max_stack = this.codeStream.stackMax;
this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
this.contents[codeAttributeOffset + 7] = (byte) max_stack;
int max_locals = this.codeStream.maxLocals;
this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
this.contents[codeAttributeOffset + 9] = (byte) max_locals;
this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
this.contents[codeAttributeOffset + 13] = (byte) code_length;
// write the exception table
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
// debug attributes
int codeAttributeAttributeOffset = localContentsOffset;
int attributesNumber = 0; // leave two bytes for the attribute_length
localContentsOffset += 2; // first we handle the linenumber attribute
if (localContentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contentsOffset = localContentsOffset;
// first we handle the linenumber attribute
if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) {
attributesNumber += generateLineNumberAttribute(problemLine);
}
localContentsOffset = this.contentsOffset;
// then we do the local variable attribute
if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) {
int localVariableNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName);
if (localContentsOffset + 8 >= this.contents.length) {
resizeContents(8);
}
this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) localVariableNameIndex;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 2;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
attributesNumber++;
}
this.contentsOffset = localContentsOffset;
if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0) {
attributesNumber += generateStackMapTableAttribute(
null,
code_length,
codeAttributeOffset,
max_locals,
true,
scope);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) {
attributesNumber += generateStackMapAttribute(
null,
code_length,
codeAttributeOffset,
max_locals,
true,
scope);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) {
attributesNumber += generateTypeAnnotationsOnCodeAttribute();
}
// update the number of attributes
// ensure first that there is enough space available inside the contents array
if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8);
this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber;
// update the attribute length
int codeAttributeLength = this.contentsOffset - (codeAttributeOffset + 6);
this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength;
}
public void completeCodeAttributeForMissingAbstractProblemMethod(
MethodBinding binding,
int codeAttributeOffset,
int[] startLineIndexes,
int problemLine) {
// reinitialize the localContents with the byte modified by the code stream
this.contents = this.codeStream.bCodeStream;
int localContentsOffset = this.codeStream.classFileOffset;
// codeAttributeOffset is the position inside localContents byte array before we started to write// any information about the codeAttribute// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset// to get the right position, 6 for the max_stack etc...
int max_stack = this.codeStream.stackMax;
this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
this.contents[codeAttributeOffset + 7] = (byte) max_stack;
int max_locals = this.codeStream.maxLocals;
this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
this.contents[codeAttributeOffset + 9] = (byte) max_locals;
int code_length = this.codeStream.position;
this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
this.contents[codeAttributeOffset + 13] = (byte) code_length;
// write the exception table
if (localContentsOffset + 50 >= this.contents.length) {
resizeContents(50);
}
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
// debug attributes
int codeAttributeAttributeOffset = localContentsOffset;
int attributesNumber = 0; // leave two bytes for the attribute_length
localContentsOffset += 2; // first we handle the linenumber attribute
if (localContentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contentsOffset = localContentsOffset;
if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) {
if (problemLine == 0) {
problemLine = Util.getLineNumber(binding.sourceStart(), startLineIndexes, 0, startLineIndexes.length-1);
}
attributesNumber += generateLineNumberAttribute(problemLine);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0) {
attributesNumber += generateStackMapTableAttribute(
binding,
code_length,
codeAttributeOffset,
max_locals,
false,
null);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) {
attributesNumber += generateStackMapAttribute(
binding,
code_length,
codeAttributeOffset,
max_locals,
false,
null);
}
// then we do the local variable attribute
// update the number of attributes// ensure first that there is enough space available inside the localContents array
if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8);
this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber;
// update the attribute length
int codeAttributeLength = this.contentsOffset - (codeAttributeOffset + 6);
this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength;
}
/**
* INTERNAL USE-ONLY
* That method completes the creation of the code attribute by setting
* - the attribute_length
* - max_stack
* - max_locals
* - code_length
* - exception table
* - and debug attributes if necessary.
*
* @param codeAttributeOffset int
*/
public void completeCodeAttributeForProblemMethod(
AbstractMethodDeclaration method,
MethodBinding binding,
int codeAttributeOffset,
int[] startLineIndexes,
int problemLine) {
// reinitialize the localContents with the byte modified by the code stream
this.contents = this.codeStream.bCodeStream;
int localContentsOffset = this.codeStream.classFileOffset;
// codeAttributeOffset is the position inside localContents byte array before we started to write// any information about the codeAttribute// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset// to get the right position, 6 for the max_stack etc...
int max_stack = this.codeStream.stackMax;
this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
this.contents[codeAttributeOffset + 7] = (byte) max_stack;
int max_locals = this.codeStream.maxLocals;
this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
this.contents[codeAttributeOffset + 9] = (byte) max_locals;
int code_length = this.codeStream.position;
this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
this.contents[codeAttributeOffset + 13] = (byte) code_length;
// write the exception table
if (localContentsOffset + 50 >= this.contents.length) {
resizeContents(50);
}
// write the exception table
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
// debug attributes
int codeAttributeAttributeOffset = localContentsOffset;
int attributesNumber = 0; // leave two bytes for the attribute_length
localContentsOffset += 2; // first we handle the linenumber attribute
if (localContentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contentsOffset = localContentsOffset;
if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) {
if (problemLine == 0) {
problemLine = Util.getLineNumber(binding.sourceStart(), startLineIndexes, 0, startLineIndexes.length-1);
}
attributesNumber += generateLineNumberAttribute(problemLine);
}
// then we do the local variable attribute
if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) {
final boolean methodDeclarationIsStatic = this.codeStream.methodDeclaration.isStatic();
attributesNumber += generateLocalVariableTableAttribute(code_length, methodDeclarationIsStatic, false);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0) {
attributesNumber += generateStackMapTableAttribute(
binding,
code_length,
codeAttributeOffset,
max_locals,
false,
null);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) {
attributesNumber += generateStackMapAttribute(
binding,
code_length,
codeAttributeOffset,
max_locals,
false,
null);
}
// update the number of attributes// ensure first that there is enough space available inside the localContents array
if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8);
this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber;
// update the attribute length
int codeAttributeLength = this.contentsOffset - (codeAttributeOffset + 6);
this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength;
}
/**
* INTERNAL USE-ONLY
* That method completes the creation of the code attribute by setting
* - the attribute_length
* - max_stack
* - max_locals
* - code_length
* - exception table
* - and debug attributes if necessary.
*
* @param binding org.eclipse.jdt.internal.compiler.lookup.SyntheticAccessMethodBinding
* @param codeAttributeOffset int
*/
public void completeCodeAttributeForSyntheticMethod(
boolean hasExceptionHandlers,
SyntheticMethodBinding binding,
int codeAttributeOffset,
int[] startLineIndexes,
Scope scope) {
// reinitialize the contents with the byte modified by the code stream
this.contents = this.codeStream.bCodeStream;
int localContentsOffset = this.codeStream.classFileOffset;
// codeAttributeOffset is the position inside contents byte array before we started to write
// any information about the codeAttribute
// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset
// to get the right position, 6 for the max_stack etc...
int max_stack = this.codeStream.stackMax;
this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
this.contents[codeAttributeOffset + 7] = (byte) max_stack;
int max_locals = this.codeStream.maxLocals;
this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
this.contents[codeAttributeOffset + 9] = (byte) max_locals;
int code_length = this.codeStream.position;
this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
this.contents[codeAttributeOffset + 13] = (byte) code_length;
if ((localContentsOffset + 40) >= this.contents.length) {
resizeContents(40);
}
boolean addStackMaps = (this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP_TABLE) != 0;
if (hasExceptionHandlers) {
// write the exception table
ExceptionLabel[] exceptionLabels = this.codeStream.exceptionLabels;
int exceptionHandlersCount = 0; // each label holds one handler per range (start/end contiguous)
for (int i = 0, length = this.codeStream.exceptionLabelsCounter; i < length; i++) {
exceptionHandlersCount += this.codeStream.exceptionLabels[i].getCount() / 2;
}
int exSize = exceptionHandlersCount * 8 + 2;
if (exSize + localContentsOffset >= this.contents.length) {
resizeContents(exSize);
}
// there is no exception table, so we need to offset by 2 the current offset and move
// on the attribute generation
this.contents[localContentsOffset++] = (byte) (exceptionHandlersCount >> 8);
this.contents[localContentsOffset++] = (byte) exceptionHandlersCount;
for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) {
ExceptionLabel exceptionLabel = exceptionLabels[i];
if (exceptionLabel != null) {
int iRange = 0, maxRange = exceptionLabel.getCount();
if ((maxRange & 1) != 0) {
ProblemReporter problemReporter = this.referenceBinding.scope.problemReporter();
problemReporter.abortDueToInternalError(
Messages.bind(Messages.abort_invalidExceptionAttribute, new String(binding.selector),
problemReporter.referenceContext));
}
while (iRange < maxRange) {
int start = exceptionLabel.ranges[iRange++]; // even ranges are start positions
this.contents[localContentsOffset++] = (byte) (start >> 8);
this.contents[localContentsOffset++] = (byte) start;
int end = exceptionLabel.ranges[iRange++]; // odd ranges are end positions
this.contents[localContentsOffset++] = (byte) (end >> 8);
this.contents[localContentsOffset++] = (byte) end;
int handlerPC = exceptionLabel.position;
if (addStackMaps) {
StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
stackMapFrameCodeStream.addFramePosition(handlerPC);
}
this.contents[localContentsOffset++] = (byte) (handlerPC >> 8);
this.contents[localContentsOffset++] = (byte) handlerPC;
if (exceptionLabel.exceptionType == null) {
// any exception handler
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
} else {
int nameIndex;
switch(exceptionLabel.exceptionType.id) {
case T_null :
/* represents ClassNotFoundException, see class literal access*/
nameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName);
break;
case T_long :
/* represents NoSuchFieldError, see switch table generation*/
nameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangNoSuchFieldErrorConstantPoolName);
break;
default:
nameIndex = this.constantPool.literalIndexForType(exceptionLabel.exceptionType);
}
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
}
}
}
}
} else {
// there is no exception table, so we need to offset by 2 the current offset and move
// on the attribute generation
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
}
// debug attributes
int codeAttributeAttributeOffset = localContentsOffset;
int attributesNumber = 0;
// leave two bytes for the attribute_length
localContentsOffset += 2;
if (localContentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contentsOffset = localContentsOffset;
// first we handle the linenumber attribute
if ((this.produceAttributes & ClassFileConstants.ATTR_LINES) != 0) {
int lineNumber = Util.getLineNumber(binding.sourceStart, startLineIndexes, 0, startLineIndexes.length-1);
attributesNumber += generateLineNumberAttribute(lineNumber);
}
// then we do the local variable attribute
if ((this.produceAttributes & ClassFileConstants.ATTR_VARS) != 0) {
final boolean methodDeclarationIsStatic = binding.isStatic();
attributesNumber += generateLocalVariableTableAttribute(code_length, methodDeclarationIsStatic, true);
}
if (addStackMaps) {
attributesNumber += generateStackMapTableAttribute(binding, code_length, codeAttributeOffset, max_locals, false, scope);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_STACK_MAP) != 0) {
attributesNumber += generateStackMapAttribute(
binding,
code_length,
codeAttributeOffset,
max_locals,
false,
scope);
}
// update the number of attributes
// ensure first that there is enough space available inside the contents array
if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[codeAttributeAttributeOffset++] = (byte) (attributesNumber >> 8);
this.contents[codeAttributeAttributeOffset] = (byte) attributesNumber;
// update the attribute length
int codeAttributeLength = this.contentsOffset - (codeAttributeOffset + 6);
this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
this.contents[codeAttributeOffset + 5] = (byte) codeAttributeLength;
}
/**
* INTERNAL USE-ONLY
* That method completes the creation of the code attribute by setting
* - the attribute_length
* - max_stack
* - max_locals
* - code_length
* - exception table
* - and debug attributes if necessary.
*
* @param binding org.eclipse.jdt.internal.compiler.lookup.SyntheticAccessMethodBinding
* @param codeAttributeOffset int
*/
public void completeCodeAttributeForSyntheticMethod(
SyntheticMethodBinding binding,
int codeAttributeOffset,
int[] startLineIndexes) {
this.completeCodeAttributeForSyntheticMethod(
false,
binding,
codeAttributeOffset,
startLineIndexes,
((SourceTypeBinding) binding.declaringClass).scope);
}
private void completeArgumentAnnotationInfo(Argument[] arguments, List allAnnotationContexts) {
for (int i = 0, max = arguments.length; i < max; i++) {
Argument argument = arguments[i];
if ((argument.bits & ASTNode.HasTypeAnnotations) != 0) {
argument.getAllAnnotationContexts(AnnotationTargetTypeConstants.METHOD_FORMAL_PARAMETER, i, allAnnotationContexts);
}
}
}
/**
* INTERNAL USE-ONLY
* Complete the creation of a method info by setting up the number of attributes at the right offset.
*
* @param methodAttributeOffset int
* @param attributesNumber int
*/
public void completeMethodInfo(
MethodBinding binding,
int methodAttributeOffset,
int attributesNumber) {
if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) {
List allTypeAnnotationContexts = new ArrayList<>();
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)) {
// at source level type annotations have not been moved from declaration to type use position, collect them now:
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();
attributesNumber = completeRuntimeTypeAnnotations(attributesNumber,
null,
node -> size > 0,
() -> allTypeAnnotationContexts);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_METHOD_PARAMETERS) != 0 ||
binding.isConstructor() && binding.declaringClass.isRecord()) {
attributesNumber += generateMethodParameters(binding);
}
// update the number of attributes
this.contents[methodAttributeOffset++] = (byte) (attributesNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributesNumber;
}
private void dumpLocations(int[] locations) {
if (locations == null) {
// no type path
if (this.contentsOffset + 1 >= this.contents.length) {
resizeContents(1);
}
this.contents[this.contentsOffset++] = (byte) 0;
} else {
int length = locations.length;
if (this.contentsOffset + length >= this.contents.length) {
resizeContents(length + 1);
}
this.contents[this.contentsOffset++] = (byte) (locations.length / 2);
for (int i = 0; i < length; i++) {
this.contents[this.contentsOffset++] = (byte) locations[i];
}
}
}
private void dumpTargetTypeContents(int targetType, AnnotationContext annotationContext) {
switch(targetType) {
case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER :
case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER :
// parameter index
this.contents[this.contentsOffset++] = (byte) annotationContext.info;
break;
case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER_BOUND :
// type_parameter_index
this.contents[this.contentsOffset++] = (byte) annotationContext.info;
// bound_index
this.contents[this.contentsOffset++] = (byte) annotationContext.info2;
break;
case AnnotationTargetTypeConstants.FIELD :
case AnnotationTargetTypeConstants.METHOD_RECEIVER :
case AnnotationTargetTypeConstants.METHOD_RETURN :
// target_info is empty_target
break;
case AnnotationTargetTypeConstants.METHOD_FORMAL_PARAMETER :
// target_info is parameter index
this.contents[this.contentsOffset++] = (byte) annotationContext.info;
break;
case AnnotationTargetTypeConstants.INSTANCEOF :
case AnnotationTargetTypeConstants.NEW :
case AnnotationTargetTypeConstants.EXCEPTION_PARAMETER :
case AnnotationTargetTypeConstants.CONSTRUCTOR_REFERENCE :
case AnnotationTargetTypeConstants.METHOD_REFERENCE :
// bytecode offset for new/instanceof/method_reference
// exception table entry index for exception_parameter
this.contents[this.contentsOffset++] = (byte) (annotationContext.info >> 8);
this.contents[this.contentsOffset++] = (byte) annotationContext.info;
break;
case AnnotationTargetTypeConstants.CAST :
// bytecode offset
this.contents[this.contentsOffset++] = (byte) (annotationContext.info >> 8);
this.contents[this.contentsOffset++] = (byte) annotationContext.info;
this.contents[this.contentsOffset++] = (byte) annotationContext.info2;
break;
case AnnotationTargetTypeConstants.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT :
case AnnotationTargetTypeConstants.METHOD_INVOCATION_TYPE_ARGUMENT :
case AnnotationTargetTypeConstants.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT :
case AnnotationTargetTypeConstants.METHOD_REFERENCE_TYPE_ARGUMENT :
// bytecode offset
this.contents[this.contentsOffset++] = (byte) (annotationContext.info >> 8);
this.contents[this.contentsOffset++] = (byte) annotationContext.info;
// type_argument_index
this.contents[this.contentsOffset++] = (byte) annotationContext.info2;
break;
case AnnotationTargetTypeConstants.CLASS_EXTENDS :
case AnnotationTargetTypeConstants.THROWS :
// For CLASS_EXTENDS - info is supertype index (-1 = superclass)
// For THROWS - info is exception table index
this.contents[this.contentsOffset++] = (byte) (annotationContext.info >> 8);
this.contents[this.contentsOffset++] = (byte) annotationContext.info;
break;
case AnnotationTargetTypeConstants.LOCAL_VARIABLE :
case AnnotationTargetTypeConstants.RESOURCE_VARIABLE :
int localVariableTableOffset = this.contentsOffset;
LocalVariableBinding localVariable = annotationContext.variableBinding;
int actualSize = 0;
int initializationCount = localVariable.initializationCount;
actualSize += 2 /* for number of entries */ + (6 * initializationCount);
// reserve enough space
if (this.contentsOffset + actualSize >= this.contents.length) {
resizeContents(actualSize);
}
this.contentsOffset += 2;
int numberOfEntries = 0;
for (int j = 0; j < initializationCount; j++) {
int startPC = localVariable.initializationPCs[j << 1];
int endPC = localVariable.initializationPCs[(j << 1) + 1];
if (startPC != endPC) { // only entries for non zero length
// now we can safely add the local entry
numberOfEntries++;
this.contents[this.contentsOffset++] = (byte) (startPC >> 8);
this.contents[this.contentsOffset++] = (byte) startPC;
int length = endPC - startPC;
this.contents[this.contentsOffset++] = (byte) (length >> 8);
this.contents[this.contentsOffset++] = (byte) length;
int resolvedPosition = localVariable.resolvedPosition;
this.contents[this.contentsOffset++] = (byte) (resolvedPosition >> 8);
this.contents[this.contentsOffset++] = (byte) resolvedPosition;
}
}
this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8);
this.contents[localVariableTableOffset] = (byte) numberOfEntries;
break;
case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER_BOUND :
this.contents[this.contentsOffset++] = (byte) annotationContext.info;
this.contents[this.contentsOffset++] = (byte) annotationContext.info2;
break;
}
}
/**
* INTERNAL USE-ONLY
* This methods returns a char[] representing the file name of the receiver
*
* @return char[]
*/
public char[] fileName() {
return this.constantPool.UTF8Cache.returnKeyFor(2);
}
private void generateAnnotation(Annotation annotation, int currentOffset) {
int startingContentsOffset = currentOffset;
if (this.contentsOffset + 4 >= this.contents.length) {
resizeContents(4);
}
TypeBinding annotationTypeBinding = annotation.resolvedType;
if (annotationTypeBinding == null) {
this.contentsOffset = startingContentsOffset;
return;
}
if (annotationTypeBinding.isMemberType()) {
this.recordInnerClasses(annotationTypeBinding);
}
final int typeIndex = this.constantPool.literalIndex(annotationTypeBinding.signature());
this.contents[this.contentsOffset++] = (byte) (typeIndex >> 8);
this.contents[this.contentsOffset++] = (byte) typeIndex;
if (annotation instanceof NormalAnnotation) {
NormalAnnotation normalAnnotation = (NormalAnnotation) annotation;
MemberValuePair[] memberValuePairs = normalAnnotation.memberValuePairs;
int memberValuePairOffset = this.contentsOffset;
if (memberValuePairs != null) {
int memberValuePairsCount = 0;
int memberValuePairsLengthPosition = this.contentsOffset;
this.contentsOffset += 2; // leave space to fill in the pair count later
int resetPosition = this.contentsOffset;
final int memberValuePairsLength = memberValuePairs.length;
loop: for (int i = 0; i < memberValuePairsLength; i++) {
MemberValuePair memberValuePair = memberValuePairs[i];
if (this.contentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
final int elementNameIndex = this.constantPool.literalIndex(memberValuePair.name);
this.contents[this.contentsOffset++] = (byte) (elementNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) elementNameIndex;
MethodBinding methodBinding = memberValuePair.binding;
if (methodBinding == null) {
this.contentsOffset = resetPosition;
} else {
try {
generateElementValue(memberValuePair.value, methodBinding.returnType, memberValuePairOffset);
if (this.contentsOffset == memberValuePairOffset) {
// ignore all annotation values
this.contents[this.contentsOffset++] = 0;
this.contents[this.contentsOffset++] = 0;
break loop;
}
memberValuePairsCount++;
resetPosition = this.contentsOffset;
} catch(ClassCastException | ShouldNotImplement e) {
this.contentsOffset = resetPosition;
}
}
}
this.contents[memberValuePairsLengthPosition++] = (byte) (memberValuePairsCount >> 8);
this.contents[memberValuePairsLengthPosition++] = (byte) memberValuePairsCount;
} else {
this.contents[this.contentsOffset++] = 0;
this.contents[this.contentsOffset++] = 0;
}
} else if (annotation instanceof SingleMemberAnnotation) {
SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) annotation;
// this is a single member annotation (one member value)
this.contents[this.contentsOffset++] = 0;
this.contents[this.contentsOffset++] = 1;
if (this.contentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
final int elementNameIndex = this.constantPool.literalIndex(VALUE);
this.contents[this.contentsOffset++] = (byte) (elementNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) elementNameIndex;
MethodBinding methodBinding = singleMemberAnnotation.memberValuePairs()[0].binding;
if (methodBinding == null) {
this.contentsOffset = startingContentsOffset;
} else {
int memberValuePairOffset = this.contentsOffset;
try {
generateElementValue(singleMemberAnnotation.memberValue, methodBinding.returnType, memberValuePairOffset);
if (this.contentsOffset == memberValuePairOffset) {
// completely remove the annotation as its value is invalid
this.contentsOffset = startingContentsOffset;
}
} catch(ClassCastException | ShouldNotImplement e) {
this.contentsOffset = startingContentsOffset;
}
}
} else {
// this is a marker annotation (no member value pairs)
this.contents[this.contentsOffset++] = 0;
this.contents[this.contentsOffset++] = 0;
}
}
private int generateAnnotationDefaultAttribute(AnnotationMethodDeclaration declaration, int attributeOffset) {
int attributesNumber = 0;
// add an annotation default attribute
int annotationDefaultNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.AnnotationDefaultName);
if (this.contentsOffset + 6 >= this.contents.length) {
resizeContents(6);
}
this.contents[this.contentsOffset++] = (byte) (annotationDefaultNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) annotationDefaultNameIndex;
int attributeLengthOffset = this.contentsOffset;
this.contentsOffset += 4;
generateElementValue(declaration.defaultValue, declaration.binding.returnType, attributeOffset);
if (this.contentsOffset != attributeOffset) {
int attributeLength = this.contentsOffset - attributeLengthOffset - 4;
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8);
this.contents[attributeLengthOffset++] = (byte) attributeLength;
attributesNumber++;
}
return attributesNumber;
}
/**
* INTERNAL USE-ONLY
* That method generates the header of a code attribute.
* - the index inside the constant pool for the attribute name ("Code")
* - leave some space for attribute_length(4), max_stack(2), max_locals(2), code_length(4).
*/
public void generateCodeAttributeHeader() {
if (this.contentsOffset + 20 >= this.contents.length) {
resizeContents(20);
}
int constantValueNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.CodeName);
this.contents[this.contentsOffset++] = (byte) (constantValueNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) constantValueNameIndex;
// leave space for attribute_length(4), max_stack(2), max_locals(2), code_length(4)
this.contentsOffset += 12;
}
private int generateConstantValueAttribute(Constant fieldConstant, FieldBinding fieldBinding, int fieldAttributeOffset) {
int localContentsOffset = this.contentsOffset;
int attributesNumber = 1;
if (localContentsOffset + 8 >= this.contents.length) {
resizeContents(8);
}
// Now we generate the constant attribute corresponding to the fieldBinding
int constantValueNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.ConstantValueName);
this.contents[localContentsOffset++] = (byte) (constantValueNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) constantValueNameIndex;
// The attribute length = 2 in case of a constantValue attribute
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 2;
// Need to add the constant_value_index
switch (fieldConstant.typeID()) {
case T_boolean :
int booleanValueIndex =
this.constantPool.literalIndex(fieldConstant.booleanValue() ? 1 : 0);
this.contents[localContentsOffset++] = (byte) (booleanValueIndex >> 8);
this.contents[localContentsOffset++] = (byte) booleanValueIndex;
break;
case T_byte :
case T_char :
case T_int :
case T_short :
int integerValueIndex =
this.constantPool.literalIndex(fieldConstant.intValue());
this.contents[localContentsOffset++] = (byte) (integerValueIndex >> 8);
this.contents[localContentsOffset++] = (byte) integerValueIndex;
break;
case T_float :
int floatValueIndex =
this.constantPool.literalIndex(fieldConstant.floatValue());
this.contents[localContentsOffset++] = (byte) (floatValueIndex >> 8);
this.contents[localContentsOffset++] = (byte) floatValueIndex;
break;
case T_double :
int doubleValueIndex =
this.constantPool.literalIndex(fieldConstant.doubleValue());
this.contents[localContentsOffset++] = (byte) (doubleValueIndex >> 8);
this.contents[localContentsOffset++] = (byte) doubleValueIndex;
break;
case T_long :
int longValueIndex =
this.constantPool.literalIndex(fieldConstant.longValue());
this.contents[localContentsOffset++] = (byte) (longValueIndex >> 8);
this.contents[localContentsOffset++] = (byte) longValueIndex;
break;
case T_JavaLangString :
int stringValueIndex =
this.constantPool.literalIndex(
((StringConstant) fieldConstant).stringValue());
if (stringValueIndex == -1) {
if (!this.creatingProblemType) {
// report an error and abort: will lead to a problem type classfile creation
TypeDeclaration typeDeclaration = this.referenceBinding.scope.referenceContext;
FieldDeclaration[] fieldDecls = typeDeclaration.fields;
int max = fieldDecls == null ? 0 : fieldDecls.length;
for (int i = 0; i < max; i++) {
if (fieldDecls[i].binding == fieldBinding) {
// problem should abort
typeDeclaration.scope.problemReporter().stringConstantIsExceedingUtf8Limit(
fieldDecls[i]);
}
}
} else {
// already inside a problem type creation : no constant for this field
this.contentsOffset = fieldAttributeOffset;
attributesNumber = 0;
}
} else {
this.contents[localContentsOffset++] = (byte) (stringValueIndex >> 8);
this.contents[localContentsOffset++] = (byte) stringValueIndex;
}
}
this.contentsOffset = localContentsOffset;
return attributesNumber;
}
private int generateDeprecatedAttribute() {
int localContentsOffset = this.contentsOffset;
if (localContentsOffset + 6 >= this.contents.length) {
resizeContents(6);
}
int deprecatedAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.DeprecatedName);
this.contents[localContentsOffset++] = (byte) (deprecatedAttributeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) deprecatedAttributeNameIndex;
// the length of a deprecated attribute is equals to 0
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contentsOffset = localContentsOffset;
return 1;
}
private int generateNestHostAttribute() {
SourceTypeBinding nestHost = this.referenceBinding.getNestHost();
if (nestHost == null)
return 0;
int localContentsOffset = this.contentsOffset;
if (localContentsOffset + 10 >= this.contents.length) {
resizeContents(10);
}
int nestHostAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.NestHost);
this.contents[localContentsOffset++] = (byte) (nestHostAttributeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nestHostAttributeNameIndex;
// The value of the attribute_length item must be two.
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 2;
int nestHostIndex = this.constantPool.literalIndexForType(nestHost.constantPoolName());
this.contents[localContentsOffset++] = (byte) (nestHostIndex >> 8);
this.contents[localContentsOffset++] = (byte) nestHostIndex;
this.contentsOffset = localContentsOffset;
return 1;
}
private int generateNestMembersAttribute() {
int localContentsOffset = this.contentsOffset;
List nestedMembers = getNestMembers();
int numberOfNestedMembers = nestedMembers != null ? nestedMembers.size() : 0;
if (numberOfNestedMembers == 0) // JVMS 11 4.7.29 says "at most one" NestMembers attribute - return if none.
return 0;
int exSize = 8 + 2 * numberOfNestedMembers;
if (exSize + localContentsOffset >= this.contents.length) {
resizeContents(exSize);
}
int attributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.NestMembers);
this.contents[localContentsOffset++] = (byte) (attributeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) attributeNameIndex;
int value = (numberOfNestedMembers << 1) + 2;
this.contents[localContentsOffset++] = (byte) (value >> 24);
this.contents[localContentsOffset++] = (byte) (value >> 16);
this.contents[localContentsOffset++] = (byte) (value >> 8);
this.contents[localContentsOffset++] = (byte) value;
this.contents[localContentsOffset++] = (byte) (numberOfNestedMembers >> 8);
this.contents[localContentsOffset++] = (byte) numberOfNestedMembers;
for (int i = 0; i < numberOfNestedMembers; i++) {
char[] nestMemberName = nestedMembers.get(i).toCharArray();
int nestedMemberIndex = this.constantPool.literalIndexForType(nestMemberName);
this.contents[localContentsOffset++] = (byte) (nestedMemberIndex >> 8);
this.contents[localContentsOffset++] = (byte) nestedMemberIndex;
}
this.contentsOffset = localContentsOffset;
return 1;
}
private int generateNestAttributes() {
int nAttrs = generateNestMembersAttribute(); //either member or host will exist 4.7.29
nAttrs += generateNestHostAttribute();
return nAttrs;
}
private int generatePermittedTypeAttributes() {
SourceTypeBinding type = this.referenceBinding;
int localContentsOffset = this.contentsOffset;
ReferenceBinding[] permittedTypes = type.permittedTypes();
int l = permittedTypes != null ? permittedTypes.length : 0;
if (l == 0)
return 0;
int exSize = 8 + 2 * l;
if (exSize + localContentsOffset >= this.contents.length) {
resizeContents(exSize);
}
int attributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.PermittedSubclasses);
this.contents[localContentsOffset++] = (byte) (attributeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) attributeNameIndex;
int value = (l << 1) + 2;
this.contents[localContentsOffset++] = (byte) (value >> 24);
this.contents[localContentsOffset++] = (byte) (value >> 16);
this.contents[localContentsOffset++] = (byte) (value >> 8);
this.contents[localContentsOffset++] = (byte) value;
this.contents[localContentsOffset++] = (byte) (l >> 8);
this.contents[localContentsOffset++] = (byte) l;
for (int i = 0; i < l; i++) {
int permittedTypeIndex = this.constantPool.literalIndexForType(permittedTypes[i]);
this.contents[localContentsOffset++] = (byte) (permittedTypeIndex >> 8);
this.contents[localContentsOffset++] = (byte) permittedTypeIndex;
}
this.contentsOffset = localContentsOffset;
return 1;
}
private int generateRecordAttributes() {
SourceTypeBinding record = this.referenceBinding;
if (record == null || !record.isRecord())
return 0;
int localContentsOffset = this.contentsOffset;
RecordComponentBinding[] recordComponents = this.referenceBinding.components();
if (recordComponents == null)
return 0;
// could be an empty record also, account for zero components as well.
int numberOfRecordComponents = recordComponents.length;
int exSize = 8 + 2 * numberOfRecordComponents;
if (exSize + localContentsOffset >= this.contents.length) {
resizeContents(exSize);
}
/*
* Record_attribute {
* u2 attribute_name_index;
* u4 attribute_length;
* u2 components_count;
* component_info components[components_count];
* }*/
int attributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.RecordClass);
this.contents[localContentsOffset++] = (byte) (attributeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) attributeNameIndex;
int attrLengthOffset = localContentsOffset;
localContentsOffset += 4;
int base = localContentsOffset;
this.contents[localContentsOffset++] = (byte) (numberOfRecordComponents >> 8);
this.contents[localContentsOffset++] = (byte) numberOfRecordComponents;
this.contentsOffset = localContentsOffset;
for (int i = 0; i < numberOfRecordComponents; i++) {
addComponentInfo(recordComponents[i]);
}
int attrLength = this.contentsOffset - base;
this.contents[attrLengthOffset++] = (byte) (attrLength >> 24);
this.contents[attrLengthOffset++] = (byte) (attrLength >> 16);
this.contents[attrLengthOffset++] = (byte) (attrLength >> 8);
this.contents[attrLengthOffset++] = (byte) attrLength;
return 1;
}
private int generateModuleAttribute(ModuleDeclaration module) {
ModuleBinding binding = module.binding;
int localContentsOffset = this.contentsOffset;
if (localContentsOffset + 10 >= this.contents.length) {
resizeContents(10);
}
int moduleAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.ModuleName);
this.contents[localContentsOffset++] = (byte) (moduleAttributeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) moduleAttributeNameIndex;
int attrLengthOffset = localContentsOffset;
localContentsOffset += 4;
int moduleNameIndex =
this.constantPool.literalIndexForModule(binding.moduleName);
this.contents[localContentsOffset++] = (byte) (moduleNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) moduleNameIndex;
int flags = module.modifiers & ~(ClassFileConstants.AccModule);
this.contents[localContentsOffset++] = (byte) (flags >> 8);
this.contents[localContentsOffset++] = (byte) flags;
String moduleVersion = module.getModuleVersion();
int module_version_idx = moduleVersion == null ? 0 : this.constantPool.literalIndex(moduleVersion.toCharArray());
this.contents[localContentsOffset++] = (byte) (module_version_idx >> 8);
this.contents[localContentsOffset++] = (byte) module_version_idx;
int attrLength = 6;
// ================= requires section =================
/** u2 requires_count;
{ u2 requires_index;
u2 requires_flags;
} requires[requires_count];
**/
int requiresCountOffset = localContentsOffset;
int requiresCount = module.requiresCount;
int requiresSize = 2 + requiresCount * 6;
if (localContentsOffset + requiresSize >= this.contents.length) {
resizeContents(requiresSize);
}
localContentsOffset += 2;
ModuleBinding javaBaseBinding = null;
for(int i = 0; i < module.requiresCount; i++) {
RequiresStatement req = module.requires[i];
ModuleBinding reqBinding = req.resolvedBinding;
if (CharOperation.equals(reqBinding.moduleName, TypeConstants.JAVA_BASE)) {
javaBaseBinding = reqBinding;
}
int nameIndex = this.constantPool.literalIndexForModule(reqBinding.moduleName);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) (nameIndex);
flags = req.modifiers;
this.contents[localContentsOffset++] = (byte) (flags >> 8);
this.contents[localContentsOffset++] = (byte) (flags);
int required_version = 0;
this.contents[localContentsOffset++] = (byte) (required_version >> 8);
this.contents[localContentsOffset++] = (byte) (required_version);
}
if (!CharOperation.equals(binding.moduleName, TypeConstants.JAVA_BASE) && javaBaseBinding == null) {
if (localContentsOffset + 6 >= this.contents.length) {
resizeContents(6);
}
javaBaseBinding = binding.environment.javaBaseModule();
int javabase_index = this.constantPool.literalIndexForModule(javaBaseBinding.moduleName);
this.contents[localContentsOffset++] = (byte) (javabase_index >> 8);
this.contents[localContentsOffset++] = (byte) (javabase_index);
flags = ClassFileConstants.AccMandated;
this.contents[localContentsOffset++] = (byte) (flags >> 8);
this.contents[localContentsOffset++] = (byte) flags;
int required_version = 0;
this.contents[localContentsOffset++] = (byte) (required_version >> 8);
this.contents[localContentsOffset++] = (byte) (required_version);
requiresCount++;
}
this.contents[requiresCountOffset++] = (byte) (requiresCount >> 8);
this.contents[requiresCountOffset++] = (byte) requiresCount;
attrLength += 2 + 6 * requiresCount;
// ================= end requires section =================
// ================= exports section =================
/**
* u2 exports_count;
* { u2 exports_index;
* u2 exports_flags;
* u2 exports_to_count;
* u2 exports_to_index[exports_to_count];
* } exports[exports_count];
*/
int exportsSize = 2 + module.exportsCount * 6;
if (localContentsOffset + exportsSize >= this.contents.length) {
resizeContents(exportsSize);
}
this.contents[localContentsOffset++] = (byte) (module.exportsCount >> 8);
this.contents[localContentsOffset++] = (byte) module.exportsCount;
for (int i = 0; i < module.exportsCount; i++) {
ExportsStatement ref = module.exports[i];
if (localContentsOffset + 6 >= this.contents.length) {
resizeContents((module.exportsCount - i) * 6);
}
int nameIndex = this.constantPool.literalIndexForPackage(CharOperation.replaceOnCopy(ref.pkgName, '.', '/'));
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) (nameIndex);
// TODO exports_flags - check when they are set
this.contents[localContentsOffset++] = (byte) 0;
this.contents[localContentsOffset++] = (byte) 0;
int exportsToCount = ref.isQualified() ? ref.targets.length : 0;
this.contents[localContentsOffset++] = (byte) (exportsToCount >> 8);
this.contents[localContentsOffset++] = (byte) (exportsToCount);
if (exportsToCount > 0) {
int targetSize = 2 * exportsToCount;
if (localContentsOffset + targetSize >= this.contents.length) {
resizeContents(targetSize);
}
for(int j = 0; j < exportsToCount; j++) {
nameIndex = this.constantPool.literalIndexForModule(ref.targets[j].moduleName);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) (nameIndex);
}
attrLength += targetSize;
}
}
attrLength += exportsSize;
// ================= end exports section =================
// ================= opens section =================
/**
* u2 opens_count;
* { u2 opens_index;
* u2 opens_flags;
* u2 opens_to_count;
* u2 opens_to_index[opens_to_count];
* } exports[exports_count];
*/
int opensSize = 2 + module.opensCount * 6;
if (localContentsOffset + opensSize >= this.contents.length) {
resizeContents(opensSize);
}
this.contents[localContentsOffset++] = (byte) (module.opensCount >> 8);
this.contents[localContentsOffset++] = (byte) module.opensCount;
for (int i = 0; i < module.opensCount; i++) {
OpensStatement ref = module.opens[i];
if (localContentsOffset + 6 >= this.contents.length) {
resizeContents((module.opensCount - i) * 6);
}
int nameIndex = this.constantPool.literalIndexForPackage(CharOperation.replaceOnCopy(ref.pkgName, '.', '/'));
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) (nameIndex);
// TODO opens_flags - check when they are set
this.contents[localContentsOffset++] = (byte) 0;
this.contents[localContentsOffset++] = (byte) 0;
int opensToCount = ref.isQualified() ? ref.targets.length : 0;
this.contents[localContentsOffset++] = (byte) (opensToCount >> 8);
this.contents[localContentsOffset++] = (byte) (opensToCount);
if (opensToCount > 0) {
int targetSize = 2 * opensToCount;
if (localContentsOffset + targetSize >= this.contents.length) {
resizeContents(targetSize);
}
for(int j = 0; j < opensToCount; j++) {
nameIndex = this.constantPool.literalIndexForModule(ref.targets[j].moduleName);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) (nameIndex);
}
attrLength += targetSize;
}
}
attrLength += opensSize;
// ================= end opens section =================
// ================= uses section =================
/**
* u2 uses_count;
* u2 uses_index[uses_count];
*/
int usesSize = 2 + 2 * module.usesCount;
if (localContentsOffset + usesSize >= this.contents.length) {
resizeContents(usesSize);
}
this.contents[localContentsOffset++] = (byte) (module.usesCount >> 8);
this.contents[localContentsOffset++] = (byte) module.usesCount;
for(int i = 0; i < module.usesCount; i++) {
int nameIndex = this.constantPool.literalIndexForType(module.uses[i].serviceInterface.resolvedType.constantPoolName());
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) (nameIndex);
}
attrLength += usesSize;
// ================= end uses section =================
// ================= provides section =================
/**
* u2 provides_count;
* {
* u2 provides_index;
* u2 provides_with_count;
* u2 provides_with_index[provides_with_count];
* } provides[provides_count];
*/
int servicesSize = 2 + 4 * module.servicesCount;
if (localContentsOffset + servicesSize >= this.contents.length) {
resizeContents(servicesSize);
}
this.contents[localContentsOffset++] = (byte) (module.servicesCount >> 8);
this.contents[localContentsOffset++] = (byte) module.servicesCount;
for(int i = 0; i < module.servicesCount; i++) {
if (localContentsOffset + 4 >= this.contents.length) {
resizeContents((module.servicesCount - i) * 4);
}
int nameIndex = this.constantPool.literalIndexForType(module.services[i].serviceInterface.resolvedType.constantPoolName());
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) (nameIndex);
TypeReference[] impls = module.services[i].implementations;
int implLength = impls.length;
this.contents[localContentsOffset++] = (byte) (implLength >> 8);
this.contents[localContentsOffset++] = (byte) implLength;
int targetSize = implLength * 2;
if (localContentsOffset + targetSize >= this.contents.length) {
resizeContents(targetSize);
}
for (int j = 0; j < implLength; j++) {
nameIndex = this.constantPool.literalIndexForType(impls[j].resolvedType.constantPoolName());
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) (nameIndex);
}
attrLength += targetSize;
}
attrLength += servicesSize;
// ================= end provides section =================
this.contents[attrLengthOffset++] = (byte)(attrLength >> 24);
this.contents[attrLengthOffset++] = (byte)(attrLength >> 16);
this.contents[attrLengthOffset++] = (byte)(attrLength >> 8);
this.contents[attrLengthOffset++] = (byte)attrLength;
this.contentsOffset = localContentsOffset;
return 1;
}
private int generateModuleMainClassAttribute(char[] moduleMainClass) {
int localContentsOffset = this.contentsOffset;
if (localContentsOffset + 8 >= this.contents.length) {
resizeContents(8);
}
int moduleAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.ModuleMainClass);
this.contents[localContentsOffset++] = (byte) (moduleAttributeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) moduleAttributeNameIndex;
int attrLength = 2;
this.contents[localContentsOffset++] = (byte)(attrLength >> 24);
this.contents[localContentsOffset++] = (byte)(attrLength >> 16);
this.contents[localContentsOffset++] = (byte)(attrLength >> 8);
this.contents[localContentsOffset++] = (byte)attrLength;
int moduleNameIndex = this.constantPool.literalIndexForType(moduleMainClass);
this.contents[localContentsOffset++] = (byte) (moduleNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) moduleNameIndex;
this.contentsOffset = localContentsOffset;
return 1;
}
private int generateModulePackagesAttribute(char[][] packageNames) {
int localContentsOffset = this.contentsOffset;
int maxSize = 6 + 2*packageNames.length;
if (localContentsOffset + maxSize >= this.contents.length) {
resizeContents(maxSize);
}
int moduleAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.ModulePackages);
this.contents[localContentsOffset++] = (byte) (moduleAttributeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) moduleAttributeNameIndex;
int attrLengthOffset = localContentsOffset;
localContentsOffset+= 4;
int packageCountOffset = localContentsOffset;
localContentsOffset+= 2;
int packagesCount = 0;
for (char[] packageName : packageNames) {
if (packageName == null || packageName.length == 0) continue;
int packageNameIndex = this.constantPool.literalIndexForPackage(packageName);
this.contents[localContentsOffset++] = (byte) (packageNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) packageNameIndex;
packagesCount++;
}
this.contents[packageCountOffset++] = (byte)(packagesCount >> 8);
this.contents[packageCountOffset++] = (byte)packagesCount;
int attrLength = 2 + 2 * packagesCount;
this.contents[attrLengthOffset++] = (byte)(attrLength >> 24);
this.contents[attrLengthOffset++] = (byte)(attrLength >> 16);
this.contents[attrLengthOffset++] = (byte)(attrLength >> 8);
this.contents[attrLengthOffset++] = (byte)attrLength;
this.contentsOffset = localContentsOffset;
return 1;
}
private void generateElementValue(
Expression defaultValue,
TypeBinding memberValuePairReturnType,
int attributeOffset) {
Constant constant = defaultValue.constant;
TypeBinding defaultValueBinding = defaultValue.resolvedType;
if (defaultValueBinding == null) {
this.contentsOffset = attributeOffset;
} else {
if (defaultValueBinding.isMemberType()) {
this.recordInnerClasses(defaultValueBinding);
}
if (memberValuePairReturnType.isMemberType()) {
this.recordInnerClasses(memberValuePairReturnType);
}
if (memberValuePairReturnType.isArrayType() && !defaultValueBinding.isArrayType()) {
// automatic wrapping
if (this.contentsOffset + 3 >= this.contents.length) {
resizeContents(3);
}
this.contents[this.contentsOffset++] = (byte) '[';
this.contents[this.contentsOffset++] = (byte) 0;
this.contents[this.contentsOffset++] = (byte) 1;
}
if (constant != null && constant != Constant.NotAConstant) {
generateElementValue(attributeOffset, defaultValue, constant, memberValuePairReturnType.leafComponentType());
} else {
generateElementValueForNonConstantExpression(defaultValue, attributeOffset, defaultValueBinding);
}
}
}
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()) {
ReferenceBinding superClass = innerClass.superclass();
if (superClass == null || !(superClass.isEnum() && superClass.isSealed()))
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 Map createInitBootStrapMethodsMap() {
Map fPtr = new HashMap<>(ClassFile.BOOTSTRAP_METHODS.length);
for (String key : ClassFile.BOOTSTRAP_METHODS) {
fPtr.put(key, 0);
}
return fPtr;
}
private int generateBootstrapMethods(List bootStrapMethodsList) {
/* 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
int numberOfBootstraps = bootStrapMethodsList != null ? bootStrapMethodsList.size() : 0;
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;
Map fPtr = createInitBootStrapMethodsMap();
for (int i = 0; i < numberOfBootstraps; i++) {
Object o = this.bootstrapMethods.get(i);
if (o instanceof FunctionalExpression) {
localContentsOffset = addBootStrapLambdaEntry(localContentsOffset, (FunctionalExpression) o, fPtr);
} else if (o instanceof TypeDeclaration) {
localContentsOffset = addBootStrapRecordEntry(localContentsOffset, (TypeDeclaration) o, fPtr);
} else if (o instanceof SwitchStatement) {
SwitchStatement stmt = (SwitchStatement) o;
if (stmt.expression.resolvedType.isEnum()) {
localContentsOffset = addBootStrapEnumSwitchEntry(localContentsOffset, stmt, fPtr);
} else {
localContentsOffset = addBootStrapTypeSwitchEntry(localContentsOffset, stmt, fPtr);
}
} else if (o instanceof String) {
localContentsOffset = addBootStrapStringConcatEntry(localContentsOffset, (String) o, fPtr);
} else if (o instanceof ResolvedCase) {
localContentsOffset = addBootStrapTypeCaseConstantEntry(localContentsOffset, (ResolvedCase) o, fPtr);
} else if (o instanceof TypeBinding) {
localContentsOffset = addClassDescBootstrap(localContentsOffset, (TypeBinding) o, fPtr);
} else if (o instanceof StringTemplate template) {
localContentsOffset = addBootStrapTemplateRuntimeEntry(localContentsOffset, template, fPtr);
}
}
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 addBootStrapLambdaEntry(int localContentsOffset, FunctionalExpression functional, Map fPtr) {
MethodBinding [] bridges = functional.getRequiredBridges();
TypeBinding[] markerInterfaces = null;
final int contentsEntries = 10;
int indexForAltMetaFactory = fPtr.get(ClassFile.ALTMETAFACTORY_STRING);
int indexForMetaFactory = fPtr.get(ClassFile.METAFACTORY_STRING);
if ((functional instanceof LambdaExpression
&& (((markerInterfaces = ((LambdaExpression) functional).getMarkerInterfaces()) != null))
|| bridges != null) || functional.isSerializable) {
// may need even more space
int extraSpace = 2; // at least 2 more than when the normal metafactory is used, for the bitflags entry
if (markerInterfaces != null) {
// 2 for the marker interface list size then 2 per marker interface index
extraSpace += (2 + 2 * markerInterfaces.length);
}
if (bridges != null) {
// 2 for bridge count then 2 per bridge method type.
extraSpace += (2 + 2 * bridges.length);
}
if (extraSpace + contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(extraSpace + contentsEntries);
}
if (indexForAltMetaFactory == 0) {
ReferenceBinding javaLangInvokeLambdaMetafactory = this.referenceBinding.scope.getJavaLangInvokeLambdaMetafactory();
indexForAltMetaFactory =
this.constantPool.literalIndexForMethodHandle(ClassFileConstants.MethodHandleRefKindInvokeStatic, javaLangInvokeLambdaMetafactory,
ConstantPool.ALTMETAFACTORY, ConstantPool.JAVA_LANG_INVOKE_LAMBDAMETAFACTORY_ALTMETAFACTORY_SIGNATURE, false);
fPtr.put(ClassFile.ALTMETAFACTORY_STRING, indexForAltMetaFactory);
}
this.contents[localContentsOffset++] = (byte) (indexForAltMetaFactory >> 8);
this.contents[localContentsOffset++] = (byte) indexForAltMetaFactory;
// u2 num_bootstrap_arguments
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (4 + (markerInterfaces==null?0:1+markerInterfaces.length) +
(bridges == null ? 0 : 1 + bridges.length));
int functionalDescriptorIndex = this.constantPool.literalIndexForMethodType(functional.descriptor.original().signature());
this.contents[localContentsOffset++] = (byte) (functionalDescriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) functionalDescriptorIndex;
int methodHandleIndex = this.constantPool.literalIndexForMethodHandle(functional.binding.original()); // Speak of " implementation" (erased) version here, adaptations described below.
this.contents[localContentsOffset++] = (byte) (methodHandleIndex >> 8);
this.contents[localContentsOffset++] = (byte) methodHandleIndex;
char [] instantiatedSignature = functional.descriptor.signature();
int methodTypeIndex = this.constantPool.literalIndexForMethodType(instantiatedSignature);
this.contents[localContentsOffset++] = (byte) (methodTypeIndex >> 8);
this.contents[localContentsOffset++] = (byte) methodTypeIndex;
int bitflags = 0;
if (functional.isSerializable) {
bitflags |= ClassFileConstants.FLAG_SERIALIZABLE;
}
if (markerInterfaces!=null) {
bitflags |= ClassFileConstants.FLAG_MARKERS;
}
if (bridges != null) {
bitflags |= ClassFileConstants.FLAG_BRIDGES;
}
int indexForBitflags = this.constantPool.literalIndex(bitflags);
this.contents[localContentsOffset++] = (byte)(indexForBitflags>>8);
this.contents[localContentsOffset++] = (byte)(indexForBitflags);
if (markerInterfaces != null) {
int markerInterfaceCountIndex = this.constantPool.literalIndex(markerInterfaces.length);
this.contents[localContentsOffset++] = (byte)(markerInterfaceCountIndex>>8);
this.contents[localContentsOffset++] = (byte)(markerInterfaceCountIndex);
for (int m = 0, maxm = markerInterfaces.length; m < maxm; m++) {
int classTypeIndex = this.constantPool.literalIndexForType(markerInterfaces[m]);
this.contents[localContentsOffset++] = (byte)(classTypeIndex>>8);
this.contents[localContentsOffset++] = (byte)(classTypeIndex);
}
}
if (bridges != null) {
int bridgeCountIndex = this.constantPool.literalIndex(bridges.length);
this.contents[localContentsOffset++] = (byte) (bridgeCountIndex >> 8);
this.contents[localContentsOffset++] = (byte) (bridgeCountIndex);
for (int m = 0, maxm = bridges.length; m < maxm; m++) {
char [] bridgeSignature = bridges[m].signature();
int bridgeMethodTypeIndex = this.constantPool.literalIndexForMethodType(bridgeSignature);
this.contents[localContentsOffset++] = (byte) (bridgeMethodTypeIndex >> 8);
this.contents[localContentsOffset++] = (byte) bridgeMethodTypeIndex;
}
}
} else {
if (contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(contentsEntries);
}
if (indexForMetaFactory == 0) {
ReferenceBinding javaLangInvokeLambdaMetafactory = this.referenceBinding.scope.getJavaLangInvokeLambdaMetafactory();
indexForMetaFactory = this.constantPool.literalIndexForMethodHandle(ClassFileConstants.MethodHandleRefKindInvokeStatic, javaLangInvokeLambdaMetafactory,
ConstantPool.METAFACTORY, ConstantPool.JAVA_LANG_INVOKE_LAMBDAMETAFACTORY_METAFACTORY_SIGNATURE, false);
fPtr.put(ClassFile.METAFACTORY_STRING, indexForMetaFactory);
}
this.contents[localContentsOffset++] = (byte) (indexForMetaFactory >> 8);
this.contents[localContentsOffset++] = (byte) indexForMetaFactory;
// u2 num_bootstrap_arguments
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) 3;
int functionalDescriptorIndex = this.constantPool.literalIndexForMethodType(functional.descriptor.original().signature());
this.contents[localContentsOffset++] = (byte) (functionalDescriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) functionalDescriptorIndex;
int methodHandleIndex = this.constantPool.literalIndexForMethodHandle(functional.binding instanceof PolymorphicMethodBinding ? functional.binding : functional.binding.original()); // Speak of " implementation" (erased) version here, adaptations described below.
this.contents[localContentsOffset++] = (byte) (methodHandleIndex >> 8);
this.contents[localContentsOffset++] = (byte) methodHandleIndex;
char [] instantiatedSignature = functional.descriptor.signature();
int methodTypeIndex = this.constantPool.literalIndexForMethodType(instantiatedSignature);
this.contents[localContentsOffset++] = (byte) (methodTypeIndex >> 8);
this.contents[localContentsOffset++] = (byte) methodTypeIndex;
}
return localContentsOffset;
}
private int addBootStrapRecordEntry(int localContentsOffset, TypeDeclaration typeDecl, Map fPtr) {
TypeBinding type = typeDecl.binding;
assert type.isRecord(); // sanity check
final int contentsEntries = 10;
int indexForObjectMethodBootStrap = fPtr.get(ClassFile.BOOTSTRAP_STRING);
if (contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(contentsEntries);
}
if (indexForObjectMethodBootStrap == 0) {
ReferenceBinding javaLangRuntimeObjectMethods = this.referenceBinding.scope.getJavaLangRuntimeObjectMethods();
indexForObjectMethodBootStrap = this.constantPool.literalIndexForMethodHandle(ClassFileConstants.MethodHandleRefKindInvokeStatic, javaLangRuntimeObjectMethods,
ConstantPool.BOOTSTRAP, ConstantPool.JAVA_LANG_RUNTIME_OBJECTMETHOD_BOOTSTRAP_SIGNATURE, false);
fPtr.put(ClassFile.BOOTSTRAP_STRING, indexForObjectMethodBootStrap);
}
this.contents[localContentsOffset++] = (byte) (indexForObjectMethodBootStrap >> 8);
this.contents[localContentsOffset++] = (byte) indexForObjectMethodBootStrap;
// u2 num_bootstrap_arguments
int numArgsLocation = localContentsOffset;
localContentsOffset += 2;
char[] recordName = type.constantPoolName();
int recordIndex = this.constantPool.literalIndexForType(recordName);
this.contents[localContentsOffset++] = (byte) (recordIndex >> 8);
this.contents[localContentsOffset++] = (byte) recordIndex;
assert type instanceof SourceTypeBinding;
SourceTypeBinding sourceType = (SourceTypeBinding) type;
FieldBinding[] recordComponents = sourceType.getImplicitComponentFields();
int numArgs = 2 + recordComponents.length;
this.contents[numArgsLocation++] = (byte) (numArgs >> 8);
this.contents[numArgsLocation] = (byte) numArgs;
String names =
Arrays.stream(recordComponents)
.map(f -> new String(f.name))
.reduce((s1, s2) -> (s1 + ";" + s2)) //$NON-NLS-1$
.orElse(Util.EMPTY_STRING);
int namesIndex = this.constantPool.literalIndex(names);
this.contents[localContentsOffset++] = (byte) (namesIndex >> 8);
this.contents[localContentsOffset++] = (byte) namesIndex;
if (recordComponents.length * 2 + localContentsOffset >= this.contents.length) {
resizeContents(recordComponents.length * 2);
}
for (FieldBinding field : recordComponents) {
int methodHandleIndex = this.constantPool.literalIndexForMethodHandleFieldRef(
ClassFileConstants.MethodHandleRefKindGetField,
recordName, field.name, field.type.signature());
this.contents[localContentsOffset++] = (byte) (methodHandleIndex >> 8);
this.contents[localContentsOffset++] = (byte) methodHandleIndex;
}
return localContentsOffset;
}
private int addBootStrapTypeCaseConstantEntry(int localContentsOffset, ResolvedCase caseConstant, Map fPtr) {
final int contentsEntries = 10;
if (contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(contentsEntries);
}
int idx = fPtr.get(ClassFile.INVOKE_STRING);
if (idx == 0) {
ReferenceBinding constantBootstrap = this.referenceBinding.scope.getJavaLangInvokeConstantBootstraps();
idx = this.constantPool.literalIndexForMethodHandle(
ClassFileConstants.MethodHandleRefKindInvokeStatic,
constantBootstrap,
ConstantPool.INVOKE_METHOD_METHOD_NAME,
ConstantPool.JAVA_LANG_INVOKE_CONSTANTBOOTSTRAP_SIGNATURE,
false);
fPtr.put(ClassFile.INVOKE_STRING, idx);
}
this.contents[localContentsOffset++] = (byte) (idx >> 8);
this.contents[localContentsOffset++] = (byte) idx;
// u2 num_bootstrap_arguments
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) 3;
idx = fPtr.get(ClassFile.ENUMDESC_OF);
if (idx == 0) {
ReferenceBinding enumDesc = this.referenceBinding.scope.getJavaLangEnumDesc();
idx = this.constantPool.literalIndexForMethodHandle(
ClassFileConstants.MethodHandleRefKindInvokeStatic,
enumDesc,
"of".toCharArray(), //$NON-NLS-1$
ConstantPool.JAVA_LANG_ENUMDESC_OF_SIGNATURE,
false);
fPtr.put(ClassFile.ENUMDESC_OF, idx);
}
this.contents[localContentsOffset++] = (byte) (idx >> 8);
this.contents[localContentsOffset++] = (byte) idx;
idx = this.constantPool.literalIndexForDynamic(
caseConstant.classDescIdx,
ConstantPool.INVOKE_METHOD_METHOD_NAME,
ConstantPool.JAVA_LANG_CONST_CLASSDESC);
this.contents[localContentsOffset++] = (byte) (idx >> 8);
this.contents[localContentsOffset++] = (byte) idx;
idx = this.constantPool.literalIndex(caseConstant.c.stringValue());
this.contents[localContentsOffset++] = (byte) (idx >> 8);
this.contents[localContentsOffset++] = (byte) idx;
return localContentsOffset;
}
private int addClassDescBootstrap(int localContentsOffset, TypeBinding type, Map fPtr) {
final int contentsEntries = 10;
int idx = fPtr.get(ClassFile.INVOKE_STRING);
if (contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(contentsEntries);
}
if (idx == 0) {
ReferenceBinding constantBootstrap = this.referenceBinding.scope.getJavaLangInvokeConstantBootstraps();
idx = this.constantPool.literalIndexForMethodHandle(
ClassFileConstants.MethodHandleRefKindInvokeStatic,
constantBootstrap,
ConstantPool.INVOKE_METHOD_METHOD_NAME,
ConstantPool.JAVA_LANG_INVOKE_CONSTANTBOOTSTRAP_SIGNATURE,
false);
fPtr.put(ClassFile.INVOKE_STRING, idx);
}
this.contents[localContentsOffset++] = (byte) (idx >> 8);
this.contents[localContentsOffset++] = (byte) idx;
// u2 num_bootstrap_arguments
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) 2;
idx = fPtr.get(ClassFile.CLASSDESC);
if (idx == 0) {
ReferenceBinding classDesc = this.referenceBinding.scope.getJavaLangClassDesc();
idx = this.constantPool.literalIndexForMethodHandle(
ClassFileConstants.MethodHandleRefKindInvokeStatic,
classDesc,
"of".toCharArray(), //$NON-NLS-1$
ConstantPool.JAVA_LANG_CLASSDESC_OF_SIGNATURE,
true);
fPtr.put(ClassFile.CLASSDESC_OF, idx);
}
this.contents[localContentsOffset++] = (byte) (idx >> 8);
this.contents[localContentsOffset++] = (byte) idx;
ReferenceBinding refBinding = (ReferenceBinding) type;
idx = this.constantPool.literalIndex(CharOperation.toString(refBinding.compoundName));
this.contents[localContentsOffset++] = (byte) (idx >> 8);
this.contents[localContentsOffset++] = (byte) idx;
return localContentsOffset;
}
private int addBootStrapTypeSwitchEntry(int localContentsOffset, SwitchStatement switchStatement, Map fPtr) {
CaseStatement.ResolvedCase[] constants = switchStatement.otherConstants;
int numArgs = constants.length;
final int contentsEntries = 10 + (numArgs * 2);
int indexFortypeSwitch = fPtr.get(ClassFile.TYPESWITCH_STRING);
if (contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(contentsEntries);
}
if (indexFortypeSwitch == 0) {
ReferenceBinding javaLangRuntimeSwitchBootstraps = this.referenceBinding.scope.getJavaLangRuntimeSwitchBootstraps();
indexFortypeSwitch = this.constantPool.literalIndexForMethodHandle(ClassFileConstants.MethodHandleRefKindInvokeStatic, javaLangRuntimeSwitchBootstraps,
ConstantPool.TYPESWITCH, ConstantPool.JAVA_LANG_RUNTIME_SWITCHBOOTSTRAPS_SWITCH_SIGNATURE, false);
fPtr.put(ClassFile.TYPESWITCH_STRING, indexFortypeSwitch);
}
this.contents[localContentsOffset++] = (byte) (indexFortypeSwitch >> 8);
this.contents[localContentsOffset++] = (byte) indexFortypeSwitch;
// u2 num_bootstrap_arguments
int numArgsLocation = localContentsOffset;
if (switchStatement.containsNull) --numArgs;
this.contents[numArgsLocation++] = (byte) (numArgs >> 8);
this.contents[numArgsLocation] = (byte) numArgs;
localContentsOffset += 2;
for (CaseStatement.ResolvedCase c : constants) {
if (c.isPattern()) {
char[] typeName = c.t.constantPoolName();
int typeIndex = this.constantPool.literalIndexForType(typeName);
this.contents[localContentsOffset++] = (byte) (typeIndex >> 8);
this.contents[localContentsOffset++] = (byte) typeIndex;
} else if (c.isQualifiedEnum()){
int typeIndex = this.constantPool.literalIndexForDynamic(c.enumDescIdx,
ConstantPool.INVOKE_METHOD_METHOD_NAME,
ConstantPool.JAVA_LANG_ENUM_ENUMDESC);
this.contents[localContentsOffset++] = (byte) (typeIndex >> 8);
this.contents[localContentsOffset++] = (byte) typeIndex;
} else if ((c.e instanceof StringLiteral)||(c.c instanceof StringConstant)) {
int intValIdx =
this.constantPool.literalIndex(c.c.stringValue());
this.contents[localContentsOffset++] = (byte) (intValIdx >> 8);
this.contents[localContentsOffset++] = (byte) intValIdx;
} else {
if (c.e instanceof NullLiteral) continue;
int intValIdx =
this.constantPool.literalIndex(c.intValue());
this.contents[localContentsOffset++] = (byte) (intValIdx >> 8);
this.contents[localContentsOffset++] = (byte) intValIdx;
}
}
return localContentsOffset;
}
private int addBootStrapEnumSwitchEntry(int localContentsOffset, SwitchStatement switchStatement, Map fPtr) {
final int contentsEntries = 10;
int indexForenumSwitch = fPtr.get(ClassFile.ENUMSWITCH_STRING);
if (contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(contentsEntries);
}
if (indexForenumSwitch == 0) {
ReferenceBinding javaLangRuntimeSwitchBootstraps = this.referenceBinding.scope.getJavaLangRuntimeSwitchBootstraps();
indexForenumSwitch = this.constantPool.literalIndexForMethodHandle(ClassFileConstants.MethodHandleRefKindInvokeStatic, javaLangRuntimeSwitchBootstraps,
ConstantPool.ENUMSWITCH, ConstantPool.JAVA_LANG_RUNTIME_SWITCHBOOTSTRAPS_SWITCH_SIGNATURE, false);
fPtr.put(ClassFile.ENUMSWITCH_STRING, indexForenumSwitch);
}
this.contents[localContentsOffset++] = (byte) (indexForenumSwitch >> 8);
this.contents[localContentsOffset++] = (byte) indexForenumSwitch;
// u2 num_bootstrap_arguments
int numArgsLocation = localContentsOffset;
CaseStatement.ResolvedCase[] constants = switchStatement.otherConstants;
int numArgs = constants.length;
if (switchStatement.containsNull) --numArgs;
this.contents[numArgsLocation++] = (byte) (numArgs >> 8);
this.contents[numArgsLocation] = (byte) numArgs;
localContentsOffset += 2;
for (CaseStatement.ResolvedCase c : constants) {
if (c.isPattern()) {
char[] typeName = switchStatement.expression.resolvedType.constantPoolName();
int typeIndex = this.constantPool.literalIndexForType(typeName);
this.contents[localContentsOffset++] = (byte) (typeIndex >> 8);
this.contents[localContentsOffset++] = (byte) typeIndex;
} else {
if (c.e instanceof NullLiteral) continue;
String s = c.e instanceof QualifiedNameReference qnr ? // handle superfluously qualified enumerator.
new String(qnr.tokens[qnr.tokens.length-1]) : c.e.toString();
int intValIdx =
this.constantPool.literalIndex(s);
this.contents[localContentsOffset++] = (byte) (intValIdx >> 8);
this.contents[localContentsOffset++] = (byte) intValIdx;
}
}
return localContentsOffset;
}
private int addBootStrapStringConcatEntry(int localContentsOffset, String recipe, Map fPtr) {
final int contentsEntries = 10;
int indexForStringConcat = fPtr.get(ClassFile.CONCAT_CONSTANTS);
if (contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(contentsEntries);
}
if (indexForStringConcat == 0) {
ReferenceBinding stringConcatBootstrap = this.referenceBinding.scope.getJavaLangInvokeStringConcatFactory();
indexForStringConcat = this.constantPool.literalIndexForMethodHandle(ClassFileConstants.MethodHandleRefKindInvokeStatic, stringConcatBootstrap,
ConstantPool.ConcatWithConstants, ConstantPool.JAVA_LANG_INVOKE_STRING_CONCAT_FACTORY_SIGNATURE, false);
fPtr.put(ClassFile.CONCAT_CONSTANTS, indexForStringConcat);
}
this.contents[localContentsOffset++] = (byte) (indexForStringConcat >> 8);
this.contents[localContentsOffset++] = (byte) indexForStringConcat;
// u2 num_bootstrap_arguments
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) 1;
int intValIdx =
this.constantPool.literalIndex(recipe);
this.contents[localContentsOffset++] = (byte) (intValIdx >> 8);
this.contents[localContentsOffset++] = (byte) intValIdx;
return localContentsOffset;
}
private int addBootStrapTemplateRuntimeEntry(int localContentsOffset, StringTemplate template, Map fPtr) {
final int contentsEntries = 10;
int indexForProcess = fPtr.get(NEW_STRING_TEMPLATE);
if (contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(contentsEntries);
}
if (indexForProcess == 0) {
ReferenceBinding javaLangRuntimeTemplateBootstraps = this.referenceBinding.scope.getJavaLangRuntimeTemplateRuntimeBootstraps();
indexForProcess = this.constantPool.literalIndexForMethodHandle(ClassFileConstants.MethodHandleRefKindInvokeStatic, javaLangRuntimeTemplateBootstraps,
NEW_STRING_TEMPLATE.toCharArray(), ConstantPool.JAVA_LANG_RUNTIME_STRING_TEMPLATE_SIGNATURE, false);
fPtr.put(NEW_STRING_TEMPLATE, indexForProcess);
}
this.contents[localContentsOffset++] = (byte) (indexForProcess >> 8);
this.contents[localContentsOffset++] = (byte) indexForProcess;
// u2 num_bootstrap_arguments
int numArgsLocation = localContentsOffset;
StringLiteral[] fragments = template.fragments();
int numArgs = fragments.length;
this.contents[numArgsLocation++] = (byte) (numArgs >> 8);
this.contents[numArgsLocation] = (byte) numArgs;
localContentsOffset += 2;
if ((numArgs * 2) + localContentsOffset >= this.contents.length) {
resizeContents(numArgs * 2);
}
for (StringLiteral frag : fragments) {
int intValIdx =
this.constantPool.literalIndex(frag.constant.stringValue());
this.contents[localContentsOffset++] = (byte) (intValIdx >> 8);
this.contents[localContentsOffset++] = (byte) intValIdx;
}
return localContentsOffset;
}
private int generateLineNumberAttribute() {
int localContentsOffset = this.contentsOffset;
int attributesNumber = 0;
/* Create and add the line number attribute (used for debugging)
* Build the pairs of:
* (bytecodePC lineNumber)
* according to the table of start line indexes and the pcToSourceMap table
* contained into the codestream
*/
int[] pcToSourceMapTable;
if (((pcToSourceMapTable = this.codeStream.pcToSourceMap) != null)
&& (this.codeStream.pcToSourceMapSize != 0)) {
int lineNumberNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName);
if (localContentsOffset + 8 >= this.contents.length) {
resizeContents(8);
}
this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) lineNumberNameIndex;
int lineNumberTableOffset = localContentsOffset;
localContentsOffset += 6;
// leave space for attribute_length and line_number_table_length
int numberOfEntries = 0;
int length = this.codeStream.pcToSourceMapSize;
for (int i = 0; i < length;) {
// write the entry
if (localContentsOffset + 4 >= this.contents.length) {
resizeContents(4);
}
int pc = pcToSourceMapTable[i++];
this.contents[localContentsOffset++] = (byte) (pc >> 8);
this.contents[localContentsOffset++] = (byte) pc;
int lineNumber = pcToSourceMapTable[i++];
this.contents[localContentsOffset++] = (byte) (lineNumber >> 8);
this.contents[localContentsOffset++] = (byte) lineNumber;
numberOfEntries++;
}
// now we change the size of the line number attribute
int lineNumberAttr_length = numberOfEntries * 4 + 2;
this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 24);
this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 16);
this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 8);
this.contents[lineNumberTableOffset++] = (byte) lineNumberAttr_length;
this.contents[lineNumberTableOffset++] = (byte) (numberOfEntries >> 8);
this.contents[lineNumberTableOffset++] = (byte) numberOfEntries;
attributesNumber = 1;
}
this.contentsOffset = localContentsOffset;
return attributesNumber;
}
// this is used for problem and synthetic methods
private int generateLineNumberAttribute(int problemLine) {
int localContentsOffset = this.contentsOffset;
if (localContentsOffset + 12 >= this.contents.length) {
resizeContents(12);
}
/* Create and add the line number attribute (used for debugging)
* Build the pairs of:
* (bytecodePC lineNumber)
* according to the table of start line indexes and the pcToSourceMap table
* contained into the codestream
*/
int lineNumberNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName);
this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) lineNumberNameIndex;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 6;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 1;
// first entry at pc = 0
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (problemLine >> 8);
this.contents[localContentsOffset++] = (byte) problemLine;
// now we change the size of the line number attribute
this.contentsOffset = localContentsOffset;
return 1;
}
private int generateLocalVariableTableAttribute(int code_length, boolean methodDeclarationIsStatic, boolean isSynthetic) {
int attributesNumber = 0;
int localContentsOffset = this.contentsOffset;
int numberOfEntries = 0;
int localVariableNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName);
int maxOfEntries = 8 + 10 * (methodDeclarationIsStatic ? 0 : 1);
for (int i = 0; i < this.codeStream.allLocalsCounter; i++) {
LocalVariableBinding localVariableBinding = this.codeStream.locals[i];
maxOfEntries += 10 * localVariableBinding.initializationCount;
}
// reserve enough space
if (localContentsOffset + maxOfEntries >= this.contents.length) {
resizeContents(maxOfEntries);
}
this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) localVariableNameIndex;
int localVariableTableOffset = localContentsOffset;
// leave space for attribute_length and local_variable_table_length
localContentsOffset += 6;
int nameIndex;
int descriptorIndex;
SourceTypeBinding declaringClassBinding = null;
if (!methodDeclarationIsStatic && !isSynthetic) {
numberOfEntries++;
this.contents[localContentsOffset++] = 0; // the startPC for this is always 0
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (code_length >> 8);
this.contents[localContentsOffset++] = (byte) code_length;
nameIndex = this.constantPool.literalIndex(ConstantPool.This);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
declaringClassBinding = (SourceTypeBinding)
(this.codeStream.methodDeclaration != null ? this.codeStream.methodDeclaration.binding.declaringClass : this.codeStream.lambdaExpression.binding.declaringClass);
descriptorIndex =
this.constantPool.literalIndex(
declaringClassBinding.signature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
this.contents[localContentsOffset++] = 0;// the resolved position for this is always 0
this.contents[localContentsOffset++] = 0;
}
// used to remember the local variable with a generic type
int genericLocalVariablesCounter = 0;
LocalVariableBinding[] genericLocalVariables = null;
int numberOfGenericEntries = 0;
for (int i = 0, max = this.codeStream.allLocalsCounter; i < max; i++) {
LocalVariableBinding localVariable = this.codeStream.locals[i];
int initializationCount = localVariable.initializationCount;
if (initializationCount == 0) continue;
if (localVariable.declaration == null) continue;
final TypeBinding localVariableTypeBinding = localVariable.type;
boolean isParameterizedType = localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable();
if (isParameterizedType) {
if (genericLocalVariables == null) {
// we cannot have more than max locals
genericLocalVariables = new LocalVariableBinding[max];
}
genericLocalVariables[genericLocalVariablesCounter++] = localVariable;
}
for (int j = 0; j < initializationCount; j++) {
int startPC = localVariable.initializationPCs[j << 1];
int endPC = localVariable.initializationPCs[(j << 1) + 1];
if (startPC != endPC) { // only entries for non zero length
if (endPC == -1) {
localVariable.declaringScope.problemReporter().abortDueToInternalError(
Messages.bind(Messages.abort_invalidAttribute, new String(localVariable.name)),
(ASTNode) localVariable.declaringScope.methodScope().referenceContext);
}
if (isParameterizedType) {
numberOfGenericEntries++;
}
// now we can safely add the local entry
numberOfEntries++;
this.contents[localContentsOffset++] = (byte) (startPC >> 8);
this.contents[localContentsOffset++] = (byte) startPC;
int length = endPC - startPC;
this.contents[localContentsOffset++] = (byte) (length >> 8);
this.contents[localContentsOffset++] = (byte) length;
nameIndex = this.constantPool.literalIndex(localVariable.name);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
descriptorIndex = this.constantPool.literalIndex(localVariableTypeBinding.signature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
int resolvedPosition = localVariable.resolvedPosition;
this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
this.contents[localContentsOffset++] = (byte) resolvedPosition;
}
}
}
int value = numberOfEntries * 10 + 2;
this.contents[localVariableTableOffset++] = (byte) (value >> 24);
this.contents[localVariableTableOffset++] = (byte) (value >> 16);
this.contents[localVariableTableOffset++] = (byte) (value >> 8);
this.contents[localVariableTableOffset++] = (byte) value;
this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8);
this.contents[localVariableTableOffset] = (byte) numberOfEntries;
attributesNumber++;
final boolean currentInstanceIsGeneric =
!methodDeclarationIsStatic
&& declaringClassBinding != null
&& declaringClassBinding.typeVariables != Binding.NO_TYPE_VARIABLES;
if (genericLocalVariablesCounter != 0 || currentInstanceIsGeneric) {
// add the local variable type table attribute
numberOfGenericEntries += (currentInstanceIsGeneric ? 1 : 0);
maxOfEntries = 8 + numberOfGenericEntries * 10;
// reserve enough space
if (localContentsOffset + maxOfEntries >= this.contents.length) {
resizeContents(maxOfEntries);
}
int localVariableTypeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.LocalVariableTypeTableName);
this.contents[localContentsOffset++] = (byte) (localVariableTypeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) localVariableTypeNameIndex;
value = numberOfGenericEntries * 10 + 2;
this.contents[localContentsOffset++] = (byte) (value >> 24);
this.contents[localContentsOffset++] = (byte) (value >> 16);
this.contents[localContentsOffset++] = (byte) (value >> 8);
this.contents[localContentsOffset++] = (byte) value;
this.contents[localContentsOffset++] = (byte) (numberOfGenericEntries >> 8);
this.contents[localContentsOffset++] = (byte) numberOfGenericEntries;
if (currentInstanceIsGeneric) {
this.contents[localContentsOffset++] = 0; // the startPC for this is always 0
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (code_length >> 8);
this.contents[localContentsOffset++] = (byte) code_length;
nameIndex = this.constantPool.literalIndex(ConstantPool.This);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
descriptorIndex = this.constantPool.literalIndex(declaringClassBinding.genericTypeSignature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
this.contents[localContentsOffset++] = 0;// the resolved position for this is always 0
this.contents[localContentsOffset++] = 0;
}
for (int i = 0; i < genericLocalVariablesCounter; i++) {
LocalVariableBinding localVariable = genericLocalVariables[i];
for (int j = 0; j < localVariable.initializationCount; j++) {
int startPC = localVariable.initializationPCs[j << 1];
int endPC = localVariable.initializationPCs[(j << 1) + 1];
if (startPC != endPC) {
// only entries for non zero length
// now we can safely add the local entry
this.contents[localContentsOffset++] = (byte) (startPC >> 8);
this.contents[localContentsOffset++] = (byte) startPC;
int length = endPC - startPC;
this.contents[localContentsOffset++] = (byte) (length >> 8);
this.contents[localContentsOffset++] = (byte) length;
nameIndex = this.constantPool.literalIndex(localVariable.name);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
descriptorIndex = this.constantPool.literalIndex(localVariable.type.genericTypeSignature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
int resolvedPosition = localVariable.resolvedPosition;
this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
this.contents[localContentsOffset++] = (byte) resolvedPosition;
}
}
}
attributesNumber++;
}
this.contentsOffset = localContentsOffset;
return attributesNumber;
}
/**
* INTERNAL USE-ONLY
* That method generates the attributes of a code attribute.
* They could be:
* - an exception attribute for each try/catch found inside the method
* - a deprecated attribute
* - a synthetic attribute for synthetic access methods
*
* It returns the number of attributes created for the code attribute.
*
* @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding
* @return int
*/
public int generateMethodInfoAttributes(MethodBinding methodBinding) {
// leave two bytes for the attribute_number
this.contentsOffset += 2;
if (this.contentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
// now we can handle all the attribute for that method info:
// it could be:
// - a CodeAttribute
// - a ExceptionAttribute
// - a DeprecatedAttribute
// - a SyntheticAttribute
// Exception attribute
ReferenceBinding[] thrownsExceptions;
int attributesNumber = 0;
if ((thrownsExceptions = methodBinding.thrownExceptions) != Binding.NO_EXCEPTIONS) {
// The method has a throw clause. So we need to add an exception attribute
// check that there is enough space to write all the bytes for the exception attribute
attributesNumber += generateExceptionsAttribute(thrownsExceptions);
}
if (methodBinding.isDeprecated()) {
// Deprecated attribute
attributesNumber += generateDeprecatedAttribute();
}
if (this.targetJDK < ClassFileConstants.JDK1_5) {
if (methodBinding.isSynthetic()) {
attributesNumber += generateSyntheticAttribute();
}
if (methodBinding.isVarargs()) {
attributesNumber += generateVarargsAttribute();
}
}
// add signature attribute
char[] genericSignature = methodBinding.genericSignature();
if (genericSignature != null) {
attributesNumber += generateSignatureAttribute(genericSignature);
}
if (this.targetJDK >= ClassFileConstants.JDK1_4) {
AbstractMethodDeclaration methodDeclaration = methodBinding.sourceMethod();
if (methodBinding instanceof SyntheticMethodBinding) {
SyntheticMethodBinding syntheticMethod = (SyntheticMethodBinding) methodBinding;
if (syntheticMethod.purpose == SyntheticMethodBinding.SuperMethodAccess && CharOperation.equals(syntheticMethod.selector, syntheticMethod.targetMethod.selector))
methodDeclaration = ((SyntheticMethodBinding)methodBinding).targetMethod.sourceMethod();
if (syntheticMethod.recordComponentBinding != null) {
assert methodDeclaration == null;
long rcMask = TagBits.AnnotationForMethod | TagBits.AnnotationForTypeUse;
// record component (field) accessor method
ReferenceBinding declaringClass = methodBinding.declaringClass;
RecordComponent comp = getRecordComponent(declaringClass, methodBinding.selector);
if (comp != null) {
Annotation[] annotations = ASTNode.getRelevantAnnotations(comp.annotations, rcMask, null);
if (annotations != null) {
assert !methodBinding.isConstructor();
attributesNumber += generateRuntimeAnnotations(annotations, TagBits.AnnotationForMethod);
}
if ((this.produceAttributes & ClassFileConstants.ATTR_TYPE_ANNOTATION) != 0) {
List allTypeAnnotationContexts = new ArrayList<>();
if (annotations != null && (comp.bits & ASTNode.HasTypeAnnotations) != 0) {
comp.getAllAnnotationContexts(AnnotationTargetTypeConstants.METHOD_RETURN, allTypeAnnotationContexts);
}
TypeReference compType = comp.type;
if (compType != null && ((compType.bits & ASTNode.HasTypeAnnotations) != 0)) {
compType.getAllAnnotationContexts(AnnotationTargetTypeConstants.METHOD_RETURN, allTypeAnnotationContexts);
}
int size = allTypeAnnotationContexts.size();
attributesNumber = completeRuntimeTypeAnnotations(attributesNumber,
null,
node -> size > 0,
() -> allTypeAnnotationContexts);
}
}
}
}
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) {
propagateRecordComponentArguments(methodDeclaration);
attributesNumber += generateRuntimeAnnotationsForParameters(arguments);
}
}
} else {
LambdaExpression lambda = methodBinding.sourceLambda();
if (lambda != null) {
if ((methodBinding.tagBits & TagBits.HasParameterAnnotations) != 0) {
Argument[] arguments = lambda.arguments();
if (arguments != null) {
int parameterCount = methodBinding.parameters.length;
int argumentCount = arguments.length;
if (parameterCount > argumentCount) { // synthetics prefixed
int redShift = parameterCount - argumentCount;
System.arraycopy(arguments, 0, arguments = new Argument[parameterCount], redShift, argumentCount);
for (int i = 0; i < redShift; i++)
arguments[i] = new Argument(CharOperation.NO_CHAR, 0, null, 0);
}
attributesNumber += generateRuntimeAnnotationsForParameters(arguments);
}
}
}
}
}
if ((methodBinding.tagBits & TagBits.HasMissingType) != 0) {
this.missingTypes = methodBinding.collectMissingTypes(this.missingTypes);
}
return attributesNumber;
}
private int completeRuntimeTypeAnnotations(int attributesNumber,
ASTNode node,
Predicate condition,
Supplier> supplier) {
int invisibleTypeAnnotationsCounter = 0;
int visibleTypeAnnotationsCounter = 0;
if (condition.test(node)) {
List allTypeAnnotationContexts = supplier.get();
if (allTypeAnnotationContexts.size() > 0) {
AnnotationContext[] allTypeAnnotationContextsArray = new AnnotationContext[allTypeAnnotationContexts.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;
}
private void propagateRecordComponentArguments(AbstractMethodDeclaration methodDeclaration) {
if ((methodDeclaration.bits & (ASTNode.IsCanonicalConstructor | ASTNode.IsImplicit)) == 0)
return;
ReferenceBinding declaringClass = methodDeclaration.binding.declaringClass;
if (declaringClass instanceof SourceTypeBinding) {
assert declaringClass.isRecord();
RecordComponentBinding[] rcbs = ((SourceTypeBinding) declaringClass).components();
Argument[] arguments = methodDeclaration.arguments;
for (int i = 0, length = rcbs.length; i < length; i++) {
RecordComponentBinding rcb = rcbs[i];
RecordComponent recordComponent = rcb.sourceRecordComponent();
if ((recordComponent.bits & ASTNode.HasTypeAnnotations) != 0) {
methodDeclaration.bits |= ASTNode.HasTypeAnnotations;
arguments[i].bits |= ASTNode.HasTypeAnnotations;
}
long rcMask = TagBits.AnnotationForParameter | TagBits.AnnotationForTypeUse;
arguments[i].annotations = ASTNode.getRelevantAnnotations(recordComponent.annotations, rcMask, null);
}
}
}
public int generateMethodInfoAttributes(MethodBinding methodBinding, AnnotationMethodDeclaration declaration) {
int attributesNumber = generateMethodInfoAttributes(methodBinding);
int attributeOffset = this.contentsOffset;
if ((declaration.modifiers & ClassFileConstants.AccAnnotationDefault) != 0) {
// add an annotation default attribute
attributesNumber += generateAnnotationDefaultAttribute(declaration, attributeOffset);
}
return attributesNumber;
}
/**
* INTERNAL USE-ONLY
* That method generates the header of a method info:
* The header consists in:
* - the access flags
* - the name index of the method name inside the constant pool
* - the descriptor index of the signature of the method inside the constant pool.
*
* @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding
*/
public void generateMethodInfoHeader(MethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding, methodBinding.modifiers);
}
/**
* INTERNAL USE-ONLY
* That method generates the header of a method info:
* The header consists in:
* - the access flags
* - the name index of the method name inside the constant pool
* - the descriptor index of the signature of the method inside the constant pool.
*
* @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding
* @param accessFlags the access flags
*/
public void generateMethodInfoHeader(MethodBinding methodBinding, int accessFlags) {
// check that there is enough space to write all the bytes for the method info corresponding
// to the @methodBinding
this.methodCount++; // add one more method
if (this.contentsOffset + 10 >= this.contents.length) {
resizeContents(10);
}
if (this.targetJDK < ClassFileConstants.JDK1_5) {
// pre 1.5, synthetic is an attribute, not a modifier
// pre 1.5, varargs is an attribute, not a modifier (-target jsr14 mode)
accessFlags &= ~(ClassFileConstants.AccSynthetic | ClassFileConstants.AccVarargs);
}
if ((methodBinding.tagBits & TagBits.ClearPrivateModifier) != 0) {
accessFlags &= ~ClassFileConstants.AccPrivate;
}
if (this.targetJDK >= ClassFileConstants.JDK17) {
accessFlags &= ~(ClassFileConstants.AccStrictfp);
}
this.contents[this.contentsOffset++] = (byte) (accessFlags >> 8);
this.contents[this.contentsOffset++] = (byte) accessFlags;
int nameIndex = this.constantPool.literalIndex(methodBinding.selector);
this.contents[this.contentsOffset++] = (byte) (nameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) nameIndex;
int descriptorIndex = this.constantPool.literalIndex(methodBinding.signature(this));
this.contents[this.contentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[this.contentsOffset++] = (byte) descriptorIndex;
}
public void addSyntheticDeserializeLambda(SyntheticMethodBinding methodBinding, SyntheticMethodBinding[] syntheticMethodBindings ) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
// this will add exception attribute, synthetic attribute, deprecated attribute,...
int attributeNumber = generateMethodInfoAttributes(methodBinding);
// Code attribute
int codeAttributeOffset = this.contentsOffset;
attributeNumber++; // add code attribute
generateCodeAttributeHeader();
this.codeStream.init(this);
this.codeStream.generateSyntheticBodyForDeserializeLambda(methodBinding, syntheticMethodBindings);
int code_length = this.codeStream.position;
if (code_length > 65535) {
this.referenceBinding.scope.problemReporter().bytecodeExceeds64KLimit(
methodBinding, this.referenceBinding.sourceStart(), this.referenceBinding.sourceEnd());
}
completeCodeAttributeForSyntheticMethod(
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.getLineSeparatorPositions());
this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[methodAttributeOffset] = (byte) attributeNumber;
}
/**
* INTERNAL USE-ONLY
* That method generates the method info header of a clinit:
* The header consists in:
* - the access flags (always default access + static)
* - the name index of the method name (always {@code }) inside the constant pool
* - the descriptor index of the signature (always ()V) of the method inside the constant pool.
*/
public void generateMethodInfoHeaderForClinit() {
// check that there is enough space to write all the bytes for the method info corresponding
// to the @methodBinding
this.methodCount++; // add one more method
if (this.contentsOffset + 10 >= this.contents.length) {
resizeContents(10);
}
this.contents[this.contentsOffset++] = (byte) ((ClassFileConstants.AccDefault | ClassFileConstants.AccStatic) >> 8);
this.contents[this.contentsOffset++] = (byte) (ClassFileConstants.AccDefault | ClassFileConstants.AccStatic);
int nameIndex = this.constantPool.literalIndex(ConstantPool.Clinit);
this.contents[this.contentsOffset++] = (byte) (nameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) nameIndex;
int descriptorIndex =
this.constantPool.literalIndex(ConstantPool.ClinitSignature);
this.contents[this.contentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[this.contentsOffset++] = (byte) descriptorIndex;
// We know that we won't get more than 1 attribute: the code attribute
this.contents[this.contentsOffset++] = 0;
this.contents[this.contentsOffset++] = 1;
}
/**
* INTERNAL USE-ONLY
* Generate the byte for problem method infos that correspond to missing abstract methods.
* http://dev.eclipse.org/bugs/show_bug.cgi?id=3179
*
* @param methodDeclarations Array of all missing abstract methods
*/
public void generateMissingAbstractMethods(MethodDeclaration[] methodDeclarations, CompilationResult compilationResult) {
if (methodDeclarations != null) {
TypeDeclaration currentDeclaration = this.referenceBinding.scope.referenceContext;
int typeDeclarationSourceStart = currentDeclaration.sourceStart();
int typeDeclarationSourceEnd = currentDeclaration.sourceEnd();
for (int i = 0, max = methodDeclarations.length; i < max; i++) {
MethodDeclaration methodDeclaration = methodDeclarations[i];
MethodBinding methodBinding = methodDeclaration.binding;
String readableName = new String(methodBinding.readableName());
CategorizedProblem[] problems = compilationResult.problems;
int problemsCount = compilationResult.problemCount;
for (int j = 0; j < problemsCount; j++) {
CategorizedProblem problem = problems[j];
if (problem != null
&& problem.getID() == IProblem.AbstractMethodMustBeImplemented
&& problem.getMessage().indexOf(readableName) != -1
&& problem.getSourceStart() >= typeDeclarationSourceStart
&& problem.getSourceEnd() <= typeDeclarationSourceEnd) {
// we found a match
addMissingAbstractProblemMethod(methodDeclaration, methodBinding, problem, compilationResult);
}
}
}
}
}
private void generateMissingTypesAttribute() {
int initialSize = this.missingTypes.size();
int[] missingTypesIndexes = new int[initialSize];
int numberOfMissingTypes = 0;
if (initialSize > 1) {
Collections.sort(this.missingTypes, new Comparator() {
@Override
public int compare(TypeBinding o1, TypeBinding o2) {
return CharOperation.compareTo(o1.constantPoolName(), o2.constantPoolName());
}
});
}
int previousIndex = 0;
next: for (int i = 0; i < initialSize; i++) {
int missingTypeIndex = this.constantPool.literalIndexForType(this.missingTypes.get(i));
if (previousIndex == missingTypeIndex) {
continue next;
}
previousIndex = missingTypeIndex;
missingTypesIndexes[numberOfMissingTypes++] = missingTypeIndex;
}
// we don't need to resize as we interate from 0 to numberOfMissingTypes when recording the indexes in the .class file
int attributeLength = numberOfMissingTypes * 2 + 2;
if (this.contentsOffset + attributeLength + 6 >= this.contents.length) {
resizeContents(attributeLength + 6);
}
int missingTypesNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.MissingTypesName);
this.contents[this.contentsOffset++] = (byte) (missingTypesNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) missingTypesNameIndex;
// generate attribute length
this.contents[this.contentsOffset++] = (byte) (attributeLength >> 24);
this.contents[this.contentsOffset++] = (byte) (attributeLength >> 16);
this.contents[this.contentsOffset++] = (byte) (attributeLength >> 8);
this.contents[this.contentsOffset++] = (byte) attributeLength;
// generate number of missing types
this.contents[this.contentsOffset++] = (byte) (numberOfMissingTypes >> 8);
this.contents[this.contentsOffset++] = (byte) numberOfMissingTypes;
// generate entry for each missing type
for (int i = 0; i < numberOfMissingTypes; i++) {
int missingTypeIndex = missingTypesIndexes[i];
this.contents[this.contentsOffset++] = (byte) (missingTypeIndex >> 8);
this.contents[this.contentsOffset++] = (byte) missingTypeIndex;
}
}
private boolean jdk16packageInfoAnnotation(final long annotationMask, final long targetMask) {
if (this.targetJDK <= ClassFileConstants.JDK1_6 &&
targetMask == TagBits.AnnotationForPackage && annotationMask != 0 &&
(annotationMask & TagBits.AnnotationForPackage) == 0) {
return true;
}
return false;
}
/**
* @param targetMask allowed targets
* @return the number of attributes created while dumping the annotations in the .class file
*/
private int generateRuntimeAnnotations(final Annotation[] annotations, final long targetMask) {
int attributesNumber = 0;
final int length = annotations.length;
int visibleAnnotationsCounter = 0;
int invisibleAnnotationsCounter = 0;
for (int i = 0; i < length; i++) {
Annotation annotation;
if ((annotation = annotations[i].getPersistibleAnnotation()) == null) continue; // already packaged into container.
long annotationMask = annotation.resolvedType != null ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK : 0;
if (annotationMask != 0 && (annotationMask & targetMask) == 0) {
if (!jdk16packageInfoAnnotation(annotationMask, targetMask)) continue;
}
if (annotation.isRuntimeInvisible()) {
invisibleAnnotationsCounter++;
} else if (annotation.isRuntimeVisible()) {
visibleAnnotationsCounter++;
}
}
int annotationAttributeOffset = this.contentsOffset;
if (invisibleAnnotationsCounter != 0) {
if (this.contentsOffset + 10 >= this.contents.length) {
resizeContents(10);
}
int runtimeInvisibleAnnotationsAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.RuntimeInvisibleAnnotationsName);
this.contents[this.contentsOffset++] = (byte) (runtimeInvisibleAnnotationsAttributeNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) runtimeInvisibleAnnotationsAttributeNameIndex;
int attributeLengthOffset = this.contentsOffset;
this.contentsOffset += 4; // leave space for the attribute length
int annotationsLengthOffset = this.contentsOffset;
this.contentsOffset += 2; // leave space for the annotations length
int counter = 0;
loop: for (int i = 0; i < length; i++) {
if (invisibleAnnotationsCounter == 0) break loop;
Annotation annotation;
if ((annotation = annotations[i].getPersistibleAnnotation()) == null) continue; // already packaged into container.
long annotationMask = annotation.resolvedType != null ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK : 0;
if (annotationMask != 0 && (annotationMask & targetMask) == 0) {
if (!jdk16packageInfoAnnotation(annotationMask, targetMask)) continue;
}
if (annotation.isRuntimeInvisible()) {
int currentAnnotationOffset = this.contentsOffset;
generateAnnotation(annotation, currentAnnotationOffset);
invisibleAnnotationsCounter--;
if (this.contentsOffset != currentAnnotationOffset) {
counter++;
}
}
}
if (counter != 0) {
this.contents[annotationsLengthOffset++] = (byte) (counter >> 8);
this.contents[annotationsLengthOffset++] = (byte) counter;
int attributeLength = this.contentsOffset - attributeLengthOffset - 4;
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8);
this.contents[attributeLengthOffset++] = (byte) attributeLength;
attributesNumber++;
} else {
this.contentsOffset = annotationAttributeOffset;
}
}
annotationAttributeOffset = this.contentsOffset;
if (visibleAnnotationsCounter != 0) {
if (this.contentsOffset + 10 >= this.contents.length) {
resizeContents(10);
}
int runtimeVisibleAnnotationsAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.RuntimeVisibleAnnotationsName);
this.contents[this.contentsOffset++] = (byte) (runtimeVisibleAnnotationsAttributeNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) runtimeVisibleAnnotationsAttributeNameIndex;
int attributeLengthOffset = this.contentsOffset;
this.contentsOffset += 4; // leave space for the attribute length
int annotationsLengthOffset = this.contentsOffset;
this.contentsOffset += 2; // leave space for the annotations length
int counter = 0;
loop: for (int i = 0; i < length; i++) {
if (visibleAnnotationsCounter == 0) break loop;
Annotation annotation;
if ((annotation = annotations[i].getPersistibleAnnotation()) == null) continue; // already packaged into container.
long annotationMask = annotation.resolvedType != null ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK : 0;
if (annotationMask != 0 && (annotationMask & targetMask) == 0) {
if (!jdk16packageInfoAnnotation(annotationMask, targetMask)) continue;
}
if (annotation.isRuntimeVisible()) {
visibleAnnotationsCounter--;
int currentAnnotationOffset = this.contentsOffset;
generateAnnotation(annotation, currentAnnotationOffset);
if (this.contentsOffset != currentAnnotationOffset) {
counter++;
}
}
}
if (counter != 0) {
this.contents[annotationsLengthOffset++] = (byte) (counter >> 8);
this.contents[annotationsLengthOffset++] = (byte) counter;
int attributeLength = this.contentsOffset - attributeLengthOffset - 4;
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8);
this.contents[attributeLengthOffset++] = (byte) attributeLength;
attributesNumber++;
} else {
this.contentsOffset = annotationAttributeOffset;
}
}
return attributesNumber;
}
private int generateRuntimeAnnotationsForParameters(Argument[] arguments) {
final int argumentsLength = arguments.length;
final int VISIBLE_INDEX = 0;
final int INVISIBLE_INDEX = 1;
int invisibleParametersAnnotationsCounter = 0;
int visibleParametersAnnotationsCounter = 0;
int[][] annotationsCounters = new int[argumentsLength][2];
for (int i = 0; i < argumentsLength; i++) {
Argument argument = arguments[i];
Annotation[] annotations = argument.annotations;
if (annotations != null) {
for (int j = 0, max2 = annotations.length; j < max2; j++) {
Annotation annotation;
if ((annotation = annotations[j].getPersistibleAnnotation()) == null) continue; // already packaged into container.
long annotationMask = annotation.resolvedType != null ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK : 0;
if (annotationMask != 0 && (annotationMask & TagBits.AnnotationForParameter) == 0) continue;
if (annotation.isRuntimeInvisible()) {
annotationsCounters[i][INVISIBLE_INDEX]++;
invisibleParametersAnnotationsCounter++;
} else if (annotation.isRuntimeVisible()) {
annotationsCounters[i][VISIBLE_INDEX]++;
visibleParametersAnnotationsCounter++;
}
}
}
}
int attributesNumber = 0;
int annotationAttributeOffset = this.contentsOffset;
if (invisibleParametersAnnotationsCounter != 0) {
int globalCounter = 0;
if (this.contentsOffset + 7 >= this.contents.length) {
resizeContents(7);
}
int attributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.RuntimeInvisibleParameterAnnotationsName);
this.contents[this.contentsOffset++] = (byte) (attributeNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) attributeNameIndex;
int attributeLengthOffset = this.contentsOffset;
this.contentsOffset += 4; // leave space for the attribute length
this.contents[this.contentsOffset++] = (byte) argumentsLength;
for (int i = 0; i < argumentsLength; i++) {
if (this.contentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
if (invisibleParametersAnnotationsCounter == 0) {
this.contents[this.contentsOffset++] = (byte) 0;
this.contents[this.contentsOffset++] = (byte) 0;
} else {
final int numberOfInvisibleAnnotations = annotationsCounters[i][INVISIBLE_INDEX];
int invisibleAnnotationsOffset = this.contentsOffset;
// leave space for number of annotations
this.contentsOffset += 2;
int counter = 0;
if (numberOfInvisibleAnnotations != 0) {
Argument argument = arguments[i];
Annotation[] annotations = argument.annotations;
for (int j = 0, max = annotations.length; j < max; j++) {
Annotation annotation;
if ((annotation = annotations[j].getPersistibleAnnotation()) == null) continue; // already packaged into container.
long annotationMask = annotation.resolvedType != null ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK : 0;
if (annotationMask != 0 && (annotationMask & TagBits.AnnotationForParameter) == 0) continue;
if (annotation.isRuntimeInvisible()) {
int currentAnnotationOffset = this.contentsOffset;
generateAnnotation(annotation, currentAnnotationOffset);
if (this.contentsOffset != currentAnnotationOffset) {
counter++;
globalCounter++;
}
invisibleParametersAnnotationsCounter--;
}
}
}
this.contents[invisibleAnnotationsOffset++] = (byte) (counter >> 8);
this.contents[invisibleAnnotationsOffset] = (byte) counter;
}
}
if (globalCounter != 0) {
int attributeLength = this.contentsOffset - attributeLengthOffset - 4;
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8);
this.contents[attributeLengthOffset++] = (byte) attributeLength;
attributesNumber++;
} else {
// if globalCounter is 0, this means that the code generation for all visible annotations failed
this.contentsOffset = annotationAttributeOffset;
}
}
if (visibleParametersAnnotationsCounter != 0) {
int globalCounter = 0;
if (this.contentsOffset + 7 >= this.contents.length) {
resizeContents(7);
}
int attributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.RuntimeVisibleParameterAnnotationsName);
this.contents[this.contentsOffset++] = (byte) (attributeNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) attributeNameIndex;
int attributeLengthOffset = this.contentsOffset;
this.contentsOffset += 4; // leave space for the attribute length
this.contents[this.contentsOffset++] = (byte) argumentsLength;
for (int i = 0; i < argumentsLength; i++) {
if (this.contentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
if (visibleParametersAnnotationsCounter == 0) {
this.contents[this.contentsOffset++] = (byte) 0;
this.contents[this.contentsOffset++] = (byte) 0;
} else {
final int numberOfVisibleAnnotations = annotationsCounters[i][VISIBLE_INDEX];
int visibleAnnotationsOffset = this.contentsOffset;
// leave space for number of annotations
this.contentsOffset += 2;
int counter = 0;
if (numberOfVisibleAnnotations != 0) {
Argument argument = arguments[i];
Annotation[] annotations = argument.annotations;
for (int j = 0, max = annotations.length; j < max; j++) {
Annotation annotation;
if ((annotation = annotations[j].getPersistibleAnnotation()) == null) continue; // already packaged into container.
long annotationMask = annotation.resolvedType != null ? annotation.resolvedType.getAnnotationTagBits() & TagBits.AnnotationTargetMASK : 0;
if (annotationMask != 0 && (annotationMask & TagBits.AnnotationForParameter) == 0) continue;
if (annotation.isRuntimeVisible()) {
int currentAnnotationOffset = this.contentsOffset;
generateAnnotation(annotation, currentAnnotationOffset);
if (this.contentsOffset != currentAnnotationOffset) {
counter++;
globalCounter++;
}
visibleParametersAnnotationsCounter--;
}
}
}
this.contents[visibleAnnotationsOffset++] = (byte) (counter >> 8);
this.contents[visibleAnnotationsOffset] = (byte) counter;
}
}
if (globalCounter != 0) {
int attributeLength = this.contentsOffset - attributeLengthOffset - 4;
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8);
this.contents[attributeLengthOffset++] = (byte) attributeLength;
attributesNumber++;
} else {
// if globalCounter is 0, this means that the code generation for all visible annotations failed
this.contentsOffset = annotationAttributeOffset;
}
}
return attributesNumber;
}
/**
* @param annotationContexts the given annotation contexts
* @param visibleTypeAnnotationsNumber the given number of visible type annotations
* @param invisibleTypeAnnotationsNumber the given number of invisible type annotations
* @return the number of attributes created while dumping the annotations in the .class file
*/
private int generateRuntimeTypeAnnotations(
final AnnotationContext[] annotationContexts,
int visibleTypeAnnotationsNumber,
int invisibleTypeAnnotationsNumber) {
int attributesNumber = 0;
final int length = annotationContexts.length;
int visibleTypeAnnotationsCounter = visibleTypeAnnotationsNumber;
int invisibleTypeAnnotationsCounter = invisibleTypeAnnotationsNumber;
int annotationAttributeOffset = this.contentsOffset;
if (invisibleTypeAnnotationsCounter != 0) {
if (this.contentsOffset + 10 >= this.contents.length) {
resizeContents(10);
}
int runtimeInvisibleAnnotationsAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.RuntimeInvisibleTypeAnnotationsName);
this.contents[this.contentsOffset++] = (byte) (runtimeInvisibleAnnotationsAttributeNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) runtimeInvisibleAnnotationsAttributeNameIndex;
int attributeLengthOffset = this.contentsOffset;
this.contentsOffset += 4; // leave space for the attribute length
int annotationsLengthOffset = this.contentsOffset;
this.contentsOffset += 2; // leave space for the annotations length
int counter = 0;
loop: for (int i = 0; i < length; i++) {
if (invisibleTypeAnnotationsCounter == 0) break loop;
AnnotationContext annotationContext = annotationContexts[i];
if ((annotationContext.visibility & AnnotationContext.INVISIBLE) != 0) {
int currentAnnotationOffset = this.contentsOffset;
generateTypeAnnotation(annotationContext, currentAnnotationOffset);
invisibleTypeAnnotationsCounter--;
if (this.contentsOffset != currentAnnotationOffset) {
counter++;
}
}
}
if (counter != 0) {
this.contents[annotationsLengthOffset++] = (byte) (counter >> 8);
this.contents[annotationsLengthOffset++] = (byte) counter;
int attributeLength = this.contentsOffset - attributeLengthOffset - 4;
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8);
this.contents[attributeLengthOffset++] = (byte) attributeLength;
attributesNumber++;
} else {
this.contentsOffset = annotationAttributeOffset;
}
}
annotationAttributeOffset = this.contentsOffset;
if (visibleTypeAnnotationsCounter != 0) {
if (this.contentsOffset + 10 >= this.contents.length) {
resizeContents(10);
}
int runtimeVisibleAnnotationsAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.RuntimeVisibleTypeAnnotationsName);
this.contents[this.contentsOffset++] = (byte) (runtimeVisibleAnnotationsAttributeNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) runtimeVisibleAnnotationsAttributeNameIndex;
int attributeLengthOffset = this.contentsOffset;
this.contentsOffset += 4; // leave space for the attribute length
int annotationsLengthOffset = this.contentsOffset;
this.contentsOffset += 2; // leave space for the annotations length
int counter = 0;
loop: for (int i = 0; i < length; i++) {
if (visibleTypeAnnotationsCounter == 0) break loop;
AnnotationContext annotationContext = annotationContexts[i];
if ((annotationContext.visibility & AnnotationContext.VISIBLE) != 0) {
visibleTypeAnnotationsCounter--;
int currentAnnotationOffset = this.contentsOffset;
generateTypeAnnotation(annotationContext, currentAnnotationOffset);
if (this.contentsOffset != currentAnnotationOffset) {
counter++;
}
}
}
if (counter != 0) {
this.contents[annotationsLengthOffset++] = (byte) (counter >> 8);
this.contents[annotationsLengthOffset++] = (byte) counter;
int attributeLength = this.contentsOffset - attributeLengthOffset - 4;
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 24);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 16);
this.contents[attributeLengthOffset++] = (byte) (attributeLength >> 8);
this.contents[attributeLengthOffset++] = (byte) attributeLength;
attributesNumber++;
} else {
this.contentsOffset = annotationAttributeOffset;
}
}
return attributesNumber;
}
/**
* @param binding the given method binding
* @return the number of attributes created while dumping he method's parameters in the .class file (0 or 1)
*/
private int generateMethodParameters(final MethodBinding binding) {
if (binding.sourceLambda() != null)
return 0;
int initialContentsOffset = this.contentsOffset;
int length = 0; // count of actual parameters
AbstractMethodDeclaration methodDeclaration = binding.sourceMethod();
boolean isConstructor = binding.isConstructor();
TypeBinding[] targetParameters = binding.parameters;
ReferenceBinding declaringClass = binding.declaringClass;
if (declaringClass.isEnum()) {
if (isConstructor) { // insert String name,int ordinal
length = writeArgumentName(ConstantPool.EnumName, ClassFileConstants.AccSynthetic, length);
length = writeArgumentName(ConstantPool.EnumOrdinal, ClassFileConstants.AccSynthetic, length);
} else if (binding instanceof SyntheticMethodBinding
&& CharOperation.equals(ConstantPool.ValueOf, binding.selector)) { // insert String name
length = writeArgumentName(ConstantPool.Name, ClassFileConstants.AccMandated, length);
targetParameters = Binding.NO_PARAMETERS; // Override "unknown" synthetics below
}
}
boolean needSynthetics = isConstructor && declaringClass.isNestedType();
if (needSynthetics) {
// Take into account the synthetic argument names
// This tracks JLS8, paragraph 8.8.9
boolean anonymousWithLocalSuper = declaringClass.isAnonymousType() && declaringClass.superclass().isLocalType();
boolean anonymousWithNestedSuper = declaringClass.isAnonymousType() && declaringClass.superclass().isNestedType();
boolean isImplicitlyDeclared = ((! declaringClass.isPrivate()) || declaringClass.isAnonymousType()) && !anonymousWithLocalSuper;
ReferenceBinding[] syntheticArgumentTypes = declaringClass.syntheticEnclosingInstanceTypes();
if (syntheticArgumentTypes != null) {
for (int i = 0, count = syntheticArgumentTypes.length; i < count; i++) {
// This behaviour tracks JLS 15.9.5.1
// This covers that the parameter ending up in a nested class must be mandated "on the way in", even if it
// isn't the first. The practical relevance of this is questionable, since the constructor call will be
// generated by the same constructor.
boolean couldForwardToMandated = anonymousWithNestedSuper ? declaringClass.superclass().enclosingType().equals(syntheticArgumentTypes[i]) : true;
int modifier = couldForwardToMandated && isImplicitlyDeclared ? ClassFileConstants.AccMandated : ClassFileConstants.AccSynthetic;
char[] name = CharOperation.concat(
TypeConstants.SYNTHETIC_ENCLOSING_INSTANCE_PREFIX,
String.valueOf(i).toCharArray()); // cannot use depth, can be identical
length = writeArgumentName(name, modifier | ClassFileConstants.AccFinal, length);
}
}
if (binding instanceof SyntheticMethodBinding) {
targetParameters = ((SyntheticMethodBinding)binding).targetMethod.parameters;
methodDeclaration = ((SyntheticMethodBinding)binding).targetMethod.sourceMethod();
}
}
if (targetParameters != Binding.NO_PARAMETERS) {
Argument[] arguments = null;
if (methodDeclaration != null && methodDeclaration.arguments != null) {
arguments = methodDeclaration.arguments;
}
for (int i = 0, max = targetParameters.length, argumentsLength = arguments != null ? arguments.length : 0; i < max; i++) {
if (argumentsLength > i && arguments[i] != null) {
Argument argument = arguments[i];
int modifiers = argument.binding.modifiers;
if (binding.isCompactConstructor())
modifiers |= ClassFileConstants.AccMandated;
length = writeArgumentName(argument.name, modifiers, length);
} else {
length = writeArgumentName(null, ClassFileConstants.AccSynthetic, length);
}
}
}
if (needSynthetics) {
SyntheticArgumentBinding[] syntheticOuterArguments = declaringClass.syntheticOuterLocalVariables();
int count = syntheticOuterArguments == null ? 0 : syntheticOuterArguments.length;
for (int i = 0; i < count; i++) {
length = writeArgumentName(syntheticOuterArguments[i].name, syntheticOuterArguments[i].modifiers | ClassFileConstants.AccSynthetic, length);
}
// move the extra padding arguments of the synthetic constructor invocation to the end
for (int i = targetParameters.length, extraLength = binding.parameters.length; i < extraLength; i++) {
TypeBinding parameter = binding.parameters[i];
length = writeArgumentName(parameter.constantPoolName(), ClassFileConstants.AccSynthetic, length);
}
}
if (length > 0) {
// so we actually output the parameter
int attributeLength = 1 + 4 * length; // u1 for count, u2+u2 per parameter
if (this.contentsOffset + 6 + attributeLength >= this.contents.length) {
resizeContents(6 + attributeLength);
}
int methodParametersNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.MethodParametersName);
this.contents[initialContentsOffset++] = (byte) (methodParametersNameIndex >> 8);
this.contents[initialContentsOffset++] = (byte) methodParametersNameIndex;
this.contents[initialContentsOffset++] = (byte) (attributeLength >> 24);
this.contents[initialContentsOffset++] = (byte) (attributeLength >> 16);
this.contents[initialContentsOffset++] = (byte) (attributeLength >> 8);
this.contents[initialContentsOffset++] = (byte) attributeLength;
this.contents[initialContentsOffset++] = (byte) length;
return 1;
}
else {
return 0;
}
}
private int writeArgumentName(char[] name, int modifiers, int oldLength) {
int ensureRoomForBytes = 4;
if (oldLength == 0) {
// Make room for
ensureRoomForBytes += 7;
this.contentsOffset += 7; // Make room for attribute header + count byte
}
if (this.contentsOffset + ensureRoomForBytes > this.contents.length) {
resizeContents(ensureRoomForBytes);
}
int parameterNameIndex = name == null ? 0 : this.constantPool.literalIndex(name);
this.contents[this.contentsOffset++] = (byte) (parameterNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) parameterNameIndex;
int flags = modifiers & (ClassFileConstants.AccFinal | ClassFileConstants.AccSynthetic | ClassFileConstants.AccMandated);
this.contents[this.contentsOffset++] = (byte) (flags >> 8);
this.contents[this.contentsOffset++] = (byte) flags;
return oldLength + 1;
}
private int generateSignatureAttribute(char[] genericSignature) {
int localContentsOffset = this.contentsOffset;
if (localContentsOffset + 8 >= this.contents.length) {
resizeContents(8);
}
int signatureAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.SignatureName);
this.contents[localContentsOffset++] = (byte) (signatureAttributeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) signatureAttributeNameIndex;
// the length of a signature attribute is equals to 2
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 2;
int signatureIndex =
this.constantPool.literalIndex(genericSignature);
this.contents[localContentsOffset++] = (byte) (signatureIndex >> 8);
this.contents[localContentsOffset++] = (byte) signatureIndex;
this.contentsOffset = localContentsOffset;
return 1;
}
private int generateSourceAttribute(String fullFileName) {
int localContentsOffset = this.contentsOffset;
// check that there is enough space to write all the bytes for the field info corresponding
// to the @fieldBinding
if (localContentsOffset + 8 >= this.contents.length) {
resizeContents(8);
}
int sourceAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.SourceName);
this.contents[localContentsOffset++] = (byte) (sourceAttributeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) sourceAttributeNameIndex;
// The length of a source file attribute is 2. This is a fixed-length
// attribute
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 2;
// write the source file name
int fileNameIndex = this.constantPool.literalIndex(fullFileName.toCharArray());
this.contents[localContentsOffset++] = (byte) (fileNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) fileNameIndex;
this.contentsOffset = localContentsOffset;
return 1;
}
private int generateStackMapAttribute(
MethodBinding methodBinding,
int code_length,
int codeAttributeOffset,
int max_locals,
boolean isClinit,
Scope scope) {
int attributesNumber = 0;
int localContentsOffset = this.contentsOffset;
StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
stackMapFrameCodeStream.removeFramePosition(code_length);
if (stackMapFrameCodeStream.hasFramePositions()) {
Map frames = new HashMap<>();
List realFrames = traverse(isClinit ? null : methodBinding, max_locals, this.contents, codeAttributeOffset + 14, code_length, frames, isClinit, scope);
int numberOfFrames = realFrames.size();
if (numberOfFrames > 1) {
int stackMapTableAttributeOffset = localContentsOffset;
// add the stack map table attribute
if (localContentsOffset + 8 >= this.contents.length) {
resizeContents(8);
}
int stackMapAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.StackMapName);
this.contents[localContentsOffset++] = (byte) (stackMapAttributeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) stackMapAttributeNameIndex;
int stackMapAttributeLengthOffset = localContentsOffset;
// generate the attribute
localContentsOffset += 4;
if (localContentsOffset + 4 >= this.contents.length) {
resizeContents(4);
}
int numberOfFramesOffset = localContentsOffset;
localContentsOffset += 2;
if (localContentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
StackMapFrame currentFrame = realFrames.get(0);
for (int j = 1; j < numberOfFrames; j++) {
// select next frame
currentFrame = realFrames.get(j);
// generate current frame
// need to find differences between the current frame and the previous frame
int frameOffset = currentFrame.pc;
// FULL_FRAME
if (localContentsOffset + 5 >= this.contents.length) {
resizeContents(5);
}
this.contents[localContentsOffset++] = (byte) (frameOffset >> 8);
this.contents[localContentsOffset++] = (byte) frameOffset;
int numberOfLocalOffset = localContentsOffset;
localContentsOffset += 2; // leave two spots for number of locals
int numberOfLocalEntries = 0;
int numberOfLocals = currentFrame.getNumberOfLocals();
int numberOfEntries = 0;
int localsLength = currentFrame.locals == null ? 0 : currentFrame.locals.length;
for (int i = 0; i < localsLength && numberOfLocalEntries < numberOfLocals; i++) {
if (localContentsOffset + 3 >= this.contents.length) {
resizeContents(3);
}
VerificationTypeInfo info = currentFrame.locals[i];
if (info == null) {
this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP;
} else {
switch(info.id()) {
case T_boolean :
case T_byte :
case T_char :
case T_int :
case T_short :
this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER;
break;
case T_float :
this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT;
break;
case T_long :
this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG;
i++;
break;
case T_double :
this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE;
i++;
break;
case T_null :
this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL;
break;
default:
this.contents[localContentsOffset++] = (byte) info.tag;
switch (info.tag) {
case VerificationTypeInfo.ITEM_UNINITIALIZED :
int offset = info.offset;
this.contents[localContentsOffset++] = (byte) (offset >> 8);
this.contents[localContentsOffset++] = (byte) offset;
break;
case VerificationTypeInfo.ITEM_OBJECT :
int indexForType = this.constantPool.literalIndexForType(info.constantPoolName());
this.contents[localContentsOffset++] = (byte) (indexForType >> 8);
this.contents[localContentsOffset++] = (byte) indexForType;
}
}
numberOfLocalEntries++;
}
numberOfEntries++;
}
if (localContentsOffset + 4 >= this.contents.length) {
resizeContents(4);
}
this.contents[numberOfLocalOffset++] = (byte) (numberOfEntries >> 8);
this.contents[numberOfLocalOffset] = (byte) numberOfEntries;
int numberOfStackItems = currentFrame.numberOfStackItems;
this.contents[localContentsOffset++] = (byte) (numberOfStackItems >> 8);
this.contents[localContentsOffset++] = (byte) numberOfStackItems;
for (int i = 0; i < numberOfStackItems; i++) {
if (localContentsOffset + 3 >= this.contents.length) {
resizeContents(3);
}
VerificationTypeInfo info = currentFrame.stackItems[i];
if (info == null) {
this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_TOP;
} else {
switch(info.id()) {
case T_boolean :
case T_byte :
case T_char :
case T_int :
case T_short :
this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_INTEGER;
break;
case T_float :
this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_FLOAT;
break;
case T_long :
this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_LONG;
break;
case T_double :
this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_DOUBLE;
break;
case T_null :
this.contents[localContentsOffset++] = (byte) VerificationTypeInfo.ITEM_NULL;
break;
default:
this.contents[localContentsOffset++] = (byte) info.tag;
switch (info.tag) {
case VerificationTypeInfo.ITEM_UNINITIALIZED :
int offset = info.offset;
this.contents[localContentsOffset++] = (byte) (offset >> 8);
this.contents[localContentsOffset++] = (byte) offset;
break;
case VerificationTypeInfo.ITEM_OBJECT :
int indexForType = this.constantPool.literalIndexForType(info.constantPoolName());
this.contents[localContentsOffset++] = (byte) (indexForType >> 8);
this.contents[localContentsOffset++] = (byte) indexForType;
}
}
}
}
}
numberOfFrames--;
if (numberOfFrames != 0) {
this.contents[numberOfFramesOffset++] = (byte) (numberOfFrames >> 8);
this.contents[numberOfFramesOffset] = (byte) numberOfFrames;
int attributeLength = localContentsOffset - stackMapAttributeLengthOffset - 4;
this.contents[stackMapAttributeLengthOffset++] = (byte) (attributeLength >> 24);
this.contents[stackMapAttributeLengthOffset++] = (byte) (attributeLength >> 16);
this.contents[stackMapAttributeLengthOffset++] = (byte) (attributeLength >> 8);
this.contents[stackMapAttributeLengthOffset] = (byte) attributeLength;
attributesNumber++;
} else {
localContentsOffset = stackMapTableAttributeOffset;
}
}
}
this.contentsOffset = localContentsOffset;
return attributesNumber;
}
private int generateStackMapTableAttribute(
MethodBinding methodBinding,
int code_length,
int codeAttributeOffset,
int max_locals,
boolean isClinit,
Scope scope) {
int attributesNumber = 0;
int localContentsOffset = this.contentsOffset;
StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
stackMapFrameCodeStream.removeFramePosition(code_length);
if (stackMapFrameCodeStream.hasFramePositions()) {
Map frames = new HashMap<>();
List realFrames = traverse(isClinit ? null: methodBinding, max_locals, this.contents, codeAttributeOffset + 14, code_length, frames, isClinit, scope);
int numberOfFrames = realFrames.size();
if (numberOfFrames > 1) {
int stackMapTableAttributeOffset = localContentsOffset;
// add the stack map table attribute
if (localContentsOffset + 8 >= this.contents.length) {
resizeContents(8);
}
int stackMapTableAttributeNameIndex =
this.constantPool.literalIndex(AttributeNamesConstants.StackMapTableName);
this.contents[localContentsOffset++] = (byte) (stackMapTableAttributeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) stackMapTableAttributeNameIndex;
int stackMapTableAttributeLengthOffset = localContentsOffset;
// generate the attribute
localContentsOffset += 4;
if (localContentsOffset + 4 >= this.contents.length) {
resizeContents(4);
}
int numberOfFramesOffset = localContentsOffset;
localContentsOffset += 2;
if (localContentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
StackMapFrame currentFrame = realFrames.get(0);
StackMapFrame prevFrame = null;
for (int j = 1; j < numberOfFrames; j++) {
// select next frame
prevFrame = currentFrame;
currentFrame = 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;
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);
}
}
// TODO: permittedTypes codegen
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();
attributesNumber = completeRuntimeTypeAnnotations(attributesNumber,
null,
node -> size > 0,
() -> allTypeAnnotationContexts);
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;
}
/**
* Sets the actual bytes of the class file.
*
* This method is made public only to be accessible from org.eclipse.jdt.internal.core.builder.AbstractImageBuilder
* during compilation post processing to store the modified byte representation of the class. Using this method for
* any other purpose is discouraged and may lead to unpredictable results.
*
* @param newBytes
* array containing new bytes, will be stored "as is", all subsequent modification on given
* array will be reflected and vice versa.
*/
public void internalSetBytes(byte[] newBytes) {
this.bytes = newBytes;
}
/**
* EXTERNAL API
* Answer the compound name of the class file.
* @return char[][]
* e.g. {{java}, {util}, {Hashtable}}.
*/
public char[][] getCompoundName() {
return CharOperation.splitOn('/', fileName());
}
private int getParametersCount(char[] methodSignature) {
int i = CharOperation.indexOf('(', methodSignature);
i++;
char currentCharacter = methodSignature[i];
if (currentCharacter == ')') {
return 0;
}
int result = 0;
while (true) {
currentCharacter = methodSignature[i];
if (currentCharacter == ')') {
return result;
}
switch (currentCharacter) {
case '[':
// array type
int scanType = scanType(methodSignature, i + 1);
result++;
i = scanType + 1;
break;
case 'L':
scanType = CharOperation.indexOf(';', methodSignature,
i + 1);
result++;
i = scanType + 1;
break;
case 'Z':
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
result++;
i++;
break;
default:
throw new IllegalArgumentException("Invalid starting type character : " + currentCharacter); //$NON-NLS-1$
}
}
}
private char[] getReturnType(char[] methodSignature) {
// skip type parameters
int paren = CharOperation.lastIndexOf(')', methodSignature);
// there could be thrown exceptions behind, thus scan one type exactly
return CharOperation.subarray(methodSignature, paren + 1, methodSignature.length);
}
private final int i4At(byte[] reference, int relativeOffset,
int structOffset) {
int position = relativeOffset + structOffset;
return ((reference[position++] & 0xFF) << 24)
+ ((reference[position++] & 0xFF) << 16)
+ ((reference[position++] & 0xFF) << 8)
+ (reference[position] & 0xFF);
}
protected void initByteArrays(int members) {
this.header = new byte[INITIAL_HEADER_SIZE];
this.contents = new byte[members < 15 ? INITIAL_CONTENTS_SIZE : INITIAL_HEADER_SIZE];
}
private void initializeHeader(ClassFile parentClassFile, int accessFlags) {
// generate the magic numbers inside the header
this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 24);
this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 16);
this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 8);
this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 0);
long targetVersion = this.targetJDK;
this.header[this.headerOffset++] = (byte) (targetVersion >> 8); // minor high
this.header[this.headerOffset++] = (byte) (targetVersion>> 0); // minor low
this.header[this.headerOffset++] = (byte) (targetVersion >> 24); // major high
this.header[this.headerOffset++] = (byte) (targetVersion >> 16); // major low
this.constantPoolOffset = this.headerOffset;
this.headerOffset += 2;
this.constantPool.initialize(this);
this.enclosingClassFile = parentClassFile;
// now we continue to generate the bytes inside the contents array
this.contents[this.contentsOffset++] = (byte) (accessFlags >> 8);
this.contents[this.contentsOffset++] = (byte) accessFlags;
}
public void initialize(SourceTypeBinding aType, ClassFile parentClassFile, boolean createProblemType) {
// Modifier manipulations for classfile
int accessFlags = aType.getAccessFlags();
if (aType.isPrivate()) { // rewrite private to non-public
accessFlags &= ~ClassFileConstants.AccPublic;
}
if (aType.isProtected()) { // rewrite protected into public
accessFlags |= ClassFileConstants.AccPublic;
}
// clear all bits that are illegal for a class or an interface
accessFlags
&= ~(
ClassFileConstants.AccStrictfp
| ClassFileConstants.AccProtected
| ClassFileConstants.AccPrivate
| ClassFileConstants.AccStatic
| ClassFileConstants.AccSynchronized
| ClassFileConstants.AccNative);
// set the AccSuper flag (has to be done after clearing AccSynchronized - since same value)
if (!aType.isInterface()) { // class or enum
accessFlags |= ClassFileConstants.AccSuper;
}
if (aType.isAnonymousType()) {
ReferenceBinding superClass = aType.superclass;
if (superClass == null || !(superClass.isEnum() && superClass.isSealed()))
accessFlags &= ~ClassFileConstants.AccFinal;
}
int finalAbstract = ClassFileConstants.AccFinal | ClassFileConstants.AccAbstract;
if ((accessFlags & finalAbstract) == finalAbstract) {
accessFlags &= ~finalAbstract;
}
initializeHeader(parentClassFile, accessFlags);
// innerclasses get their names computed at code gen time
int classNameIndex = this.constantPool.literalIndexForType(aType);
this.contents[this.contentsOffset++] = (byte) (classNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) classNameIndex;
int superclassNameIndex;
if (aType.isInterface()) {
superclassNameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangObjectConstantPoolName);
} else {
if (aType.superclass != null) {
if ((aType.superclass.tagBits & TagBits.HasMissingType) != 0) {
superclassNameIndex = this.constantPool.literalIndexForType(ConstantPool.JavaLangObjectConstantPoolName);
} else {
superclassNameIndex = this.constantPool.literalIndexForType(aType.superclass);
}
} else {
superclassNameIndex = 0;
}
}
this.contents[this.contentsOffset++] = (byte) (superclassNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) superclassNameIndex;
ReferenceBinding[] superInterfacesBinding = aType.superInterfaces();
int interfacesCount = superInterfacesBinding.length;
int interfacesCountPosition = this.contentsOffset;
this.contentsOffset += 2;
int interfaceCounter = 0;
for (int i = 0; i < interfacesCount; i++) {
ReferenceBinding binding = superInterfacesBinding[i];
if ((binding.tagBits & TagBits.HasMissingType) != 0) {
continue;
}
if (this.contentsOffset + 4 >= this.contents.length) {
resizeContents(4); // 2 bytes this iteration plus 2 bytes after the loop
}
interfaceCounter++;
int interfaceIndex = this.constantPool.literalIndexForType(binding);
this.contents[this.contentsOffset++] = (byte) (interfaceIndex >> 8);
this.contents[this.contentsOffset++] = (byte) interfaceIndex;
}
this.contents[interfacesCountPosition++] = (byte) (interfaceCounter >> 8);
this.contents[interfacesCountPosition] = (byte) interfaceCounter;
this.creatingProblemType = createProblemType;
// retrieve the enclosing one guaranteed to be the one matching the propagated flow info
// 1FF9ZBU: LFCOM:ALL - Local variable attributes busted (Sanity check)
this.codeStream.maxFieldCount = aType.scope.outerMostClassScope().referenceType().maxFieldCount;
}
public void initializeForModule(ModuleBinding module) {
initializeHeader(null, ClassFileConstants.AccModule);
int classNameIndex = this.constantPool.literalIndexForType(TypeConstants.MODULE_INFO_NAME);
this.contents[this.contentsOffset++] = (byte) (classNameIndex >> 8);
this.contents[this.contentsOffset++] = (byte) classNameIndex;
this.codeStream.maxFieldCount = 0;
// superclass:
this.contents[this.contentsOffset++] = 0;
this.contents[this.contentsOffset++] = 0;
// superInterfacesCount
this.contents[this.contentsOffset++] = 0;
this.contents[this.contentsOffset++] = 0;
// fieldsCount
this.contents[this.contentsOffset++] = 0;
this.contents[this.contentsOffset++] = 0;
// methodsCount
this.contents[this.contentsOffset++] = 0;
this.contents[this.contentsOffset++] = 0;
}
private void initializeDefaultLocals(StackMapFrame frame,
MethodBinding methodBinding,
int maxLocals,
int codeLength) {
if (maxLocals != 0) {
int resolvedPosition = 0;
// take into account enum constructor synthetic name+ordinal
final boolean isConstructor = methodBinding.isConstructor();
if (isConstructor || !methodBinding.isStatic()) {
LocalVariableBinding localVariableBinding = new LocalVariableBinding(ConstantPool.This, methodBinding.declaringClass, 0, false);
localVariableBinding.resolvedPosition = 0;
this.codeStream.record(localVariableBinding);
localVariableBinding.recordInitializationStartPC(0);
localVariableBinding.recordInitializationEndPC(codeLength);
frame.putLocal(resolvedPosition, new VerificationTypeInfo(
isConstructor ? VerificationTypeInfo.ITEM_UNINITIALIZED_THIS : VerificationTypeInfo.ITEM_OBJECT,
methodBinding.declaringClass));
resolvedPosition++;
}
if (isConstructor) {
if (methodBinding.declaringClass.isEnum()) {
LocalVariableBinding localVariableBinding = new LocalVariableBinding(" name".toCharArray(), 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(this.referenceBinding.scope.getJavaLangString()));
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.eclipse.jdt.internal.compiler.codegen.ClassFile
*/
public ClassFile outerMostEnclosingClassFile() {
ClassFile current = this;
while (current.enclosingClassFile != null)
current = current.enclosingClassFile;
return current;
}
public void recordInnerClasses(TypeBinding binding) {
recordInnerClasses(binding, false);
}
public void recordInnerClasses(TypeBinding binding, boolean onBottomForBug445231) {
if (this.innerClassesBindings == null) {
this.innerClassesBindings = new HashMap<>(INNER_CLASSES_SIZE);
}
ReferenceBinding innerClass = (ReferenceBinding) binding;
this.innerClassesBindings.put(innerClass.erasure().unannotated(), onBottomForBug445231); // should not emit yet another inner class for Outer.@Inner Inner.
ReferenceBinding enclosingType = innerClass.enclosingType();
while (enclosingType != null
&& enclosingType.isNestedType()) {
this.innerClassesBindings.put(enclosingType.erasure().unannotated(), onBottomForBug445231);
enclosingType = enclosingType.enclosingType();
}
}
public void recordNestMember(SourceTypeBinding binding) {
SourceTypeBinding nestHost = binding != null ? binding.getNestHost() : null;
if (nestHost != null && !binding.equals(nestHost)) {// member
if (this.nestMembers == null) {
this.nestMembers = new HashSet<>(NESTED_MEMBER_SIZE);
}
this.nestMembers.add(binding);
}
}
public List getNestMembers() {
if (this.nestMembers == null)
return null;
List list = this.nestMembers
.stream()
.map(s -> new String(s.constantPoolName()))
.sorted()
.collect(Collectors.toList());
return list;
}
public int recordBootstrapMethod(FunctionalExpression expression) {
if (this.bootstrapMethods == null) {
this.bootstrapMethods = new ArrayList<>();
}
if (expression instanceof ReferenceExpression) {
for (int i = 0; i < this.bootstrapMethods.size(); i++) {
Object node = this.bootstrapMethods.get(i);
if (node instanceof FunctionalExpression) {
FunctionalExpression fexp = (FunctionalExpression) node;
if (fexp.binding == expression.binding
&& TypeBinding.equalsEquals(fexp.expectedType(), expression.expectedType()))
return expression.bootstrapMethodNumber = i;
}
}
}
this.bootstrapMethods.add(expression);
// Record which bootstrap method was assigned to the expression
return expression.bootstrapMethodNumber = this.bootstrapMethods.size() - 1;
}
public int recordBootstrapMethod(SwitchStatement switchStatement) {
if (this.bootstrapMethods == null) {
this.bootstrapMethods = new ArrayList<>();
}
this.bootstrapMethods.add(switchStatement);
return this.bootstrapMethods.size() - 1;
}
public int recordBootstrapMethod(ResolvedCase resolvedCase) {
if (this.bootstrapMethods == null) {
this.bootstrapMethods = new ArrayList<>();
}
this.bootstrapMethods.add(resolvedCase);
return this.bootstrapMethods.size() - 1;
}
public int recordBootstrapMethod(TypeBinding type) {
if (this.bootstrapMethods == null) {
this.bootstrapMethods = new ArrayList<>();
} else {
int idx = this.bootstrapMethods.indexOf(type);
if (idx != -1) {
return idx;
}
}
this.bootstrapMethods.add(type);
return this.bootstrapMethods.size() - 1;
}
public int recordBootstrapMethod(String expression) {
if (this.bootstrapMethods == null) {
this.bootstrapMethods = new ArrayList<>();
}
this.bootstrapMethods.add(expression);
return this.bootstrapMethods.size() - 1;
}
public int recordBootstrapMethod(StringTemplate template) {
if (this.bootstrapMethods == null) {
this.bootstrapMethods = new ArrayList<>();
}
this.bootstrapMethods.add(template);
return this.bootstrapMethods.size() - 1;
}
public void reset(/*@Nullable*/SourceTypeBinding typeBinding, CompilerOptions options) {
// the code stream is reinitialized for each method
if (typeBinding != null) {
this.referenceBinding = typeBinding;
this.isNestedType = typeBinding.isNestedType();
} else {
this.referenceBinding = null;
this.isNestedType = false;
}
this.targetJDK = options.targetJDK;
this.produceAttributes = options.produceDebugAttributes;
if (this.targetJDK >= ClassFileConstants.JDK1_6) {
this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP_TABLE;
if (this.targetJDK >= ClassFileConstants.JDK1_8) {
this.produceAttributes |= ClassFileConstants.ATTR_TYPE_ANNOTATION;
if (!(this.codeStream instanceof TypeAnnotationCodeStream) && this.referenceBinding != null)
this.codeStream = new TypeAnnotationCodeStream(this);
if (options.produceMethodParameters) {
this.produceAttributes |= ClassFileConstants.ATTR_METHOD_PARAMETERS;
}
}
} else if (this.targetJDK == ClassFileConstants.CLDC_1_1) {
this.targetJDK = ClassFileConstants.JDK1_1; // put back 45.3
this.produceAttributes |= ClassFileConstants.ATTR_STACK_MAP;
}
this.bytes = null;
this.constantPool.reset();
this.codeStream.reset(this);
this.constantPoolOffset = 0;
this.contentsOffset = 0;
this.creatingProblemType = false;
this.enclosingClassFile = null;
this.headerOffset = 0;
this.methodCount = 0;
this.methodCountOffset = 0;
if (this.innerClassesBindings != null) {
this.innerClassesBindings.clear();
}
if (this.nestMembers != null) {
this.nestMembers.clear();
}
if (this.bootstrapMethods != null) {
this.bootstrapMethods.clear();
}
this.missingTypes = null;
this.visitedTypes = null;
}
/**
* Resize the pool contents
*/
private final void resizeContents(int minimalSize) {
int length = this.contents.length;
int toAdd = length;
if (toAdd < minimalSize)
toAdd = minimalSize;
System.arraycopy(this.contents, 0, this.contents = new byte[length + toAdd], 0, length);
}
private VerificationTypeInfo retrieveLocal(int currentPC, int resolvedPosition) {
for (int i = 0, max = this.codeStream.allLocalsCounter; i < max; i++) {
LocalVariableBinding localVariable = this.codeStream.locals[i];
if (localVariable == null) continue;
if (resolvedPosition == localVariable.resolvedPosition) {
inits: for (int j = 0; j < localVariable.initializationCount; j++) {
int startPC = localVariable.initializationPCs[j << 1];
int endPC = localVariable.initializationPCs[(j << 1) + 1];
if (currentPC < startPC) {
continue inits;
} else if (currentPC < endPC) {
// the current local is an active local
return new VerificationTypeInfo(localVariable.type);
}
}
}
}
return null;
}
private int scanType(char[] methodSignature, int index) {
switch (methodSignature[index]) {
case '[':
// array type
return scanType(methodSignature, index + 1);
case 'L':
return CharOperation.indexOf(';', methodSignature, index + 1);
case 'Z':
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
return index;
default:
throw newIllegalArgumentException(methodSignature, index);
}
}
private static IllegalArgumentException newIllegalArgumentException(char[] string, int index) {
return new IllegalArgumentException("\"" + String.valueOf(string) + "\" at " + index); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* INTERNAL USE-ONLY
* This methods leaves the space for method counts recording.
*/
public void setForMethodInfos() {
// leave some space for the methodCount
this.methodCountOffset = this.contentsOffset;
this.contentsOffset += 2;
}
private List filterFakeFrames(Set realJumpTargets, Map frames, int codeLength) {
// no more frame to generate
// filter out "fake" frames
realJumpTargets.remove(Integer.valueOf(codeLength));
List result = new ArrayList<>();
for (Iterator iterator = realJumpTargets.iterator(); iterator.hasNext(); ) {
Integer jumpTarget = iterator.next();
StackMapFrame frame = frames.get(jumpTarget);
if (frame != null) {
result.add(frame);
}
}
Collections.sort(result, new Comparator() {
@Override
public int compare(StackMapFrame frame, StackMapFrame frame2) {
return frame.pc - frame2.pc;
}
});
return result;
}
private TypeBinding getTypeBinding(char[] typeConstantPoolName, Scope scope, boolean checkcast) {
if (typeConstantPoolName.length == 1 && !checkcast) {
// base type
switch(typeConstantPoolName[0]) {
case 'Z':
return TypeBinding.BOOLEAN;
case 'B':
return TypeBinding.BYTE;
case 'C':
return TypeBinding.CHAR;
case 'D':
return TypeBinding.DOUBLE;
case 'F':
return TypeBinding.FLOAT;
case 'I':
return TypeBinding.INT;
case 'J':
return TypeBinding.LONG;
case 'S':
return TypeBinding.SHORT;
default:
return null;
}
} else if (typeConstantPoolName[0] == '[') {
int dimensions = getDimensions(typeConstantPoolName);
if (typeConstantPoolName.length - dimensions == 1) {
// array of base types
TypeBinding baseType = null;
switch(typeConstantPoolName[typeConstantPoolName.length - 1]) {
case 'Z':
baseType = TypeBinding.BOOLEAN;
break;
case 'B':
baseType = TypeBinding.BYTE;
break;
case 'C':
baseType = TypeBinding.CHAR;
break;
case 'D':
baseType = TypeBinding.DOUBLE;
break;
case 'F':
baseType = TypeBinding.FLOAT;
break;
case 'I':
baseType = TypeBinding.INT;
break;
case 'J':
baseType = TypeBinding.LONG;
break;
case 'S':
baseType = TypeBinding.SHORT;
break;
case 'V':
baseType = TypeBinding.VOID;
}
return scope.createArrayType(baseType, dimensions);
} else {
// array of object types
char[] typeName = CharOperation.subarray(typeConstantPoolName, dimensions + 1, typeConstantPoolName.length - 1);
TypeBinding type = (TypeBinding) scope.getTypeOrPackage(CharOperation.splitOn('/', typeName));
if (!type.isValidBinding()) {
ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type;
if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0
|| (problemReferenceBinding.problemId() & ProblemReasons.NotVisible) != 0) {
type = problemReferenceBinding.closestMatch();
} else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0 && this.innerClassesBindings != null) {
// check local inner types to see if this is a anonymous type
Set innerTypeBindings = this.innerClassesBindings.keySet();
for (TypeBinding binding : innerTypeBindings) {
if (CharOperation.equals(binding.constantPoolName(), typeName)) {
type = binding;
break;
}
}
}
}
return scope.createArrayType(type, dimensions);
}
} else {
char[] typeName = checkcast ? typeConstantPoolName : CharOperation.subarray(typeConstantPoolName, 1, typeConstantPoolName.length - 1);
TypeBinding type = (TypeBinding) scope.getTypeOrPackage(CharOperation.splitOn('/', typeName));
if (!type.isValidBinding()) {
ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type;
if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0
|| (problemReferenceBinding.problemId() & ProblemReasons.NotVisible) != 0) {
type = problemReferenceBinding.closestMatch();
} else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0 && this.innerClassesBindings != null) {
// check local inner types to see if this is a anonymous type
Set innerTypeBindings = this.innerClassesBindings.keySet();
for (TypeBinding binding : innerTypeBindings) {
if (CharOperation.equals(binding.constantPoolName(), typeName)) {
type = binding;
break;
}
}
}
}
return type;
}
}
private TypeBinding getNewTypeBinding(char[] typeConstantPoolName, Scope scope) {
char[] typeName = typeConstantPoolName;
if (this.innerClassesBindings != null && isLikelyLocalTypeName(typeName)) {
// find local type in innerClassesBindings:
Set innerTypeBindings = this.innerClassesBindings.keySet();
for (TypeBinding binding : innerTypeBindings) {
if (CharOperation.equals(binding.constantPoolName(), typeName))
return binding;
}
}
TypeBinding type = (TypeBinding) scope.getTypeOrPackage(CharOperation.splitOn('/', typeName));
if (!type.isValidBinding()) {
ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type;
if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0
|| (problemReferenceBinding.problemId() & ProblemReasons.NotVisible) != 0) {
type = problemReferenceBinding.closestMatch();
}
}
return type;
}
private boolean isLikelyLocalTypeName(char[] typeName) {
int dollarPos = CharOperation.lastIndexOf('$', typeName);
while (dollarPos != -1 && dollarPos+1 < typeName.length) {
if (Character.isDigit(typeName[dollarPos+1]))
return true; // name segment starts with a digit => likely a local type (but still "$0" etc. could be part of the source name)
dollarPos = CharOperation.lastIndexOf('$', typeName, 0, dollarPos-1);
}
return false;
}
private TypeBinding getANewArrayTypeBinding(char[] typeConstantPoolName, Scope scope) {
if (typeConstantPoolName[0] == '[') {
int dimensions = getDimensions(typeConstantPoolName);
if (typeConstantPoolName.length - dimensions == 1) {
// array of base types
TypeBinding baseType = null;
switch(typeConstantPoolName[typeConstantPoolName.length - 1]) {
case 'Z':
baseType = TypeBinding.BOOLEAN;
break;
case 'B':
baseType = TypeBinding.BYTE;
break;
case 'C':
baseType = TypeBinding.CHAR;
break;
case 'D':
baseType = TypeBinding.DOUBLE;
break;
case 'F':
baseType = TypeBinding.FLOAT;
break;
case 'I':
baseType = TypeBinding.INT;
break;
case 'J':
baseType = TypeBinding.LONG;
break;
case 'S':
baseType = TypeBinding.SHORT;
break;
case 'V':
baseType = TypeBinding.VOID;
}
return scope.createArrayType(baseType, dimensions);
} else {
// array of object types
char[] elementTypeClassName = CharOperation.subarray(typeConstantPoolName, dimensions + 1, typeConstantPoolName.length - 1);
TypeBinding type = (TypeBinding) scope.getTypeOrPackage(
CharOperation.splitOn('/', elementTypeClassName));
if (!type.isValidBinding()) {
ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type;
if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0
|| (problemReferenceBinding.problemId() & ProblemReasons.NotVisible) != 0) {
type = problemReferenceBinding.closestMatch();
} else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0 && this.innerClassesBindings != null) {
// check local inner types to see if this is a anonymous type
Set innerTypeBindings = this.innerClassesBindings.keySet();
for (TypeBinding binding : innerTypeBindings) {
if (CharOperation.equals(binding.constantPoolName(), elementTypeClassName)) {
type = binding;
break;
}
}
}
}
return scope.createArrayType(type, dimensions);
}
} else {
TypeBinding type = (TypeBinding) scope.getTypeOrPackage(
CharOperation.splitOn('/', typeConstantPoolName));
if (!type.isValidBinding()) {
ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) type;
if ((problemReferenceBinding.problemId() & ProblemReasons.InternalNameProvided) != 0
|| (problemReferenceBinding.problemId() & ProblemReasons.NotVisible) != 0) {
type = problemReferenceBinding.closestMatch();
} else if ((problemReferenceBinding.problemId() & ProblemReasons.NotFound) != 0 && this.innerClassesBindings != null) {
// check local inner types to see if this is a anonymous type
Set innerTypeBindings = this.innerClassesBindings.keySet();
for (TypeBinding binding : innerTypeBindings) {
if (CharOperation.equals(binding.constantPoolName(), typeConstantPoolName)) {
type = binding;
break;
}
}
}
}
return type;
}
}
public List traverse(
MethodBinding methodBinding,
int maxLocals,
byte[] bytecodes,
int codeOffset,
int codeLength,
Map frames,
boolean isClinit,
Scope scope) {
Set realJumpTarget = new HashSet<>();
StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
int[] framePositions = stackMapFrameCodeStream.getFramePositions();
int pc = codeOffset;
int index;
int[] constantPoolOffsets = this.constantPool.offsets;
byte[] poolContents = this.constantPool.poolContent;
// set initial values for frame positions
int indexInFramePositions = 0;
int framePositionsLength = framePositions.length;
int currentFramePosition = framePositions[0];
// set initial values for exception markers
int indexInExceptionMarkers = 0;
ExceptionMarker[] exceptionMarkers= stackMapFrameCodeStream.getExceptionMarkers();
int exceptionsMarkersLength = exceptionMarkers == null ? 0 : exceptionMarkers.length;
boolean hasExceptionMarkers = exceptionsMarkersLength != 0;
ExceptionMarker exceptionMarker = null;
if (hasExceptionMarkers) {
exceptionMarker = exceptionMarkers[0];
}
StackMapFrame frame = new StackMapFrame(maxLocals);
if (!isClinit) {
initializeDefaultLocals(frame, methodBinding, maxLocals, codeLength);
}
frame.pc = -1;
add(frames, frame.duplicate(), scope);
addRealJumpTarget(realJumpTarget, -1);
for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) {
ExceptionLabel exceptionLabel = this.codeStream.exceptionLabels[i];
if (exceptionLabel != null) {
addRealJumpTarget(realJumpTarget, exceptionLabel.position);
}
}
while (true) {
int currentPC = pc - codeOffset;
if (hasExceptionMarkers && exceptionMarker.pc == currentPC) {
frame.numberOfStackItems = 0;
frame.addStackItem(new VerificationTypeInfo(exceptionMarker.getBinding()));
indexInExceptionMarkers++;
if (indexInExceptionMarkers < exceptionsMarkersLength) {
exceptionMarker = exceptionMarkers[indexInExceptionMarkers];
} else {
hasExceptionMarkers = false;
}
}
if (currentFramePosition < currentPC) {
do {
indexInFramePositions++;
if (indexInFramePositions < framePositionsLength) {
currentFramePosition = framePositions[indexInFramePositions];
} else {
currentFramePosition = Integer.MAX_VALUE;
}
} while (currentFramePosition < currentPC);
}
if (currentFramePosition == currentPC) {
// need to build a new frame and create a stack map attribute entry
StackMapFrame currentFrame = frames.get(Integer.valueOf(currentPC));
if (currentFrame == null) {
currentFrame = createNewFrame(currentPC, frame, isClinit, methodBinding);
add(frames, currentFrame, scope);
} else {
frame = currentFrame.merge(frame.duplicate(), scope).duplicate();
}
indexInFramePositions++;
if (indexInFramePositions < framePositionsLength) {
currentFramePosition = framePositions[indexInFramePositions];
} else {
currentFramePosition = Integer.MAX_VALUE;
}
}
byte opcode = (byte) u1At(bytecodes, 0, pc);
inspectFrame(currentPC, frame);
switch (opcode) {
case Opcodes.OPC_nop:
pc++;
break;
case Opcodes.OPC_aconst_null:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.NULL));
pc++;
break;
case Opcodes.OPC_iconst_m1:
case Opcodes.OPC_iconst_0:
case Opcodes.OPC_iconst_1:
case Opcodes.OPC_iconst_2:
case Opcodes.OPC_iconst_3:
case Opcodes.OPC_iconst_4:
case Opcodes.OPC_iconst_5:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT));
pc++;
break;
case Opcodes.OPC_lconst_0:
case Opcodes.OPC_lconst_1:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG));
pc++;
break;
case Opcodes.OPC_fconst_0:
case Opcodes.OPC_fconst_1:
case Opcodes.OPC_fconst_2:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT));
pc++;
break;
case Opcodes.OPC_dconst_0:
case Opcodes.OPC_dconst_1:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE));
pc++;
break;
case Opcodes.OPC_bipush:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.BYTE));
pc += 2;
break;
case Opcodes.OPC_sipush:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.SHORT));
pc += 3;
break;
case Opcodes.OPC_ldc:
index = u1At(bytecodes, 1, pc);
switch (u1At(poolContents, 0, constantPoolOffsets[index])) {
case ClassFileConstants.StringTag:
frame
.addStackItem(new VerificationTypeInfo(scope.getJavaLangString()));
break;
case ClassFileConstants.IntegerTag:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT));
break;
case ClassFileConstants.FloatTag:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT));
break;
case ClassFileConstants.ClassTag:
frame.addStackItem(new VerificationTypeInfo(scope.getJavaLangClass()));
}
pc += 2;
break;
case Opcodes.OPC_ldc_w:
index = u2At(bytecodes, 1, pc);
switch (u1At(poolContents, 0, constantPoolOffsets[index])) {
case ClassFileConstants.StringTag:
frame
.addStackItem(new VerificationTypeInfo(scope.getJavaLangString()));
break;
case ClassFileConstants.IntegerTag:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT));
break;
case ClassFileConstants.FloatTag:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT));
break;
case ClassFileConstants.ClassTag:
frame.addStackItem(new VerificationTypeInfo(scope.getJavaLangClass()));
}
pc += 3;
break;
case Opcodes.OPC_ldc2_w:
index = u2At(bytecodes, 1, pc);
switch (u1At(poolContents, 0, constantPoolOffsets[index])) {
case ClassFileConstants.DoubleTag:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE));
break;
case ClassFileConstants.LongTag:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG));
break;
}
pc += 3;
break;
case Opcodes.OPC_iload:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT));
pc += 2;
break;
case Opcodes.OPC_lload:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG));
pc += 2;
break;
case Opcodes.OPC_fload:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT));
pc += 2;
break;
case Opcodes.OPC_dload:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE));
pc += 2;
break;
case Opcodes.OPC_aload:
index = u1At(bytecodes, 1, pc);
VerificationTypeInfo localsN = retrieveLocal(currentPC, index);
frame.addStackItem(localsN);
pc += 2;
break;
case Opcodes.OPC_iload_0:
case Opcodes.OPC_iload_1:
case Opcodes.OPC_iload_2:
case Opcodes.OPC_iload_3:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT));
pc++;
break;
case Opcodes.OPC_lload_0:
case Opcodes.OPC_lload_1:
case Opcodes.OPC_lload_2:
case Opcodes.OPC_lload_3:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG));
pc++;
break;
case Opcodes.OPC_fload_0:
case Opcodes.OPC_fload_1:
case Opcodes.OPC_fload_2:
case Opcodes.OPC_fload_3:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT));
pc++;
break;
case Opcodes.OPC_dload_0:
case Opcodes.OPC_dload_1:
case Opcodes.OPC_dload_2:
case Opcodes.OPC_dload_3:
frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE));
pc++;
break;
case Opcodes.OPC_aload_0:
VerificationTypeInfo locals0 = frame.locals[0];
if (locals0 == null || locals0.tag != VerificationTypeInfo.ITEM_UNINITIALIZED_THIS) {
// special case to handle uninitialized object
locals0 = retrieveLocal(currentPC, 0);
}
frame.addStackItem(locals0);
pc++;
break;
case Opcodes.OPC_aload_1:
VerificationTypeInfo locals1 = retrieveLocal(currentPC, 1);
frame.addStackItem(locals1);
pc++;
break;
case Opcodes.OPC_aload_2:
VerificationTypeInfo locals2 = retrieveLocal(currentPC, 2);
frame.addStackItem(locals2);
pc++;
break;
case Opcodes.OPC_aload_3:
VerificationTypeInfo locals3 = retrieveLocal(currentPC, 3);
frame.addStackItem(locals3);
pc++;
break;
case Opcodes.OPC_iaload:
frame.numberOfStackItems -=2;
frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT));
pc++;
break;
case Opcodes.OPC_laload:
frame.numberOfStackItems -=2;
frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG));
pc++;
break;
case Opcodes.OPC_faload:
frame.numberOfStackItems -=2;
frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT));
pc++;
break;
case Opcodes.OPC_daload:
frame.numberOfStackItems -=2;
frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE));
pc++;
break;
case Opcodes.OPC_aaload:
frame.numberOfStackItems--;
frame.replaceWithElementType();
pc++;
break;
case Opcodes.OPC_baload:
frame.numberOfStackItems -=2;
frame.addStackItem(new VerificationTypeInfo(TypeBinding.BYTE));
pc++;
break;
case Opcodes.OPC_caload:
frame.numberOfStackItems -=2;
frame.addStackItem(new VerificationTypeInfo(TypeBinding.CHAR));
pc++;
break;
case Opcodes.OPC_saload:
frame.numberOfStackItems -=2;
frame.addStackItem(new VerificationTypeInfo(TypeBinding.SHORT));
pc++;
break;
case Opcodes.OPC_istore:
case Opcodes.OPC_lstore:
case Opcodes.OPC_fstore:
case Opcodes.OPC_dstore:
frame.numberOfStackItems--;
pc += 2;
break;
case Opcodes.OPC_astore:
index = u1At(bytecodes, 1, pc);
frame.numberOfStackItems--;
pc += 2;
break;
case Opcodes.OPC_astore_0:
frame.locals[0] = frame.stackItems[frame.numberOfStackItems - 1];
frame.numberOfStackItems--;
pc++;
break;
case Opcodes.OPC_astore_1:
case Opcodes.OPC_astore_2:
case Opcodes.OPC_astore_3:
case Opcodes.OPC_istore_0:
case Opcodes.OPC_istore_1:
case Opcodes.OPC_istore_2:
case Opcodes.OPC_istore_3:
case Opcodes.OPC_lstore_0:
case Opcodes.OPC_lstore_1:
case Opcodes.OPC_lstore_2:
case Opcodes.OPC_lstore_3:
case Opcodes.OPC_fstore_0:
case Opcodes.OPC_fstore_1:
case Opcodes.OPC_fstore_2:
case Opcodes.OPC_fstore_3:
case Opcodes.OPC_dstore_0:
case Opcodes.OPC_dstore_1:
case Opcodes.OPC_dstore_2:
case Opcodes.OPC_dstore_3:
frame.numberOfStackItems--;
pc++;
break;
case Opcodes.OPC_iastore:
case Opcodes.OPC_lastore:
case Opcodes.OPC_fastore:
case Opcodes.OPC_dastore:
case Opcodes.OPC_aastore:
case Opcodes.OPC_bastore:
case Opcodes.OPC_castore:
case Opcodes.OPC_sastore:
frame.numberOfStackItems-=3;
pc++;
break;
case Opcodes.OPC_pop:
frame.numberOfStackItems--;
pc++;
break;
case Opcodes.OPC_pop2:
int numberOfStackItems = frame.numberOfStackItems;
switch(frame.stackItems[numberOfStackItems - 1].id()) {
case TypeIds.T_long :
case TypeIds.T_double :
frame.numberOfStackItems--;
break;
default:
frame.numberOfStackItems -= 2;
}
pc++;
break;
case Opcodes.OPC_dup:
frame.addStackItem(frame.stackItems[frame.numberOfStackItems - 1]);
pc++;
break;
case Opcodes.OPC_dup_x1:
VerificationTypeInfo info = frame.stackItems[frame.numberOfStackItems - 1];
frame.numberOfStackItems--;
VerificationTypeInfo info2 = frame.stackItems[frame.numberOfStackItems - 1];
frame.numberOfStackItems--;
frame.addStackItem(info);
frame.addStackItem(info2);
frame.addStackItem(info);
pc++;
break;
case Opcodes.OPC_dup_x2:
info = frame.stackItems[frame.numberOfStackItems - 1];
frame.numberOfStackItems--;
info2 = frame.stackItems[frame.numberOfStackItems - 1];
frame.numberOfStackItems--;
switch(info2.id()) {
case TypeIds.T_long :
case TypeIds.T_double :
frame.addStackItem(info);
frame.addStackItem(info2);
frame.addStackItem(info);
break;
default:
numberOfStackItems = frame.numberOfStackItems;
VerificationTypeInfo info3 = frame.stackItems[numberOfStackItems - 1];
frame.numberOfStackItems--;
frame.addStackItem(info);
frame.addStackItem(info3);
frame.addStackItem(info2);
frame.addStackItem(info);
}
pc++;
break;
case Opcodes.OPC_dup2:
info = frame.stackItems[frame.numberOfStackItems - 1];
frame.numberOfStackItems--;
switch(info.id()) {
case TypeIds.T_double :
case TypeIds.T_long :
frame.addStackItem(info);
frame.addStackItem(info);
break;
default:
info2 = frame.stackItems[frame.numberOfStackItems - 1];
frame.numberOfStackItems--;
frame.addStackItem(info2);
frame.addStackItem(info);
frame.addStackItem(info2);
frame.addStackItem(info);
}
pc++;
break;
case Opcodes.OPC_dup2_x1:
info = frame.stackItems[frame.numberOfStackItems - 1];
frame.numberOfStackItems--;
info2 = frame.stackItems[frame.numberOfStackItems - 1];
frame.numberOfStackItems--;
switch(info.id()) {
case TypeIds.T_double :
case TypeIds.T_long :
frame.addStackItem(info);
frame.addStackItem(info2);
frame.addStackItem(info);
break;
default:
VerificationTypeInfo info3 = frame.stackItems[frame.numberOfStackItems - 1];
frame.numberOfStackItems--;
frame.addStackItem(info2);
frame.addStackItem(info);
frame.addStackItem(info3);
frame.addStackItem(info2);
frame.addStackItem(info);
}
pc++;
break;
case Opcodes.OPC_dup2_x2:
numberOfStackItems = frame.numberOfStackItems;
info = frame.stackItems[numberOfStackItems - 1];
frame.numberOfStackItems--;
info2 = frame.stackItems[frame.numberOfStackItems - 1];
frame.numberOfStackItems--;
switch(info.id()) {
case TypeIds.T_long :
case TypeIds.T_double :
switch(info2.id()) {
case TypeIds.T_long :
case TypeIds.T_double :
// form 4
frame.addStackItem(info);
frame.addStackItem(info2);
frame.addStackItem(info);
break;
default:
// form 2
numberOfStackItems = frame.numberOfStackItems;
VerificationTypeInfo info3 = frame.stackItems[numberOfStackItems - 1];
frame.numberOfStackItems--;
frame.addStackItem(info);
frame.addStackItem(info3);
frame.addStackItem(info2);
frame.addStackItem(info);
}
break;
default:
numberOfStackItems = frame.numberOfStackItems;
VerificationTypeInfo info3 = frame.stackItems[numberOfStackItems - 1];
frame.numberOfStackItems--;
switch(info3.id()) {
case TypeIds.T_long :
case TypeIds.T_double :
// form 3
frame.addStackItem(info2);
frame.addStackItem(info);
frame.addStackItem(info3);
frame.addStackItem(info2);
frame.addStackItem(info);
break;
default:
// form 1
numberOfStackItems = frame.numberOfStackItems;
VerificationTypeInfo info4 = frame.stackItems[numberOfStackItems - 1];
frame.numberOfStackItems--;
frame.addStackItem(info2);
frame.addStackItem(info);
frame.addStackItem(info4);
frame.addStackItem(info3);
frame.addStackItem(info2);
frame.addStackItem(info);
}
}
pc++;
break;
case Opcodes.OPC_swap:
numberOfStackItems = frame.numberOfStackItems;
info = frame.stackItems[numberOfStackItems - 1];
info2 = frame.stackItems[numberOfStackItems - 2];
frame.stackItems[numberOfStackItems - 1] = info2;
frame.stackItems[numberOfStackItems - 2] = info;
pc++;
break;
case Opcodes.OPC_iadd:
case Opcodes.OPC_ladd:
case Opcodes.OPC_fadd:
case Opcodes.OPC_dadd:
case Opcodes.OPC_isub:
case Opcodes.OPC_lsub:
case Opcodes.OPC_fsub:
case Opcodes.OPC_dsub:
case Opcodes.OPC_imul:
case Opcodes.OPC_lmul:
case Opcodes.OPC_fmul:
case Opcodes.OPC_dmul:
case Opcodes.OPC_idiv:
case Opcodes.OPC_ldiv:
case Opcodes.OPC_fdiv:
case Opcodes.OPC_ddiv:
case Opcodes.OPC_irem:
case Opcodes.OPC_lrem:
case Opcodes.OPC_frem:
case Opcodes.OPC_drem:
case Opcodes.OPC_ishl:
case Opcodes.OPC_lshl:
case Opcodes.OPC_ishr:
case Opcodes.OPC_lshr:
case Opcodes.OPC_iushr:
case Opcodes.OPC_lushr:
case Opcodes.OPC_iand:
case Opcodes.OPC_land:
case Opcodes.OPC_ior:
case Opcodes.OPC_lor:
case Opcodes.OPC_ixor:
case Opcodes.OPC_lxor:
frame.numberOfStackItems--;
pc++;
break;
case Opcodes.OPC_ineg:
case Opcodes.OPC_lneg:
case Opcodes.OPC_fneg:
case Opcodes.OPC_dneg:
pc++;
break;
case Opcodes.OPC_iinc:
pc += 3;
break;
case Opcodes.OPC_i2l:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.LONG);
pc++;
break;
case Opcodes.OPC_i2f:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.FLOAT);
pc++;
break;
case Opcodes.OPC_i2d:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.DOUBLE);
pc++;
break;
case Opcodes.OPC_l2i:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT);
pc++;
break;
case Opcodes.OPC_l2f:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.FLOAT);
pc++;
break;
case Opcodes.OPC_l2d:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.DOUBLE);
pc++;
break;
case Opcodes.OPC_f2i:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT);
pc++;
break;
case Opcodes.OPC_f2l:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.LONG);
pc++;
break;
case Opcodes.OPC_f2d:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.DOUBLE);
pc++;
break;
case Opcodes.OPC_d2i:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT);
pc++;
break;
case Opcodes.OPC_d2l:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.LONG);
pc++;
break;
case Opcodes.OPC_d2f:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.FLOAT);
pc++;
break;
case Opcodes.OPC_i2b:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.BYTE);
pc++;
break;
case Opcodes.OPC_i2c:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.CHAR);
pc++;
break;
case Opcodes.OPC_i2s:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.SHORT);
pc++;
break;
case Opcodes.OPC_lcmp:
case Opcodes.OPC_fcmpl:
case Opcodes.OPC_fcmpg:
case Opcodes.OPC_dcmpl:
case Opcodes.OPC_dcmpg:
frame.numberOfStackItems-=2;
frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT));
pc++;
break;
case Opcodes.OPC_ifeq:
case Opcodes.OPC_ifne:
case Opcodes.OPC_iflt:
case Opcodes.OPC_ifge:
case Opcodes.OPC_ifgt:
case Opcodes.OPC_ifle:
frame.numberOfStackItems--;
int jumpPC = currentPC + i2At(bytecodes, 1, pc);
addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
pc += 3;
break;
case Opcodes.OPC_if_icmpeq:
case Opcodes.OPC_if_icmpne:
case Opcodes.OPC_if_icmplt:
case Opcodes.OPC_if_icmpge:
case Opcodes.OPC_if_icmpgt:
case Opcodes.OPC_if_icmple:
case Opcodes.OPC_if_acmpeq:
case Opcodes.OPC_if_acmpne:
frame.numberOfStackItems -= 2;
jumpPC = currentPC + i2At(bytecodes, 1, pc);
addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
pc += 3;
break;
case Opcodes.OPC_goto:
jumpPC = currentPC + i2At(bytecodes, 1, pc);
addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
pc += 3;
addRealJumpTarget(realJumpTarget, pc - codeOffset);
break;
case Opcodes.OPC_tableswitch:
frame.numberOfStackItems--;
pc++;
while (((pc - codeOffset) & 0x03) != 0) {
pc++;
}
// default offset
jumpPC = currentPC + i4At(bytecodes, 0, pc);
addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
pc += 4; // default
int low = i4At(bytecodes, 0, pc);
pc += 4;
int high = i4At(bytecodes, 0, pc);
pc += 4;
int length = high - low + 1;
for (int i = 0; i < length; i++) {
// pair offset
jumpPC = currentPC + i4At(bytecodes, 0, pc);
addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
pc += 4;
}
break;
case Opcodes.OPC_lookupswitch:
frame.numberOfStackItems--;
pc++;
while (((pc - codeOffset) & 0x03) != 0) {
pc++;
}
jumpPC = currentPC + i4At(bytecodes, 0, pc);
addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
pc += 4; // default offset
int npairs = (int) u4At(bytecodes, 0, pc);
pc += 4; // npair value
for (int i = 0; i < npairs; i++) {
pc += 4; // case value
// pair offset
jumpPC = currentPC + i4At(bytecodes, 0, pc);
addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
pc += 4;
}
break;
case Opcodes.OPC_ireturn:
case Opcodes.OPC_lreturn:
case Opcodes.OPC_freturn:
case Opcodes.OPC_dreturn:
case Opcodes.OPC_areturn:
frame.numberOfStackItems--;
pc++;
addRealJumpTarget(realJumpTarget, pc - codeOffset);
break;
case Opcodes.OPC_return:
pc++;
addRealJumpTarget(realJumpTarget, pc - codeOffset);
break;
case Opcodes.OPC_getstatic:
index = u2At(bytecodes, 1, pc);
int nameAndTypeIndex = u2At(poolContents, 3,
constantPoolOffsets[index]);
int utf8index = u2At(poolContents, 3,
constantPoolOffsets[nameAndTypeIndex]);
char[] descriptor = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
TypeBinding typeBinding = getTypeBinding(descriptor, scope, false);
if (typeBinding != null) {
frame.addStackItem(new VerificationTypeInfo(typeBinding));
}
pc += 3;
break;
case Opcodes.OPC_putstatic:
frame.numberOfStackItems--;
pc += 3;
break;
case Opcodes.OPC_getfield:
index = u2At(bytecodes, 1, pc);
nameAndTypeIndex = u2At(poolContents, 3,
constantPoolOffsets[index]);
utf8index = u2At(poolContents, 3,
constantPoolOffsets[nameAndTypeIndex]);
descriptor = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
frame.numberOfStackItems--;
typeBinding = getTypeBinding(descriptor, scope, false);
if (typeBinding != null) {
frame.addStackItem(new VerificationTypeInfo(typeBinding));
}
pc += 3;
break;
case Opcodes.OPC_putfield:
frame.numberOfStackItems -= 2;
pc += 3;
break;
case Opcodes.OPC_invokevirtual:
index = u2At(bytecodes, 1, pc);
nameAndTypeIndex = u2At(poolContents, 3,
constantPoolOffsets[index]);
utf8index = u2At(poolContents, 3,
constantPoolOffsets[nameAndTypeIndex]);
descriptor = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
utf8index = u2At(poolContents, 1,
constantPoolOffsets[nameAndTypeIndex]);
char[] name = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
frame.numberOfStackItems -= (getParametersCount(descriptor) + 1);
char[] returnType = getReturnType(descriptor);
typeBinding = getTypeBinding(returnType, scope, false);
if (typeBinding != null) {
frame.addStackItem(new VerificationTypeInfo(typeBinding));
}
pc += 3;
break;
case Opcodes.OPC_invokedynamic:
index = u2At(bytecodes, 1, pc);
nameAndTypeIndex = u2At(poolContents, 3,
constantPoolOffsets[index]);
utf8index = u2At(poolContents, 3,
constantPoolOffsets[nameAndTypeIndex]);
descriptor = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
frame.numberOfStackItems -= getParametersCount(descriptor);
returnType = getReturnType(descriptor);
typeBinding = getTypeBinding(returnType, scope, false);
if (typeBinding != null) {
frame.addStackItem(new VerificationTypeInfo(typeBinding));
}
pc += 5;
break;
case Opcodes.OPC_invokespecial:
index = u2At(bytecodes, 1, pc);
nameAndTypeIndex = u2At(poolContents, 3,
constantPoolOffsets[index]);
utf8index = u2At(poolContents, 3,
constantPoolOffsets[nameAndTypeIndex]);
descriptor = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
utf8index = u2At(poolContents, 1,
constantPoolOffsets[nameAndTypeIndex]);
name = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
frame.numberOfStackItems -= getParametersCount(descriptor);
if (CharOperation.equals(ConstantPool.Init, name)) {
// constructor
frame.stackItems[frame.numberOfStackItems - 1].tag = VerificationTypeInfo.ITEM_OBJECT;
}
frame.numberOfStackItems--;
returnType = getReturnType(descriptor);
typeBinding = getTypeBinding(returnType, scope, false);
if (typeBinding != null) {
frame.addStackItem(new VerificationTypeInfo(typeBinding));
}
pc += 3;
break;
case Opcodes.OPC_invokestatic:
index = u2At(bytecodes, 1, pc);
nameAndTypeIndex = u2At(poolContents, 3,
constantPoolOffsets[index]);
utf8index = u2At(poolContents, 3,
constantPoolOffsets[nameAndTypeIndex]);
descriptor = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
utf8index = u2At(poolContents, 1,
constantPoolOffsets[nameAndTypeIndex]);
name = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
frame.numberOfStackItems -= getParametersCount(descriptor);
returnType = getReturnType(descriptor);
typeBinding = getTypeBinding(returnType, scope, false);
if (typeBinding != null) {
frame.addStackItem(new VerificationTypeInfo(typeBinding));
}
pc += 3;
break;
case Opcodes.OPC_invokeinterface:
index = u2At(bytecodes, 1, pc);
nameAndTypeIndex = u2At(poolContents, 3,
constantPoolOffsets[index]);
utf8index = u2At(poolContents, 3,
constantPoolOffsets[nameAndTypeIndex]);
descriptor = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
utf8index = u2At(poolContents, 1,
constantPoolOffsets[nameAndTypeIndex]);
name = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
// we don't need count and args
// u1At(bytecodes, 3, pc); // count
// u1At(bytecodes, 4, pc); // extra args
frame.numberOfStackItems -= (getParametersCount(descriptor) + 1);
returnType = getReturnType(descriptor);
typeBinding = getTypeBinding(returnType, scope, false);
if (typeBinding != null) {
frame.addStackItem(new VerificationTypeInfo(typeBinding));
}
pc += 5;
break;
case Opcodes.OPC_new:
index = u2At(bytecodes, 1, pc);
utf8index = u2At(poolContents, 1,
constantPoolOffsets[index]);
char[] className = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
typeBinding = getNewTypeBinding(className, scope);
VerificationTypeInfo verificationTypeInfo = new VerificationTypeInfo(VerificationTypeInfo.ITEM_UNINITIALIZED, typeBinding);
verificationTypeInfo.offset = currentPC;
frame.addStackItem(verificationTypeInfo);
pc += 3;
break;
case Opcodes.OPC_newarray:
TypeBinding arrayType = null;
switch (u1At(bytecodes, 1, pc)) {
case ClassFileConstants.INT_ARRAY :
arrayType = scope.createArrayType(TypeBinding.INT, 1);
break;
case ClassFileConstants.BYTE_ARRAY :
arrayType = scope.createArrayType(TypeBinding.BYTE, 1);
break;
case ClassFileConstants.BOOLEAN_ARRAY :
arrayType = scope.createArrayType(TypeBinding.BOOLEAN, 1);
break;
case ClassFileConstants.SHORT_ARRAY :
arrayType = scope.createArrayType(TypeBinding.SHORT, 1);
break;
case ClassFileConstants.CHAR_ARRAY :
arrayType = scope.createArrayType(TypeBinding.CHAR, 1);
break;
case ClassFileConstants.LONG_ARRAY :
arrayType = scope.createArrayType(TypeBinding.LONG, 1);
break;
case ClassFileConstants.FLOAT_ARRAY :
arrayType = scope.createArrayType(TypeBinding.FLOAT, 1);
break;
case ClassFileConstants.DOUBLE_ARRAY :
arrayType = scope.createArrayType(TypeBinding.DOUBLE, 1);
break;
}
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(arrayType);
pc += 2;
break;
case Opcodes.OPC_anewarray:
index = u2At(bytecodes, 1, pc);
utf8index = u2At(poolContents, 1,
constantPoolOffsets[index]);
className = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
frame.numberOfStackItems--;
typeBinding = getANewArrayTypeBinding(className, scope);
if (typeBinding != null) {
if (typeBinding.isArrayType()) {
ArrayBinding arrayBinding = (ArrayBinding) typeBinding;
frame.addStackItem(new VerificationTypeInfo(scope.createArrayType(arrayBinding.leafComponentType(), arrayBinding.dimensions + 1)));
} else {
frame.addStackItem(new VerificationTypeInfo(scope.createArrayType(typeBinding, 1)));
}
}
pc += 3;
break;
case Opcodes.OPC_arraylength:
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT);
pc++;
break;
case Opcodes.OPC_athrow:
frame.numberOfStackItems--;
pc++;
addRealJumpTarget(realJumpTarget, pc - codeOffset);
break;
case Opcodes.OPC_checkcast:
index = u2At(bytecodes, 1, pc);
utf8index = u2At(poolContents, 1,
constantPoolOffsets[index]);
className = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
typeBinding = getTypeBinding(className, scope, true);
if (typeBinding != null) {
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(typeBinding);
}
pc += 3;
break;
case Opcodes.OPC_instanceof:
// no need to know the class index = u2At(bytecodes, 1, pc);
frame.stackItems[frame.numberOfStackItems - 1] = new VerificationTypeInfo(TypeBinding.INT);
pc += 3;
break;
case Opcodes.OPC_monitorenter:
case Opcodes.OPC_monitorexit:
frame.numberOfStackItems--;
pc++;
break;
case Opcodes.OPC_wide:
opcode = (byte) u1At(bytecodes, 1, pc);
if (opcode == Opcodes.OPC_iinc) {
// index = u2At(bytecodes, 2, pc);
// i2At(bytecodes, 4, pc); // const
// we don't need the index and the const value
pc += 6;
} else {
index = u2At(bytecodes, 2, pc);
// need to handle iload, fload, aload, lload, dload, istore, fstore, astore, lstore or dstore
switch(opcode) {
case Opcodes.OPC_iload :
frame.addStackItem(new VerificationTypeInfo(TypeBinding.INT));
break;
case Opcodes.OPC_fload :
frame.addStackItem(new VerificationTypeInfo(TypeBinding.FLOAT));
break;
case Opcodes.OPC_aload :
localsN = frame.locals[index];
if (localsN == null) {
localsN = retrieveLocal(currentPC, index);
}
frame.addStackItem(localsN);
break;
case Opcodes.OPC_lload :
frame.addStackItem(new VerificationTypeInfo(TypeBinding.LONG));
break;
case Opcodes.OPC_dload :
frame.addStackItem(new VerificationTypeInfo(TypeBinding.DOUBLE));
break;
case Opcodes.OPC_istore :
frame.numberOfStackItems--;
break;
case Opcodes.OPC_fstore :
frame.numberOfStackItems--;
break;
case Opcodes.OPC_astore :
frame.locals[index] = frame.stackItems[frame.numberOfStackItems - 1];
frame.numberOfStackItems--;
break;
case Opcodes.OPC_lstore :
frame.numberOfStackItems--;
break;
case Opcodes.OPC_dstore :
frame.numberOfStackItems--;
break;
}
pc += 4;
}
break;
case Opcodes.OPC_multianewarray:
index = u2At(bytecodes, 1, pc);
utf8index = u2At(poolContents, 1,
constantPoolOffsets[index]);
className = utf8At(poolContents,
constantPoolOffsets[utf8index] + 3, u2At(
poolContents, 1,
constantPoolOffsets[utf8index]));
int dimensions = u1At(bytecodes, 3, pc); // dimensions
frame.numberOfStackItems -= dimensions;
// class name is already the name of the right array type with all dimensions
typeBinding = getTypeBinding(className, scope, false);
if (typeBinding != null) {
frame.addStackItem(new VerificationTypeInfo(typeBinding));
}
pc += 4;
break;
case Opcodes.OPC_ifnull:
case Opcodes.OPC_ifnonnull:
frame.numberOfStackItems--;
jumpPC = currentPC + i2At(bytecodes, 1, pc);
addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
pc += 3;
break;
case Opcodes.OPC_goto_w:
jumpPC = currentPC + i4At(bytecodes, 1, pc);
addRealJumpTarget(realJumpTarget, jumpPC, frames, createNewFrame(jumpPC, frame, isClinit, methodBinding), scope);
pc += 5;
addRealJumpTarget(realJumpTarget, pc - codeOffset); // handle infinite loop
break;
default: // should not occur
if (this.codeStream.methodDeclaration != null) {
this.codeStream.methodDeclaration.scope.problemReporter().abortDueToInternalError(
Messages.bind(
Messages.abort_invalidOpcode,
new Object[] {
Byte.valueOf(opcode),
Integer.valueOf(pc),
new String(methodBinding.shortReadableName()),
}),
this.codeStream.methodDeclaration);
} else {
this.codeStream.lambdaExpression.scope.problemReporter().abortDueToInternalError(
Messages.bind(
Messages.abort_invalidOpcode,
new Object[] {
Byte.valueOf(opcode),
Integer.valueOf(pc),
new String(methodBinding.shortReadableName()),
}),
this.codeStream.lambdaExpression);
}
break;
}
if (pc >= (codeLength + codeOffset)) {
break;
}
}
return filterFakeFrames(realJumpTarget, frames, codeLength);
}
private void inspectFrame(int currentPC, StackMapFrame frame) {
// Plant a breakpoint at the call site to conveniently hover.
}
private StackMapFrame createNewFrame(int currentPC, StackMapFrame frame, boolean isClinit, MethodBinding methodBinding) {
StackMapFrame newFrame = frame.duplicate();
newFrame.pc = currentPC;
// initialize locals
initializeLocals(isClinit ? true : methodBinding.isStatic(), currentPC, newFrame);
return newFrame;
}
private int getDimensions(char[] returnType) {
int dimensions = 0;
while (returnType[dimensions] == '[') {
dimensions++;
}
return dimensions;
}
private void addRealJumpTarget(Set realJumpTarget, int pc) {
realJumpTarget.add(Integer.valueOf(pc));
}
private void addRealJumpTarget(Set realJumpTarget, int pc, Map frames, StackMapFrame frame, Scope scope) {
realJumpTarget.add(Integer.valueOf(pc));
add(frames, frame, scope);
}
private void add(Map frames, StackMapFrame frame, Scope scope) {
Integer key = Integer.valueOf(frame.pc);
StackMapFrame existingFrame = frames.get(key);
if(existingFrame == null) {
frames.put(key, frame);
} else {
// we need to merge
frames.put(key, existingFrame.merge(frame, scope));
}
}
private final int u1At(byte[] reference, int relativeOffset,
int structOffset) {
return (reference[relativeOffset + structOffset] & 0xFF);
}
private final int u2At(byte[] reference, int relativeOffset,
int structOffset) {
int position = relativeOffset + structOffset;
return ((reference[position++] & 0xFF) << 8)
+ (reference[position] & 0xFF);
}
private final long u4At(byte[] reference, int relativeOffset,
int structOffset) {
int position = relativeOffset + structOffset;
return (((reference[position++] & 0xFFL) << 24)
+ ((reference[position++] & 0xFF) << 16)
+ ((reference[position++] & 0xFF) << 8) + (reference[position] & 0xFF));
}
private final int i2At(byte[] reference, int relativeOffset, int structOffset) {
int position = relativeOffset + structOffset;
return (reference[position++] << 8) + (reference[position] & 0xFF);
}
public char[] utf8At(byte[] reference, int absoluteOffset,
int bytesAvailable) {
int length = bytesAvailable;
char outputBuf[] = new char[bytesAvailable];
int outputPos = 0;
int readOffset = absoluteOffset;
while (length != 0) {
int x = reference[readOffset++] & 0xFF;
length--;
if ((0x80 & x) != 0) {
if ((x & 0x20) != 0) {
length -= 2;
x = ((x & 0xF) << 12)
| ((reference[readOffset++] & 0x3F) << 6)
| (reference[readOffset++] & 0x3F);
} else {
length--;
x = ((x & 0x1F) << 6) | (reference[readOffset++] & 0x3F);
}
}
outputBuf[outputPos++] = (char) x;
}
if (outputPos != bytesAvailable) {
System.arraycopy(outputBuf, 0, (outputBuf = new char[outputPos]),
0, outputPos);
}
return outputBuf;
}
}