org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* Copyright (c) 2006, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.codegen;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ClassFile;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.problem.AbortMethod;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class StackMapFrameCodeStream extends CodeStream {
public static class ExceptionMarker implements Comparable {
private final TypeBinding binding;
public int pc;
public ExceptionMarker(int pc, TypeBinding typeBinding) {
this.pc = pc;
this.binding = typeBinding;
}
@Override
public int compareTo(Object o) {
if (o instanceof ExceptionMarker) {
return this.pc - ((ExceptionMarker) o).pc;
}
return 0;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ExceptionMarker) {
ExceptionMarker marker = (ExceptionMarker) obj;
return this.pc == marker.pc && this.binding.equals(marker.binding);
}
return false;
}
public TypeBinding getBinding() {
return this.binding;
}
@Override
public int hashCode() {
return this.pc + CharOperation.hashCode(this.binding.constantPoolName());
}
@Override
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append('(').append(this.pc).append(',').append(this.binding.constantPoolName()).append(')');
return String.valueOf(buffer);
}
}
static class FramePosition {
int counter;
}
public int[] stateIndexes;
public int stateIndexesCounter;
private HashMap framePositions;
public Set exceptionMarkers;
public ArrayList stackDepthMarkers;
public ArrayList stackMarkers;
public StackMapFrameCodeStream(ClassFile givenClassFile) {
super(givenClassFile);
this.generateAttributes |= ClassFileConstants.ATTR_STACK_MAP;
}
@Override
public void addDefinitelyAssignedVariables(Scope scope, int initStateIndex) {
// Required to fix 1PR0XVS: LFRE:WINNT - Compiler: variable table for method appears incorrect
for (int i = 0; i < this.visibleLocalsCount; i++) {
LocalVariableBinding localBinding = this.visibleLocals[i];
if (localBinding != null) {
// Check if the local is definitely assigned
boolean isDefinitelyAssigned = isDefinitelyAssigned(scope, initStateIndex, localBinding);
if (!isDefinitelyAssigned) {
continue;
} else {
if ((localBinding.initializationCount == 0)
|| (localBinding.initializationPCs[((localBinding.initializationCount - 1) << 1)
+ 1] != -1)) {
/*
* There are two cases: 1) there is no initialization interval opened ==> add an opened interval
* 2) there is already some initialization intervals but the last one is closed ==> add an
* opened interval An opened interval means that the value at
* localBinding.initializationPCs[localBinding.initializationCount - 1][1] is equals to -1.
* initializationPCs is a collection of pairs of int: first value is the startPC and second
* value is the endPC. -1 one for the last value means that the interval is not closed yet.
*/
localBinding.recordInitializationStartPC(this.position);
}
}
}
}
}
public void addExceptionMarker(int pc, TypeBinding typeBinding) {
if (this.exceptionMarkers == null) {
this.exceptionMarkers = new HashSet();
}
this.exceptionMarkers.add(new ExceptionMarker(pc, typeBinding));
}
public void addFramePosition(int pc) {
Integer newEntry = Integer.valueOf(pc);
FramePosition value;
if ((value = (FramePosition) this.framePositions.get(newEntry)) != null) {
value.counter++;
} else {
this.framePositions.put(newEntry, new FramePosition());
}
}
@Override
public void optimizeBranch(int oldPosition, BranchLabel lbl) {
super.optimizeBranch(oldPosition, lbl);
removeFramePosition(oldPosition);
}
public void removeFramePosition(int pc) {
Integer entry = Integer.valueOf(pc);
FramePosition value;
if ((value = (FramePosition) this.framePositions.get(entry)) != null) {
value.counter--;
if (value.counter <= 0) {
this.framePositions.remove(entry);
}
}
}
@Override
public void addVariable(LocalVariableBinding localBinding) {
if (localBinding.initializationPCs == null) {
record(localBinding);
}
localBinding.recordInitializationStartPC(this.position);
}
@Override
public void recordExpressionType(TypeBinding typeBinding, int delta, boolean adjustStackDepth) {
if (adjustStackDepth) {
// optimized goto
// the break label already adjusted the stack depth (-1 or -2 depending on the return type)
// we need to adjust back to what it was
switch (typeBinding.id) {
case TypeIds.T_long:
case TypeIds.T_double:
this.stackDepth += 2;
break;
case TypeIds.T_void:
break;
default:
this.stackDepth++;
break;
}
}
}
/**
* Macro for building a class descriptor object
*/
@Override
public void generateClassLiteralAccessForType(Scope scope, TypeBinding accessedType,
FieldBinding syntheticFieldBinding) {
if (accessedType.isBaseType() && accessedType != TypeBinding.NULL) {
getTYPE(accessedType.id);
return;
}
if (this.targetLevel >= ClassFileConstants.JDK1_5) {
// generation using the new ldc_w bytecode
this.ldc(accessedType);
} else {
// use in CLDC mode
BranchLabel endLabel = new BranchLabel(this);
if (syntheticFieldBinding != null) { // non interface case
fieldAccess(Opcodes.OPC_getstatic, syntheticFieldBinding, null /* default declaringClass */);
dup();
ifnonnull(endLabel);
pop();
}
/*
* Macro for building a class descriptor object... using or not a field cache to store it into... this
* sequence is responsible for building the actual class descriptor.
*
* If the fieldCache is set, then it is supposed to be the body of a synthetic access method factoring the
* actual descriptor creation out of the invocation site (saving space). If the fieldCache is nil, then we
* are dumping the bytecode on the invocation site, since we have no way to get a hand on the field cache to
* do better.
*/
// Wrap the code in an exception handler to convert a ClassNotFoundException into a NoClassDefError
ExceptionLabel classNotFoundExceptionHandler = new ExceptionLabel(this,
TypeBinding.NULL /* represents ClassNotFoundException */);
classNotFoundExceptionHandler.placeStart();
this.ldc(accessedType == TypeBinding.NULL ? "java.lang.Object" //$NON-NLS-1$
: String.valueOf(accessedType.constantPoolName()).replace('/', '.'));
invokeClassForName();
/*
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=37565 if (accessedType == BaseTypes.NullBinding) {
* this.ldc("java.lang.Object"); //$NON-NLS-1$ } else if (accessedType.isArrayType()) {
* this.ldc(String.valueOf(accessedType.constantPoolName()).replace('/', '.')); } else { // we make it an
* array type (to avoid class initialization) this.ldc("[L" +
* String.valueOf(accessedType.constantPoolName()).replace('/', '.') + ";"); //$NON-NLS-1$//$NON-NLS-2$ }
* this.invokeClassForName(); if (!accessedType.isArrayType()) { // extract the component type, which
* doesn't initialize the class this.invokeJavaLangClassGetComponentType(); }
*/
/*
* We need to protect the runtime code from binary inconsistencies in case the accessedType is missing, the
* ClassNotFoundException has to be converted into a NoClassDefError(old ex message), we thus need to build
* an exception handler for this one.
*/
classNotFoundExceptionHandler.placeEnd();
if (syntheticFieldBinding != null) { // non interface case
dup();
fieldAccess(Opcodes.OPC_putstatic, syntheticFieldBinding, null /* default declaringClass */);
}
goto_(endLabel);
int savedStackDepth = this.stackDepth;
// Generate the body of the exception handler
/*
* ClassNotFoundException on stack -- the class literal could be doing more things on the stack, which means
* that the stack may not be empty at this point in the above code gen. So we save its state and restart it
* from 1.
*/
pushExceptionOnStack(scope.getJavaLangClassNotFoundException());
classNotFoundExceptionHandler.place();
// Transform the current exception, and repush and throw a
// NoClassDefFoundError(ClassNotFound.getMessage())
newNoClassDefFoundError();
dup_x1();
swap();
// Retrieve the message from the old exception
invokeThrowableGetMessage();
// Send the constructor taking a message string as an argument
invokeNoClassDefFoundErrorStringConstructor();
athrow();
endLabel.place();
this.stackDepth = savedStackDepth;
}
}
@Override
public void generateOuterAccess(Object[] mappingSequence, ASTNode invocationSite, Binding target, Scope scope) {
int currentPosition = this.position;
super.generateOuterAccess(mappingSequence, invocationSite, target, scope);
if (currentPosition == this.position) {
// no code has been generate during outer access => no enclosing instance is available
throw new AbortMethod(scope.referenceCompilationUnit().compilationResult, null);
}
}
public ExceptionMarker[] getExceptionMarkers() {
Set exceptionMarkerSet = this.exceptionMarkers;
if (this.exceptionMarkers == null)
return null;
int size = exceptionMarkerSet.size();
ExceptionMarker[] markers = new ExceptionMarker[size];
int n = 0;
for (Iterator iterator = exceptionMarkerSet.iterator(); iterator.hasNext();) {
markers[n++] = (ExceptionMarker) iterator.next();
}
Arrays.sort(markers);
// System.out.print('[');
// for (int n = 0; n < size; n++) {
// if (n != 0) System.out.print(',');
// System.out.print(positions[n]);
// }
// System.out.println(']');
return markers;
}
public int[] getFramePositions() {
Set set = this.framePositions.keySet();
int size = set.size();
int[] positions = new int[size];
int n = 0;
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
positions[n++] = ((Integer) iterator.next()).intValue();
}
Arrays.sort(positions);
// System.out.print('[');
// for (int n = 0; n < size; n++) {
// if (n != 0) System.out.print(',');
// System.out.print(positions[n]);
// }
// System.out.println(']');
return positions;
}
public boolean hasFramePositions() {
return this.framePositions.size() != 0;
}
@Override
public void init(ClassFile targetClassFile) {
super.init(targetClassFile);
this.stateIndexesCounter = 0;
if (this.framePositions != null) {
this.framePositions.clear();
}
if (this.exceptionMarkers != null) {
this.exceptionMarkers.clear();
}
if (this.stackDepthMarkers != null) {
this.stackDepthMarkers.clear();
}
if (this.stackMarkers != null) {
this.stackMarkers.clear();
}
}
@Override
public void initializeMaxLocals(MethodBinding methodBinding) {
super.initializeMaxLocals(methodBinding);
if (this.framePositions == null) {
this.framePositions = new HashMap();
} else {
this.framePositions.clear();
}
}
public void popStateIndex() {
this.stateIndexesCounter--;
}
public void pushStateIndex(int naturalExitMergeInitStateIndex) {
if (this.stateIndexes == null) {
this.stateIndexes = new int[3];
}
int length = this.stateIndexes.length;
if (length == this.stateIndexesCounter) {
// resize
System.arraycopy(this.stateIndexes, 0, (this.stateIndexes = new int[length * 2]), 0, length);
}
this.stateIndexes[this.stateIndexesCounter++] = naturalExitMergeInitStateIndex;
}
@Override
public void removeNotDefinitelyAssignedVariables(Scope scope, int initStateIndex) {
int index = this.visibleLocalsCount;
loop: for (int i = 0; i < index; i++) {
LocalVariableBinding localBinding = this.visibleLocals[i];
if (localBinding != null && localBinding.initializationCount > 0) {
boolean isDefinitelyAssigned = isDefinitelyAssigned(scope, initStateIndex, localBinding);
if (!isDefinitelyAssigned) {
if (this.stateIndexes != null) {
for (int j = 0, max = this.stateIndexesCounter; j < max; j++) {
if (isDefinitelyAssigned(scope, this.stateIndexes[j], localBinding)) {
continue loop;
}
}
}
localBinding.recordInitializationEndPC(this.position);
}
}
}
}
@Override
public void reset(ClassFile givenClassFile) {
super.reset(givenClassFile);
this.stateIndexesCounter = 0;
if (this.framePositions != null) {
this.framePositions.clear();
}
if (this.exceptionMarkers != null) {
this.exceptionMarkers.clear();
}
if (this.stackDepthMarkers != null) {
this.stackDepthMarkers.clear();
}
if (this.stackMarkers != null) {
this.stackMarkers.clear();
}
}
@Override
protected void writePosition(BranchLabel label) {
super.writePosition(label);
addFramePosition(label.position);
}
@Override
protected void writePosition(BranchLabel label, int forwardReference) {
super.writePosition(label, forwardReference);
addFramePosition(label.position);
}
@Override
protected void writeSignedWord(int pos, int value) {
super.writeSignedWord(pos, value);
addFramePosition(this.position);
}
@Override
protected void writeWidePosition(BranchLabel label) {
super.writeWidePosition(label);
addFramePosition(label.position);
}
@Override
public void areturn() {
super.areturn();
addFramePosition(this.position);
}
@Override
public void ireturn() {
super.ireturn();
addFramePosition(this.position);
}
@Override
public void lreturn() {
super.lreturn();
addFramePosition(this.position);
}
@Override
public void freturn() {
super.freturn();
addFramePosition(this.position);
}
@Override
public void dreturn() {
super.dreturn();
addFramePosition(this.position);
}
@Override
public void return_() {
super.return_();
addFramePosition(this.position);
}
@Override
public void athrow() {
super.athrow();
addFramePosition(this.position);
}
@Override
public void pushExceptionOnStack(TypeBinding binding) {
super.pushExceptionOnStack(binding);
addExceptionMarker(this.position, binding);
}
@Override
public void goto_(BranchLabel label) {
super.goto_(label);
addFramePosition(this.position);
}
@Override
public void goto_w(BranchLabel label) {
super.goto_w(label);
addFramePosition(this.position);
}
@Override
public void resetInWideMode() {
this.resetSecretLocals();
super.resetInWideMode();
}
@Override
public void resetForCodeGenUnusedLocals() {
this.resetSecretLocals();
super.resetForCodeGenUnusedLocals();
}
public void resetSecretLocals() {
for (int i = 0, max = this.locals.length; i < max; i++) {
LocalVariableBinding localVariableBinding = this.locals[i];
if (localVariableBinding != null && localVariableBinding.isSecret()) {
// all other locals are reinitialized inside the computation of their resolved positions
localVariableBinding.resetInitializations();
}
}
}
}