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
/******************************************************************************* * 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 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() { StringBuffer buffer = new StringBuffer(); 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
/** * 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(); } } } }