Index: compiler/org/eclipse/jdt/internal/compiler/ClassFile.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java,v retrieving revision 1.107 diff -u -r1.107 ClassFile.java --- compiler/org/eclipse/jdt/internal/compiler/ClassFile.java 4 May 2005 11:31:09 -0000 1.107 +++ compiler/org/eclipse/jdt/internal/compiler/ClassFile.java 20 May 2005 19:29:39 -0000 @@ -1237,6 +1237,9 @@ // 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); } } } @@ -1316,6 +1319,36 @@ } + public void addSyntheticSwitchTable(SyntheticMethodBinding methodBinding) { + generateMethodInfoHeader(methodBinding); + // We know that we won't get more than 1 attribute: the code attribute + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 2; + // Code attribute + int codeAttributeOffset = contentsOffset; + generateCodeAttributeHeader(); + codeStream.init(this); + codeStream.generateSyntheticBodyForSwitchTable(methodBinding); + completeCodeAttributeForSyntheticMethod( + true, + methodBinding, + codeAttributeOffset, + ((SourceTypeBinding) methodBinding.declaringClass) + .scope + .referenceCompilationUnit() + .compilationResult + .lineSeparatorPositions); + // add the synthetic attribute + int syntheticAttributeNameIndex = + constantPool.literalIndex(AttributeNamesConstants.SyntheticName); + contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); + contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; + // the length of a synthetic attribute is equals to 0 + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; + } /** * INTERNAL USE-ONLY * Generate the bytes for a synthetic method that implements Enum#values() for a given enum type @@ -2575,6 +2608,32 @@ SyntheticMethodBinding binding, int codeAttributeOffset, int[] startLineIndexes) { + + this.completeCodeAttributeForSyntheticMethod( + false, + binding, + codeAttributeOffset, + startLineIndexes); + } + + /** + * 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) { // reinitialize the contents with the byte modified by the code stream this.contents = codeStream.bCodeStream; int localContentsOffset = codeStream.classFileOffset; @@ -2596,10 +2655,60 @@ if ((localContentsOffset + 40) >= this.contents.length) { resizeContents(40); } - // there is no exception table, so we need to offset by 2 the current offset and move - // on the attribute generation - contents[localContentsOffset++] = 0; - contents[localContentsOffset++] = 0; + + if (hasExceptionHandlers) { + // write the exception table + int exceptionHandlersNumber = codeStream.exceptionHandlersCounter; + ExceptionLabel[] exceptionHandlers = codeStream.exceptionHandlers; + int exSize = exceptionHandlersNumber * 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) (exceptionHandlersNumber >> 8); + this.contents[localContentsOffset++] = (byte) exceptionHandlersNumber; + for (int i = 0, max = codeStream.exceptionHandlersIndex; i < max; i++) { + ExceptionLabel exceptionHandler = exceptionHandlers[i]; + if (exceptionHandler != null) { + int start = exceptionHandler.start; + this.contents[localContentsOffset++] = (byte) (start >> 8); + this.contents[localContentsOffset++] = (byte) start; + int end = exceptionHandler.end; + this.contents[localContentsOffset++] = (byte) (end >> 8); + this.contents[localContentsOffset++] = (byte) end; + int handlerPC = exceptionHandler.position; + this.contents[localContentsOffset++] = (byte) (handlerPC >> 8); + this.contents[localContentsOffset++] = (byte) handlerPC; + if (exceptionHandler.exceptionType == null) { + // any exception handler + this.contents[localContentsOffset++] = 0; + this.contents[localContentsOffset++] = 0; + } else { + int nameIndex; + switch(exceptionHandler.exceptionType.id) { + case T_null : + /* represents ClassNotFoundException, see class literal access*/ + nameIndex = constantPool.literalIndexForType(ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName); + break; + case T_long : + /* represents NoSuchFieldError, see switch table generation*/ + nameIndex = constantPool.literalIndexForType(ConstantPool.JavaLangNoSuchFieldErrorConstantPoolName); + break; + default: + nameIndex = constantPool.literalIndexForType(exceptionHandler.exceptionType.constantPoolName()); + } + 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 + contents[localContentsOffset++] = 0; + contents[localContentsOffset++] = 0; + } // debug attributes int codeAttributeAttributeOffset = localContentsOffset; int attributeNumber = 0; @@ -2769,7 +2878,7 @@ contents[codeAttributeOffset + 5] = (byte) codeAttributeLength; contentsOffset = localContentsOffset; } - + /** * INTERNAL USE-ONLY * Complete the creation of a method info by setting up the number of attributes at the right offset. Index: compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java,v retrieving revision 1.56 diff -u -r1.56 SwitchStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java 4 May 2005 11:31:09 -0000 1.56 +++ compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java 20 May 2005 19:29:39 -0000 @@ -10,6 +10,8 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import java.util.HashMap; + import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.*; @@ -32,6 +34,8 @@ public int caseCount; int[] constants; + public HashMap synthetics; // use for switch on enums types + // for local variables table attributes int preSwitchInitStateIndex = -1; int mergedInitStateIndex = -1; @@ -75,6 +79,14 @@ } } + final TypeBinding resolvedTypeBinding = this.expression.resolvedType; + if (caseCount > 0 && resolvedTypeBinding.isEnum()) { + final SourceTypeBinding sourceTypeBinding = this.scope.classScope().referenceContext.binding; + if (this.synthetics == null) { + this.synthetics = new HashMap(2); + } + this.synthetics.put(resolvedTypeBinding, sourceTypeBinding.addSyntheticMethodForSwitchEnum(resolvedTypeBinding)); + } // if no default case, then record it may jump over the block directly to the end if (defaultCase == null) { // only retain the potential initializations @@ -120,8 +132,24 @@ if (defaultCase != null) { defaultCase.targetLabel = defaultLabel; } - // generate expression testes - expression.generateCode(currentScope, codeStream, needSwitch); + + final TypeBinding resolvedType = this.expression.resolvedType; + if (resolvedType.isEnum()) { + if (needSwitch) { + // go through the translation table + codeStream.invokestatic((SyntheticMethodBinding) this.synthetics.get(resolvedType)); + expression.generateCode(currentScope, codeStream, true); + // get enum constant ordinal() + codeStream.invokeEnumOrdinal(resolvedType.constantPoolName()); + codeStream.iaload(); + } else { + // no need to go through the translation table + expression.generateCode(currentScope, codeStream, false); + } + } else { + // generate expression + expression.generateCode(currentScope, codeStream, needSwitch); // value required (switch without cases) + } // generate the appropriate switch table/lookup bytecode if (needSwitch) { int[] sortedIndexes = new int[this.caseCount]; @@ -133,10 +161,6 @@ System.arraycopy(this.constants, 0, (localKeysCopy = new int[this.caseCount]), 0, this.caseCount); CodeStream.sort(localKeysCopy, 0, this.caseCount - 1, sortedIndexes); - // for enum constants, actually switch on constant ordinal() - if (this.expression.resolvedType.isEnum()) { - codeStream.invokeEnumOrdinal(this.expression.resolvedType.constantPoolName()); - } int max = localKeysCopy[this.caseCount - 1]; int min = localKeysCopy[0]; if ((long) (caseCount * 2.5) > ((long) max - (long) min)) { Index: compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java,v retrieving revision 1.105 diff -u -r1.105 CodeStream.java --- compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java 4 May 2005 11:31:09 -0000 1.105 +++ compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java 20 May 2005 19:29:40 -0000 @@ -10,11 +10,13 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.codegen; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.*; import org.eclipse.jdt.internal.compiler.impl.*; import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.classfmt.*; +import org.eclipse.jdt.internal.compiler.env.IConstants; import org.eclipse.jdt.internal.compiler.flow.*; import org.eclipse.jdt.internal.compiler.lookup.*; @@ -1998,6 +2000,52 @@ this.invokeJavaLangIllegalArgumentExceptionStringConstructor(); this.athrow(); } +public void generateSyntheticBodyForSwitchTable(SyntheticMethodBinding methodBinding) { + ClassScope scope = ((SourceTypeBinding)methodBinding.declaringClass).scope; + initializeMaxLocals(methodBinding); + final Label nullLabel = new Label(this); + FieldBinding syntheticFieldBinding = methodBinding.targetReadField; + + this.getstatic(syntheticFieldBinding); + this.dup(); + this.ifnonnull(nullLabel); + this.pop(); + ReferenceBinding enumBinding = (ReferenceBinding) methodBinding.targetEnumType; + char[] signature = "()".toCharArray(); //$NON-NLS-1$ + ArrayBinding arrayBinding = scope.createArrayType(enumBinding, 1); + signature = CharOperation.concat(signature, arrayBinding.constantPoolName()); + this.invoke(OPC_invokestatic, 0, 1, enumBinding.constantPoolName(), TypeConstants.VALUES, signature); + this.arraylength(); + this.newarray(INT_ARRAY); + this.putstatic(syntheticFieldBinding); + final FieldBinding[] fields = enumBinding.fields(); + if (fields != null) { + for (int i = 0, max = fields.length; i < max; i++) { + FieldBinding fieldBinding = fields[i]; + if ((fieldBinding.getAccessFlags() & IConstants.AccEnum) != 0) { + final Label endLabel = new Label(this); + final ExceptionLabel anyExceptionHandler = new ExceptionLabel(this, BaseTypes.LongBinding /* represents NoSuchFieldError*/); + this.getstatic(syntheticFieldBinding); + this.getstatic(fieldBinding); + this.invokeEnumOrdinal(enumBinding.constantPoolName()); + this.generateInlinedValue(fieldBinding.id); + this.iastore(); + anyExceptionHandler.placeEnd(); + this.goto_(endLabel); + // Generate the body of the exception handler + final int saveStackSize = stackDepth; + stackDepth = 1; + anyExceptionHandler.place(); + this.pop(); // we don't use it so we can pop it + stackDepth = saveStackSize; + endLabel.place(); + } + } + } + this.getstatic(syntheticFieldBinding); + nullLabel.place(); + areturn(); +} public void generateSyntheticBodyForFieldReadAccess(SyntheticMethodBinding accessBinding) { initializeMaxLocals(accessBinding); FieldBinding fieldBinding = accessBinding.targetReadField; Index: compiler/org/eclipse/jdt/internal/compiler/codegen/ConstantPool.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/ConstantPool.java,v retrieving revision 1.42 diff -u -r1.42 ConstantPool.java --- compiler/org/eclipse/jdt/internal/compiler/codegen/ConstantPool.java 23 Feb 2005 02:47:47 -0000 1.42 +++ compiler/org/eclipse/jdt/internal/compiler/codegen/ConstantPool.java 20 May 2005 19:29:40 -0000 @@ -150,6 +150,7 @@ public static final char[] JavaLangIntegerConstantPoolName = "java/lang/Integer".toCharArray(); //$NON-NLS-1$ public static final char[] JavaLangLongConstantPoolName = "java/lang/Long".toCharArray(); //$NON-NLS-1$ public static final char[] JavaLangNoClassDefFoundErrorConstantPoolName = "java/lang/NoClassDefFoundError".toCharArray(); //$NON-NLS-1$ + public static final char[] JavaLangNoSuchFieldErrorConstantPoolName = "java/lang/NoSuchFieldError".toCharArray(); //$NON-NLS-1$ public static final char[] JavaLangObjectConstantPoolName = "java/lang/Object".toCharArray(); //$NON-NLS-1$ public static final char[] JAVALANGREFLECTACCESSIBLEOBJECT_CONSTANTPOOLNAME = "java/lang/reflect/AccessibleObject".toCharArray(); //$NON-NLS-1$ public static final char[] JAVALANGREFLECTARRAY_CONSTANTPOOLNAME = "java/lang/reflect/Array".toCharArray(); //$NON-NLS-1$ Index: compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java,v retrieving revision 1.102 diff -u -r1.102 SourceTypeBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java 11 May 2005 21:38:43 -0000 1.102 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java 20 May 2005 19:29:40 -0000 @@ -369,6 +369,78 @@ } return accessMethod; } +/* + * Add a synthetic field to handle the cache of the switch translation table for the corresponding enum type + */ +public SyntheticFieldBinding addSyntheticFieldForSwitchEnum(char[] fieldName, String key) { + if (synthetics == null) + synthetics = new HashMap[4]; + if (synthetics[FIELD_EMUL] == null) + synthetics[FIELD_EMUL] = new HashMap(5); + + SyntheticFieldBinding synthField = (SyntheticFieldBinding) synthetics[FIELD_EMUL].get(key); //$NON-NLS-1$ + if (synthField == null) { + synthField = new SyntheticFieldBinding( + fieldName, + scope.createArrayType(BaseTypes.IntBinding,1), + AccPrivate | AccStatic | AccSynthetic | AccFinal, + this, + Constant.NotAConstant, + synthetics[FIELD_EMUL].size()); + synthetics[FIELD_EMUL].put(key, synthField); + } + // ensure there is not already such a field defined by the user + boolean needRecheck; + int index = 0; + do { + needRecheck = false; + FieldBinding existingField; + if ((existingField = this.getField(synthField.name, true /*resolve*/)) != null) { + TypeDeclaration typeDecl = scope.referenceContext; + for (int i = 0, max = typeDecl.fields.length; i < max; i++) { + FieldDeclaration fieldDecl = typeDecl.fields[i]; + if (fieldDecl.binding == existingField) { + synthField.name = CharOperation.concat( + TypeConstants.SYNTHETIC_ENUM_VALUES, + ("_" + String.valueOf(index++)).toCharArray()); //$NON-NLS-1$ + needRecheck = true; + break; + } + } + } + } while (needRecheck); + return synthField; +} +/* Add a new synthetic method the enum type. Selector can either be 'values' or 'valueOf'. + * char[] constants from TypeConstants must be used: TypeConstants.VALUES/VALUEOF +*/ +public SyntheticMethodBinding addSyntheticMethodForSwitchEnum(TypeBinding enumBinding) { + if (synthetics == null) + synthetics = new HashMap[4]; + if (synthetics[METHOD_EMUL] == null) + synthetics[METHOD_EMUL] = new HashMap(5); + + SyntheticMethodBinding accessMethod = null; + char[] selector = CharOperation.concat(TypeConstants.SYNTHETIC_SWITCH_ENUM_TABLE, enumBinding.constantPoolName()); + CharOperation.replace(selector, '/', '$'); + final String key = new String(selector); + SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) synthetics[METHOD_EMUL].get(key); + // first add the corresponding synthetic field + if (accessors == null) { + // then create the synthetic method + final SyntheticFieldBinding fieldBinding = this.addSyntheticFieldForSwitchEnum(selector, key); + accessMethod = new SyntheticMethodBinding(fieldBinding, this, enumBinding, selector); + synthetics[METHOD_EMUL].put(key, accessors = new SyntheticMethodBinding[2]); + accessors[0] = accessMethod; + } else { + if ((accessMethod = accessors[0]) == null) { + final SyntheticFieldBinding fieldBinding = this.addSyntheticFieldForSwitchEnum(selector, key); + accessMethod = new SyntheticMethodBinding(fieldBinding, this, enumBinding, selector); + accessors[0] = accessMethod; + } + } + return accessMethod; +} /* Add a new synthetic access method for access to . * Must distinguish access method used for super access from others (need to use invokespecial bytecode) Answer the new method or the existing method if one already existed. Index: compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java,v retrieving revision 1.5 diff -u -r1.5 SyntheticMethodBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java 15 Mar 2005 10:43:16 -0000 1.5 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java 20 May 2005 19:29:40 -0000 @@ -18,7 +18,8 @@ public FieldBinding targetReadField; // read access to a field public FieldBinding targetWriteField; // write access to a field - public MethodBinding targetMethod; // method or constructor + public MethodBinding targetMethod; // method or constructor + public TypeBinding targetEnumType; // enum type public int kind; @@ -30,6 +31,7 @@ public final static int BridgeMethod = 6; // bridge method public final static int EnumValues = 7; // enum #values() public final static int EnumValueOf = 8; // enum #valueOf(String) + public final static int SwitchTable = 9; // switch table method public int sourceStart = 0; // start position of the matching declaration public int index; // used for sorting access methods in the class file @@ -128,6 +130,59 @@ this.sourceStart = declaringSourceType.scope.referenceContext.sourceStart; // use the target declaring class name position instead } + public SyntheticMethodBinding(FieldBinding targetField, ReferenceBinding declaringClass, TypeBinding enumBinding, char[] selector) { + this.modifiers = AccDefault | AccStatic | AccSynthetic; + this.tagBits |= TagBits.AnnotationResolved; + SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass; + SyntheticMethodBinding[] knownAccessMethods = declaringSourceType.syntheticMethods(); + int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length; + this.index = methodId; + this.selector = selector; + this.returnType = declaringSourceType.scope.createArrayType(BaseTypes.IntBinding, 1); + this.parameters = NoParameters; + this.targetReadField = targetField; + this.targetEnumType = enumBinding; + this.kind = SwitchTable; + this.thrownExceptions = NoExceptions; + this.declaringClass = declaringSourceType; + + if (declaringSourceType.isStrictfp()) { + this.modifiers |= AccStrictfp; + } + // check for method collision + boolean needRename; + do { + check : { + needRename = false; + // check for collision with known methods + MethodBinding[] methods = declaringSourceType.methods; + for (int i = 0, length = methods.length; i < length; i++) { + if (CharOperation.equals(this.selector, methods[i].selector) && this.areParametersEqual(methods[i])) { + needRename = true; + break check; + } + } + // check for collision with synthetic accessors + if (knownAccessMethods != null) { + for (int i = 0, length = knownAccessMethods.length; i < length; i++) { + if (knownAccessMethods[i] == null) continue; + if (CharOperation.equals(this.selector, knownAccessMethods[i].selector) && this.areParametersEqual(methods[i])) { + needRename = true; + break check; + } + } + } + } + if (needRename) { // retry with a selector postfixed by a growing methodId + this.setSelector(CharOperation.concat(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX, String.valueOf(++methodId).toCharArray())); + } + } while (needRename); + + // We now at this point - per construction - it is for sure an enclosing instance, we are going to + // show the target field type declaration location. + this.sourceStart = declaringSourceType.scope.referenceContext.sourceStart; // use the target declaring class name position instead + } + public SyntheticMethodBinding(MethodBinding targetMethod, boolean isSuperAccess, ReferenceBinding receiverType) { if (targetMethod.isConstructor()) { Index: compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java,v retrieving revision 1.37 diff -u -r1.37 TypeConstants.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java 21 Apr 2005 13:06:49 -0000 1.37 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java 20 May 2005 19:29:40 -0000 @@ -144,6 +144,7 @@ // Synthetics char[] INIT = "".toCharArray(); //$NON-NLS-1$ char[] CLINIT = "".toCharArray(); //$NON-NLS-1$ + char[] SYNTHETIC_SWITCH_ENUM_TABLE = "$SWITCH_TABLE$".toCharArray(); //$NON-NLS-1$ char[] SYNTHETIC_ENUM_VALUES = "ENUM$VALUES".toCharArray(); //$NON-NLS-1$ char[] SYNTHETIC_ASSERT_DISABLED = "$assertionsDisabled".toCharArray(); //$NON-NLS-1$ char[] SYNTHETIC_CLASS = "class$".toCharArray(); //$NON-NLS-1$