From c17753cd9e62cd1a71df3d88af908de0425ac33d Mon Sep 17 00:00:00 2001 From: crawshaw Date: Fri, 31 Dec 2004 10:07:14 +0000 Subject: [PATCH] import eclipse 3.1 M4 compiler darcs-hash:20041231100714-2eb37-8bdd1b5af884c3d6c9242e3e7964c2b8964ad7c3.gz --- src/org/eclipse/jdt/core/Signature.java | 2809 ++++++++ .../eclipse/jdt/core/compiler/CharOperation.java | 107 +- src/org/eclipse/jdt/core/compiler/IProblem.java | 350 +- .../jdt/core/compiler/InvalidInputException.java | 4 +- .../eclipse/jdt/internal/compiler/ASTVisitor.java | 311 +- .../eclipse/jdt/internal/compiler/ClassFile.java | 2336 +++++-- .../jdt/internal/compiler/CompilationResult.java | 6 + .../eclipse/jdt/internal/compiler/Compiler.java | 26 +- .../jdt/internal/compiler/ConfigurableOption.java | 227 - .../eclipse/jdt/internal/compiler/ast/ASTNode.java | 174 +- .../compiler/ast/AbstractMethodDeclaration.java | 38 +- .../compiler/ast/AbstractVariableDeclaration.java | 47 +- .../compiler/ast/AllocationExpression.java | 149 +- .../jdt/internal/compiler/ast/Annotation.java | 299 + .../compiler/ast/AnnotationMethodDeclaration.java | 169 + .../jdt/internal/compiler/ast/Argument.java | 50 +- .../compiler/ast/ArrayAllocationExpression.java | 19 +- .../internal/compiler/ast/ArrayInitializer.java | 33 +- .../compiler/ast/ArrayQualifiedTypeReference.java | 47 +- .../jdt/internal/compiler/ast/ArrayReference.java | 49 +- .../internal/compiler/ast/ArrayTypeReference.java | 52 +- .../jdt/internal/compiler/ast/AssertStatement.java | 9 +- .../jdt/internal/compiler/ast/Assignment.java | 90 +- .../internal/compiler/ast/BinaryExpression.java | 112 +- .../jdt/internal/compiler/ast/CaseStatement.java | 45 +- .../jdt/internal/compiler/ast/CastExpression.java | 287 +- .../jdt/internal/compiler/ast/CharLiteral.java | 8 +- .../internal/compiler/ast/ClassLiteralAccess.java | 28 +- .../eclipse/jdt/internal/compiler/ast/Clinit.java | 78 +- .../compiler/ast/CompilationUnitDeclaration.java | 9 +- .../internal/compiler/ast/CompoundAssignment.java | 52 +- .../compiler/ast/ConditionalExpression.java | 79 +- .../compiler/ast/ConstructorDeclaration.java | 56 +- .../jdt/internal/compiler/ast/DoStatement.java | 19 +- .../jdt/internal/compiler/ast/DoubleLiteral.java | 134 +- .../jdt/internal/compiler/ast/EqualExpression.java | 869 ++- .../compiler/ast/ExplicitConstructorCall.java | 129 +- .../jdt/internal/compiler/ast/Expression.java | 570 +- .../jdt/internal/compiler/ast/FalseLiteral.java | 9 +- .../internal/compiler/ast/FieldDeclaration.java | 121 +- .../jdt/internal/compiler/ast/FieldReference.java | 243 +- .../jdt/internal/compiler/ast/FloatLiteral.java | 151 +- .../jdt/internal/compiler/ast/ForStatement.java | 42 +- .../internal/compiler/ast/ForeachStatement.java | 461 ++ .../jdt/internal/compiler/ast/IfStatement.java | 15 +- .../compiler/ast/ImplicitDocTypeReference.java | 9 +- .../jdt/internal/compiler/ast/ImportReference.java | 10 + .../jdt/internal/compiler/ast/Initializer.java | 16 +- .../compiler/ast/InstanceOfExpression.java | 163 +- .../jdt/internal/compiler/ast/IntLiteral.java | 10 +- .../eclipse/jdt/internal/compiler/ast/Javadoc.java | 321 +- .../compiler/ast/JavadocAllocationExpression.java | 5 +- .../compiler/ast/JavadocArgumentExpression.java | 36 +- .../ast/JavadocArrayQualifiedTypeReference.java | 6 + .../ast/JavadocArraySingleTypeReference.java | 6 + .../compiler/ast/JavadocFieldReference.java | 23 +- .../compiler/ast/JavadocImportReference.java | 32 - .../internal/compiler/ast/JavadocMessageSend.java | 26 +- .../ast/JavadocQualifiedTypeReference.java | 45 +- .../compiler/ast/JavadocReturnStatement.java | 18 +- .../compiler/ast/JavadocSingleNameReference.java | 10 +- .../compiler/ast/JavadocSingleTypeReference.java | 45 +- .../internal/compiler/ast/LocalDeclaration.java | 85 +- .../jdt/internal/compiler/ast/LongLiteral.java | 10 +- .../internal/compiler/ast/MarkerAnnotation.java | 35 + .../jdt/internal/compiler/ast/MemberValuePair.java | 154 + .../jdt/internal/compiler/ast/MessageSend.java | 243 +- .../internal/compiler/ast/MethodDeclaration.java | 75 +- .../jdt/internal/compiler/ast/NameReference.java | 5 +- .../internal/compiler/ast/NormalAnnotation.java | 70 + .../jdt/internal/compiler/ast/NullLiteral.java | 9 +- .../internal/compiler/ast/OperatorExpression.java | 148 +- .../ast/ParameterizedQualifiedTypeReference.java | 300 + .../ast/ParameterizedSingleTypeReference.java | 232 + .../ast/QualifiedAllocationExpression.java | 132 +- .../compiler/ast/QualifiedNameReference.java | 412 +- .../compiler/ast/QualifiedSuperReference.java | 2 +- .../compiler/ast/QualifiedThisReference.java | 18 +- .../compiler/ast/QualifiedTypeReference.java | 62 +- .../jdt/internal/compiler/ast/Reference.java | 15 + .../jdt/internal/compiler/ast/ReturnStatement.java | 45 +- .../compiler/ast/SingleMemberAnnotation.java | 62 + .../internal/compiler/ast/SingleNameReference.java | 250 +- .../internal/compiler/ast/SingleTypeReference.java | 30 +- .../jdt/internal/compiler/ast/Statement.java | 61 +- .../compiler/ast/StringLiteralConcatenation.java | 2 +- .../internal/compiler/ast/SubRoutineStatement.java | 4 +- .../jdt/internal/compiler/ast/SuperReference.java | 2 +- .../jdt/internal/compiler/ast/SwitchStatement.java | 152 +- .../compiler/ast/SynchronizedStatement.java | 16 +- .../jdt/internal/compiler/ast/ThisReference.java | 4 + .../jdt/internal/compiler/ast/ThrowStatement.java | 2 +- .../jdt/internal/compiler/ast/TrueLiteral.java | 9 +- .../jdt/internal/compiler/ast/TryStatement.java | 87 +- .../jdt/internal/compiler/ast/TypeDeclaration.java | 344 +- .../jdt/internal/compiler/ast/TypeParameter.java | 99 + .../jdt/internal/compiler/ast/TypeReference.java | 91 +- .../jdt/internal/compiler/ast/UnaryExpression.java | 31 +- .../jdt/internal/compiler/ast/WhileStatement.java | 31 +- .../jdt/internal/compiler/ast/Wildcard.java | 113 + .../compiler/classfmt/ClassFileConstants.java | 11 +- .../compiler/classfmt/ClassFileReader.java | 679 +- .../compiler/classfmt/ClassFormatException.java | 1 + .../jdt/internal/compiler/classfmt/FieldInfo.java | 197 +- .../jdt/internal/compiler/classfmt/MethodInfo.java | 236 +- .../compiler/codegen/AttributeNamesConstants.java | 26 +- .../jdt/internal/compiler/codegen/CodeStream.java | 1476 +++-- .../internal/compiler/codegen/ConstantPool.java | 3018 ++------- .../compiler/codegen/FieldNameAndTypeCache.java | 161 - .../compiler/codegen/MethodNameAndTypeCache.java | 162 - .../compiler/codegen/QualifiedNamesConstants.java | 93 - .../internal/compiler/env/AccessRestriction.java | 120 + .../jdt/internal/compiler/env/IBinaryField.java | 10 + .../jdt/internal/compiler/env/IBinaryMethod.java | 13 +- .../jdt/internal/compiler/env/IBinaryType.java | 15 +- .../jdt/internal/compiler/env/IConstants.java | 44 +- .../jdt/internal/compiler/env/IDependent.java | 17 +- .../jdt/internal/compiler/env/IGenericType.java | 21 +- .../jdt/internal/compiler/env/ISourceMethod.java | 10 + .../jdt/internal/compiler/env/ISourceType.java | 17 +- .../compiler/env/NameEnvironmentAnswer.java | 17 +- .../compiler/flow/ConditionalFlowInfo.java | 72 + .../internal/compiler/flow/FinallyFlowContext.java | 61 +- .../jdt/internal/compiler/flow/FlowContext.java | 47 +- .../jdt/internal/compiler/flow/FlowInfo.java | 46 +- .../internal/compiler/flow/LoopingFlowContext.java | 56 +- .../compiler/flow/UnconditionalFlowInfo.java | 334 +- .../internal/compiler/impl/CompilerOptions.java | 145 +- .../jdt/internal/compiler/impl/Constant.java | 111 +- .../jdt/internal/compiler/impl/ITypeRequestor.java | 7 +- .../jdt/internal/compiler/impl/StringConstant.java | 2 +- .../jdt/internal/compiler/lookup/ArrayBinding.java | 88 +- .../jdt/internal/compiler/lookup/BaseTypes.java | 20 +- .../compiler/lookup/BinaryTypeBinding.java | 596 +- .../jdt/internal/compiler/lookup/Binding.java | 40 +- .../jdt/internal/compiler/lookup/BlockScope.java | 30 +- .../jdt/internal/compiler/lookup/ClassScope.java | 644 +- .../compiler/lookup/CompilationUnitScope.java | 286 +- .../compiler/lookup/CompilerModifiers.java | 40 +- .../jdt/internal/compiler/lookup/FieldBinding.java | 80 +- .../internal/compiler/lookup/ImportBinding.java | 7 +- .../internal/compiler/lookup/InvocationSite.java | 2 + .../internal/compiler/lookup/LocalTypeBinding.java | 60 +- .../compiler/lookup/LocalVariableBinding.java | 59 +- .../compiler/lookup/LookupEnvironment.java | 513 +- .../internal/compiler/lookup/MethodBinding.java | 239 +- .../jdt/internal/compiler/lookup/MethodScope.java | 67 +- .../internal/compiler/lookup/MethodVerifier.java | 337 +- .../internal/compiler/lookup/MethodVerifier15.java | 228 + .../compiler/lookup/NestedTypeBinding.java | 6 +- .../internal/compiler/lookup/PackageBinding.java | 32 +- .../compiler/lookup/ParameterizedFieldBinding.java | 59 + .../lookup/ParameterizedGenericMethodBinding.java | 379 ++ .../lookup/ParameterizedMethodBinding.java | 99 + .../compiler/lookup/ParameterizedTypeBinding.java | 856 +++ .../internal/compiler/lookup/ProblemBinding.java | 2 +- .../compiler/lookup/ProblemMethodBinding.java | 20 +- .../internal/compiler/lookup/ProblemReasons.java | 6 + .../compiler/lookup/ProblemReferenceBinding.java | 25 +- .../internal/compiler/lookup/RawTypeBinding.java | 202 + .../internal/compiler/lookup/ReferenceBinding.java | 528 +- .../jdt/internal/compiler/lookup/Scope.java | 1718 ++++- .../internal/compiler/lookup/SignatureWrapper.java | 68 + .../compiler/lookup/SourceTypeBinding.java | 951 ++- .../lookup/{BindingIds.java => Substitution.java} | 18 +- .../compiler/lookup/SyntheticArgumentBinding.java | 7 +- ...hodBinding.java => SyntheticMethodBinding.java} | 99 +- .../jdt/internal/compiler/lookup/TagBits.java | 92 +- .../jdt/internal/compiler/lookup/TypeBinding.java | 337 +- .../internal/compiler/lookup/TypeConstants.java | 181 +- .../jdt/internal/compiler/lookup/TypeIds.java | 64 +- .../compiler/lookup/TypeVariableBinding.java | 347 + .../lookup/UnresolvedReferenceBinding.java | 66 +- .../internal/compiler/lookup/VariableBinding.java | 62 +- .../internal/compiler/lookup/WildcardBinding.java | 323 + .../compiler/parser/AbstractCommentParser.java | 813 +-- .../internal/compiler/parser/JavadocParser.java | 352 +- .../jdt/internal/compiler/parser/Parser.java | 6933 ++++++++++++++------ .../compiler/parser/ParserBasicInformation.java | 39 +- .../internal/compiler/parser/RecoveredField.java | 34 +- .../compiler/parser/RecoveredInitializer.java | 2 +- .../internal/compiler/parser/RecoveredMethod.java | 15 +- .../internal/compiler/parser/RecoveredType.java | 67 +- .../internal/compiler/parser/RecoveredUnit.java | 14 + .../jdt/internal/compiler/parser/Scanner.java | 231 +- .../internal/compiler/parser/TerminalTokens.java | 225 +- .../compiler/parser/diagnose/DiagnoseParser.java | 87 +- .../compiler/parser/diagnose/RangeUtil.java | 2 +- .../jdt/internal/compiler/parser/parser1.rsc | Bin 12274 -> 25302 bytes .../jdt/internal/compiler/parser/parser10.rsc | Bin 128 -> 266 bytes .../jdt/internal/compiler/parser/parser11.rsc | Bin 128 -> 266 bytes .../jdt/internal/compiler/parser/parser12.rsc | Bin 128 -> 266 bytes .../jdt/internal/compiler/parser/parser13.rsc | Bin 128 -> 266 bytes .../jdt/internal/compiler/parser/parser14.rsc | Bin 640 -> 1228 bytes .../jdt/internal/compiler/parser/parser15.rsc | Bin 470 -> 992 bytes .../jdt/internal/compiler/parser/parser16.rsc | Bin 1196 -> 1908 bytes .../jdt/internal/compiler/parser/parser17.rsc | Bin 434 -> 692 bytes .../jdt/internal/compiler/parser/parser18.rsc | Bin 5790 -> 8096 bytes .../jdt/internal/compiler/parser/parser19.rsc | 2 +- .../jdt/internal/compiler/parser/parser2.rsc | Bin 11406 -> 23918 bytes .../jdt/internal/compiler/parser/parser20.rsc | Bin 8526 -> 12936 bytes .../jdt/internal/compiler/parser/parser21.rsc | Bin 0 -> 5536 bytes .../jdt/internal/compiler/parser/parser3.rsc | Bin 1196 -> 1908 bytes .../jdt/internal/compiler/parser/parser4.rsc | Bin 2310 -> 3410 bytes .../jdt/internal/compiler/parser/parser5.rsc | Bin 1196 -> 1908 bytes .../jdt/internal/compiler/parser/parser6.rsc | Bin 570 -> 924 bytes .../jdt/internal/compiler/parser/parser7.rsc | Bin 212 -> 218 bytes .../jdt/internal/compiler/parser/parser8.rsc | Bin 408 -> 616 bytes .../jdt/internal/compiler/parser/parser9.rsc | Bin 11424 -> 16032 bytes .../compiler/parser/readableNames.properties | 120 +- .../compiler/problem/AbortCompilationUnit.java | 4 +- .../jdt/internal/compiler/problem/AbortMethod.java | 4 +- .../jdt/internal/compiler/problem/AbortType.java | 4 +- .../internal/compiler/problem/ProblemReporter.java | 3075 ++++++--- .../compiler/problem/ShouldNotImplement.java | 4 +- .../internal/compiler/problem/messages.properties | 245 +- .../jdt/internal/compiler/util/FloatUtil.java | 421 ++ .../compiler/util/HashtableOfIntValues.java | 148 - .../internal/compiler/util/HashtableOfLong.java | 92 - .../internal/compiler/util/SimpleLookupTable.java | 156 + .../eclipse/jdt/internal/compiler/util/Util.java | 198 +- .../jdt/internal/compiler/util/messages.properties | 7 +- src/org/ibex/tool/Compiler.java | 214 +- 223 files changed, 32138 insertions(+), 13619 deletions(-) create mode 100644 src/org/eclipse/jdt/core/Signature.java delete mode 100644 src/org/eclipse/jdt/internal/compiler/ConfigurableOption.java create mode 100644 src/org/eclipse/jdt/internal/compiler/ast/Annotation.java create mode 100644 src/org/eclipse/jdt/internal/compiler/ast/AnnotationMethodDeclaration.java create mode 100644 src/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java delete mode 100644 src/org/eclipse/jdt/internal/compiler/ast/JavadocImportReference.java create mode 100644 src/org/eclipse/jdt/internal/compiler/ast/MarkerAnnotation.java create mode 100644 src/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java create mode 100644 src/org/eclipse/jdt/internal/compiler/ast/NormalAnnotation.java create mode 100644 src/org/eclipse/jdt/internal/compiler/ast/ParameterizedQualifiedTypeReference.java create mode 100644 src/org/eclipse/jdt/internal/compiler/ast/ParameterizedSingleTypeReference.java create mode 100644 src/org/eclipse/jdt/internal/compiler/ast/SingleMemberAnnotation.java create mode 100644 src/org/eclipse/jdt/internal/compiler/ast/TypeParameter.java create mode 100644 src/org/eclipse/jdt/internal/compiler/ast/Wildcard.java delete mode 100644 src/org/eclipse/jdt/internal/compiler/codegen/FieldNameAndTypeCache.java delete mode 100644 src/org/eclipse/jdt/internal/compiler/codegen/MethodNameAndTypeCache.java delete mode 100644 src/org/eclipse/jdt/internal/compiler/codegen/QualifiedNamesConstants.java create mode 100644 src/org/eclipse/jdt/internal/compiler/env/AccessRestriction.java create mode 100644 src/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java create mode 100644 src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedFieldBinding.java create mode 100644 src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java create mode 100644 src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedMethodBinding.java create mode 100644 src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java create mode 100644 src/org/eclipse/jdt/internal/compiler/lookup/RawTypeBinding.java create mode 100644 src/org/eclipse/jdt/internal/compiler/lookup/SignatureWrapper.java rename src/org/eclipse/jdt/internal/compiler/lookup/{BindingIds.java => Substitution.java} (69%) rename src/org/eclipse/jdt/internal/compiler/lookup/{SyntheticAccessMethodBinding.java => SyntheticMethodBinding.java} (69%) create mode 100644 src/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java create mode 100644 src/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java create mode 100644 src/org/eclipse/jdt/internal/compiler/parser/parser21.rsc create mode 100644 src/org/eclipse/jdt/internal/compiler/util/FloatUtil.java delete mode 100644 src/org/eclipse/jdt/internal/compiler/util/HashtableOfIntValues.java delete mode 100644 src/org/eclipse/jdt/internal/compiler/util/HashtableOfLong.java create mode 100644 src/org/eclipse/jdt/internal/compiler/util/SimpleLookupTable.java diff --git a/src/org/eclipse/jdt/core/Signature.java b/src/org/eclipse/jdt/core/Signature.java new file mode 100644 index 0000000..7a2fa00 --- /dev/null +++ b/src/org/eclipse/jdt/core/Signature.java @@ -0,0 +1,2809 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * IBM Corporation - added J2SE 1.5 support + *******************************************************************************/ +package org.eclipse.jdt.core; + +import java.util.ArrayList; + +import org.eclipse.jdt.core.compiler.CharOperation; + + +/** + * Provides methods for encoding and decoding type and method signature strings. + *

+ * Signatures obtained from parsing source (".java") files differ subtly from + * ones obtained from pre-compiled binary (".class") files in class names are + * usually left unresolved in the former. For example, the normal resolved form + * of the type "String" embeds the class's package name ("Ljava.lang.String;" + * or "Ljava/lang/String;"), whereas the unresolved form contains only what is + * written "QString;". + *

+ *

+ * Generic types introduce to the Java language in J2SE 1.5 add three new + * facets to signatures: type variables, parameterized types with type arguments, + * and formal type parameters. Rich signatures containing these facets + * only occur when dealing with code that makes overt use of the new language + * features. All other code, and certainly all Java code written or compiled + * with J2SE 1.4 or earlier, involved only simple signatures. + *

+ *

+ * The syntax for a type signature is: + *

+ * TypeSignature ::=
+ *     "B"  // byte
+ *   | "C"  // char
+ *   | "D"  // double
+ *   | "F"  // float
+ *   | "I"  // int
+ *   | "J"  // long
+ *   | "S"  // short
+ *   | "V"  // void
+ *   | "Z"  // boolean
+ *   | "T" + Identifier + ";" // type variable
+ *   | "[" + TypeSignature  // array X[]
+ *   | ResolvedClassTypeSignature
+ *   | UnresolvedClassTypeSignature
+ * 
+ * ResolvedClassTypeSignature ::= // resolved named type (in compiled code)
+ *     "L" + Identifier + OptionalTypeArguments
+ *           ( ( "." | "/" ) + Identifier + OptionalTypeArguments )* + ";"
+ * 
+ * UnresolvedClassTypeSignature ::= // unresolved named type (in source code)
+ *     "Q" + Identifier + OptionalTypeArguments
+ *           ( ( "." | "/" ) + Identifier + OptionalTypeArguments )* + ";"
+ * 
+ * OptionalTypeArguments ::=
+ *     "<" + TypeArgument+ + ">" 
+ *   |
+ * 
+ * TypeArgument ::=
+ *   | TypeSignature
+ *   | "*" // wildcard ?
+ *   | "+" TypeSignature // wildcard ? extends X
+ *   | "-" TypeSignature // wildcard ? super X
+ * 
+ *

+ *

+ * Examples: + *

+ *

+ *

+ * The syntax for a method signature is: + *

+ * MethodSignature ::= "(" + ParamTypeSignature* + ")" + ReturnTypeSignature
+ * ParamTypeSignature ::= TypeSignature
+ * ReturnTypeSignature ::= TypeSignature
+ * 
+ *

+ * Examples: + *

+ *

+ *

+ * The syntax for a formal type parameter signature is: + *

+ * FormalTypeParameterSignature ::=
+ *     TypeVariableName + OptionalClassBound + InterfaceBound*
+ * TypeVariableName ::= Identifier
+ * OptionalClassBound ::=
+ *     ":"
+ *   | ":" + TypeSignature
+ * InterfaceBound ::= 
+ *     ":" + TypeSignature
+ * 
+ *

+ * Examples: + *

+ *

+ *

+ * This class provides static methods and constants only; it is not intended to be + * instantiated or subclassed by clients. + *

+ */ +public final class Signature { + + /** + * Character constant indicating the primitive type boolean in a signature. + * Value is 'Z'. + */ + public static final char C_BOOLEAN = 'Z'; + + /** + * Character constant indicating the primitive type byte in a signature. + * Value is 'B'. + */ + public static final char C_BYTE = 'B'; + + /** + * Character constant indicating the primitive type char in a signature. + * Value is 'C'. + */ + public static final char C_CHAR = 'C'; + + /** + * Character constant indicating the primitive type double in a signature. + * Value is 'D'. + */ + public static final char C_DOUBLE = 'D'; + + /** + * Character constant indicating the primitive type float in a signature. + * Value is 'F'. + */ + public static final char C_FLOAT = 'F'; + + /** + * Character constant indicating the primitive type int in a signature. + * Value is 'I'. + */ + public static final char C_INT = 'I'; + + /** + * Character constant indicating the semicolon in a signature. + * Value is ';'. + */ + public static final char C_SEMICOLON = ';'; + + /** + * Character constant indicating the colon in a signature. + * Value is ':'. + * @since 3.0 + */ + public static final char C_COLON = ':'; + + /** + * Character constant indicating the primitive type long in a signature. + * Value is 'J'. + */ + public static final char C_LONG = 'J'; + + /** + * Character constant indicating the primitive type short in a signature. + * Value is 'S'. + */ + public static final char C_SHORT = 'S'; + + /** + * Character constant indicating result type void in a signature. + * Value is 'V'. + */ + public static final char C_VOID = 'V'; + + /** + * Character constant indicating the start of a resolved type variable in a + * signature. Value is 'T'. + * @since 3.0 + */ + public static final char C_TYPE_VARIABLE = 'T'; + + /** + * Character constant indicating an unbound wildcard type argument + * in a signature. + * Value is '*'. + * @since 3.0 + */ + public static final char C_STAR = '*'; + + /** + * Character constant indicating a bound wildcard type argument + * in a signature with extends clause. + * Value is '+'. + * @since 3.1 + */ + public static final char C_EXTENDS = '+'; + + /** + * Character constant indicating a bound wildcard type argument + * in a signature with super clause. + * Value is '-'. + * @since 3.1 + */ + public static final char C_SUPER = '-'; + + /** + * Character constant indicating the dot in a signature. + * Value is '.'. + */ + public static final char C_DOT = '.'; + + /** + * Character constant indicating the dollar in a signature. + * Value is '$'. + */ + public static final char C_DOLLAR = '$'; + + /** + * Character constant indicating an array type in a signature. + * Value is '['. + */ + public static final char C_ARRAY = '['; + + /** + * Character constant indicating the start of a resolved, named type in a + * signature. Value is 'L'. + */ + public static final char C_RESOLVED = 'L'; + + /** + * Character constant indicating the start of an unresolved, named type in a + * signature. Value is 'Q'. + */ + public static final char C_UNRESOLVED = 'Q'; + + /** + * Character constant indicating the end of a named type in a signature. + * Value is ';'. + */ + public static final char C_NAME_END = ';'; + + /** + * Character constant indicating the start of a parameter type list in a + * signature. Value is '('. + */ + public static final char C_PARAM_START = '('; + + /** + * Character constant indicating the end of a parameter type list in a + * signature. Value is ')'. + */ + public static final char C_PARAM_END = ')'; + + /** + * Character constant indicating the start of a formal type parameter + * (or type argument) list in a signature. Value is '<'. + * @since 3.0 + */ + public static final char C_GENERIC_START = '<'; + + /** + * Character constant indicating the end of a generic type list in a + * signature. Value is '%gt;'. + * @since 3.0 + */ + public static final char C_GENERIC_END = '>'; + + /** + * String constant for the signature of the primitive type boolean. + * Value is "Z". + */ + public static final String SIG_BOOLEAN = "Z"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type byte. + * Value is "B". + */ + public static final String SIG_BYTE = "B"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type char. + * Value is "C". + */ + public static final String SIG_CHAR = "C"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type double. + * Value is "D". + */ + public static final String SIG_DOUBLE = "D"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type float. + * Value is "F". + */ + public static final String SIG_FLOAT = "F"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type int. + * Value is "I". + */ + public static final String SIG_INT = "I"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type long. + * Value is "J". + */ + public static final String SIG_LONG = "J"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type short. + * Value is "S". + */ + public static final String SIG_SHORT = "S"; //$NON-NLS-1$ + + /** String constant for the signature of result type void. + * Value is "V". + */ + public static final String SIG_VOID = "V"; //$NON-NLS-1$ + + + /** + * Kind constant for a class type signature. + * @see #getTypeSignatureKind(String) + * @since 3.0 + */ + public static int CLASS_TYPE_SIGNATURE = 1; + + /** + * Kind constant for a base (primitive or void) type signature. + * @see #getTypeSignatureKind(String) + * @since 3.0 + */ + public static int BASE_TYPE_SIGNATURE = 2; + + /** + * Kind constant for a type variable signature. + * @see #getTypeSignatureKind(String) + * @since 3.0 + */ + public static int TYPE_VARIABLE_SIGNATURE = 3; + + /** + * Kind constant for an array type signature. + * @see #getTypeSignatureKind(String) + * @since 3.0 + */ + public static int ARRAY_TYPE_SIGNATURE = 4; + + private static final char[] BOOLEAN = "boolean".toCharArray(); //$NON-NLS-1$ + private static final char[] BYTE = "byte".toCharArray(); //$NON-NLS-1$ + private static final char[] CHAR = "char".toCharArray(); //$NON-NLS-1$ + private static final char[] DOUBLE = "double".toCharArray(); //$NON-NLS-1$ + private static final char[] FLOAT = "float".toCharArray(); //$NON-NLS-1$ + private static final char[] INT = "int".toCharArray(); //$NON-NLS-1$ + private static final char[] LONG = "long".toCharArray(); //$NON-NLS-1$ + private static final char[] SHORT = "short".toCharArray(); //$NON-NLS-1$ + private static final char[] VOID = "void".toCharArray(); //$NON-NLS-1$ + private static final char[] EXTENDS = "extends".toCharArray(); //$NON-NLS-1$ + private static final char[] SUPER = "super".toCharArray(); //$NON-NLS-1$ + + private static final String EMPTY = new String(CharOperation.NO_CHAR); + +private Signature() { + // Not instantiable +} + +private static int checkName(char[] name, char[] typeName, int pos, int length) { + if (CharOperation.fragmentEquals(name, typeName, pos, true)) { + pos += name.length; + if (pos == length) return pos; + char currentChar = typeName[pos]; + switch (currentChar) { + case ' ' : + case '.' : + case '<' : + case '>' : + case '[' : + case ',' : + return pos; + default: + if (Character.isWhitespace(currentChar)) + return pos; + + } + } + return -1; +} + +/** + * Creates a new type signature with the given amount of array nesting added + * to the given type signature. + * + * @param typeSignature the type signature + * @param arrayCount the desired number of levels of array nesting + * @return the encoded array type signature + * + * @since 2.0 + */ +public static char[] createArraySignature(char[] typeSignature, int arrayCount) { + if (arrayCount == 0) return typeSignature; + int sigLength = typeSignature.length; + char[] result = new char[arrayCount + sigLength]; + for (int i = 0; i < arrayCount; i++) { + result[i] = C_ARRAY; + } + System.arraycopy(typeSignature, 0, result, arrayCount, sigLength); + return result; +} +/** + * Creates a new type signature with the given amount of array nesting added + * to the given type signature. + * + * @param typeSignature the type signature + * @param arrayCount the desired number of levels of array nesting + * @return the encoded array type signature + */ +public static String createArraySignature(String typeSignature, int arrayCount) { + return new String(createArraySignature(typeSignature.toCharArray(), arrayCount)); +} + +/** + * Creates a method signature from the given parameter and return type + * signatures. The encoded method signature is dot-based. + * + * @param parameterTypes the list of parameter type signatures + * @param returnType the return type signature + * @return the encoded method signature + * + * @since 2.0 + */ +public static char[] createMethodSignature(char[][] parameterTypes, char[] returnType) { + int parameterTypesLength = parameterTypes.length; + int parameterLength = 0; + for (int i = 0; i < parameterTypesLength; i++) { + parameterLength += parameterTypes[i].length; + + } + int returnTypeLength = returnType.length; + char[] result = new char[1 + parameterLength + 1 + returnTypeLength]; + result[0] = C_PARAM_START; + int index = 1; + for (int i = 0; i < parameterTypesLength; i++) { + char[] parameterType = parameterTypes[i]; + int length = parameterType.length; + System.arraycopy(parameterType, 0, result, index, length); + index += length; + } + result[index] = C_PARAM_END; + System.arraycopy(returnType, 0, result, index+1, returnTypeLength); + return result; +} + +/** + * Creates a method signature from the given parameter and return type + * signatures. The encoded method signature is dot-based. This method + * is equivalent to + * createMethodSignature(parameterTypes, returnType). + * + * @param parameterTypes the list of parameter type signatures + * @param returnType the return type signature + * @return the encoded method signature + * @see Signature#createMethodSignature(char[][], char[]) + */ +public static String createMethodSignature(String[] parameterTypes, String returnType) { + int parameterTypesLenth = parameterTypes.length; + char[][] parameters = new char[parameterTypesLenth][]; + for (int i = 0; i < parameterTypesLenth; i++) { + parameters[i] = parameterTypes[i].toCharArray(); + } + return new String(createMethodSignature(parameters, returnType.toCharArray())); +} + +/** + * Creates a new type parameter signature with the given name and bounds. + * + * @param typeParameterName the type parameter name + * @param boundSignatures the signatures of associated bounds or empty array if none + * @return the encoded type parameter signature + * + * @since 3.1 + */ +public static char[] createTypeParameterSignature(char[] typeParameterName, char[][] boundSignatures) { + int length = boundSignatures.length; + if (length == 0) { + return CharOperation.append(typeParameterName, C_COLON); // param signature with no bounds still gets trailing colon + } + int boundsSize = 0; + for (int i = 0; i < length; i++) { + boundsSize += boundSignatures[i].length + 1; + } + int nameLength = typeParameterName.length; + char[] result = new char[nameLength + boundsSize]; + System.arraycopy(typeParameterName, 0, result, 0, nameLength); + int index = nameLength; + for (int i = 0; i < length; i++) { + result[index++] = C_COLON; + int boundLength = boundSignatures[i].length; + System.arraycopy(boundSignatures[i], 0, result, index, boundLength); + index += boundLength; + } + return result; +} + +/** + * Creates a new type parameter signature with the given name and bounds. + * + * @param typeParameterName the type parameter name + * @param boundSignatures the signatures of associated bounds or empty array if none + * @return the encoded type parameter signature + * + * @since 3.1 + */ +public static String createTypeParameterSignature(String typeParameterName, String[] boundSignatures) { + int length = boundSignatures.length; + char[][] boundSignatureChars = new char[length][]; + for (int i = 0; i < length; i++) { + boundSignatureChars[i] = boundSignatures[i].toCharArray(); + } + return new String(createTypeParameterSignature(typeParameterName.toCharArray(), boundSignatureChars)); +} + +/** + * Creates a new type signature from the given type name encoded as a character + * array. The type name may contain primitive types, array types or parameterized types. + * This method is equivalent to + * createTypeSignature(new String(typeName),isResolved), although + * more efficient for callers with character arrays rather than strings. If the + * type name is qualified, then it is expected to be dot-based. + * + * @param typeName the possibly qualified type name + * @param isResolved true if the type name is to be considered + * resolved (for example, a type name from a binary class file), and + * false if the type name is to be considered unresolved + * (for example, a type name found in source code) + * @return the encoded type signature + * @see #createTypeSignature(java.lang.String,boolean) + */ +public static String createTypeSignature(char[] typeName, boolean isResolved) { + return new String(createCharArrayTypeSignature(typeName, isResolved)); +} + +/** + * Creates a new type signature from the given type name encoded as a character + * array. The type name may contain primitive types or array types or parameterized types. + * This method is equivalent to + * createTypeSignature(new String(typeName),isResolved).toCharArray(), + * although more efficient for callers with character arrays rather than strings. + * If the type name is qualified, then it is expected to be dot-based. + * + * @param typeName the possibly qualified type name + * @param isResolved true if the type name is to be considered + * resolved (for example, a type name from a binary class file), and + * false if the type name is to be considered unresolved + * (for example, a type name found in source code) + * @return the encoded type signature + * @see #createTypeSignature(java.lang.String,boolean) + * + * @since 2.0 + */ +public static char[] createCharArrayTypeSignature(char[] typeName, boolean isResolved) { + if (typeName == null) throw new IllegalArgumentException("null"); //$NON-NLS-1$ + int length = typeName.length; + if (length == 0) throw new IllegalArgumentException(new String(typeName)); + StringBuffer buffer = new StringBuffer(5); + int pos = encodeTypeSignature(typeName, 0, isResolved, length, buffer); + pos = consumeWhitespace(typeName, pos, length); + if (pos < length) throw new IllegalArgumentException(new String(typeName)); + char[] result = new char[length = buffer.length()]; + buffer.getChars(0, length, result, 0); + return result; +} +private static int consumeWhitespace(char[] typeName, int pos, int length) { + while (pos < length) { + char currentChar = typeName[pos]; + if (currentChar != ' ' && !CharOperation.isWhitespace(currentChar)) { + break; + } + pos++; + } + return pos; +} +private static int encodeQualifiedName(char[] typeName, int pos, int length, StringBuffer buffer) { + int count = 0; + char lastAppendedChar = 0; + nameLoop: while (pos < length) { + char currentChar = typeName[pos]; + switch (currentChar) { + case '<' : + case '>' : + case '[' : + case ',' : + break nameLoop; + case '.' : + buffer.append(C_DOT); + lastAppendedChar = C_DOT; + count++; + break; + default: + if (currentChar == ' ' || Character.isWhitespace(currentChar)) { + if (lastAppendedChar == C_DOT) { // allow spaces after a dot + pos = consumeWhitespace(typeName, pos, length) - 1; // will be incremented + break; + } + // allow spaces before a dot + int checkPos = checkNextChar(typeName, '.', pos, length, true); + if (checkPos > 0) { + buffer.append(C_DOT); // process dot immediately to avoid one iteration + lastAppendedChar = C_DOT; + count++; + pos = checkPos; + break; + } + break nameLoop; + } + buffer.append(currentChar); + lastAppendedChar = currentChar; + count++; + break; + } + pos++; + } + if (count == 0) throw new IllegalArgumentException(new String(typeName)); + return pos; +} + +private static int encodeArrayDimension(char[] typeName, int pos, int length, StringBuffer buffer) { + int checkPos; + while (pos < length && (checkPos = checkNextChar(typeName, '[', pos, length, true)) > 0) { + pos = checkNextChar(typeName, ']', checkPos, length, false); + buffer.append(C_ARRAY); + } + return pos; +} +private static int checkArrayDimension(char[] typeName, int pos, int length) { + int genericBalance = 0; + while (pos < length) { + switch(typeName[pos]) { + case '<' : + genericBalance++; + break; + case ',' : + if (genericBalance == 0) return -1; + break; + case '>': + if (genericBalance == 0) return -1; + genericBalance--; + break; + case '[': + if (genericBalance == 0) { + return pos; + } + } + pos++; + } + return -1; +} +private static int checkNextChar(char[] typeName, char expectedChar, int pos, int length, boolean isOptional) { + pos = consumeWhitespace(typeName, pos, length); + if (pos < length && typeName[pos] == expectedChar) + return pos + 1; + if (!isOptional) throw new IllegalArgumentException(new String(typeName)); + return -1; +} + +private static int encodeTypeSignature(char[] typeName, int start, boolean isResolved, int length, StringBuffer buffer) { + int pos = start; + pos = consumeWhitespace(typeName, pos, length); + if (pos >= length) throw new IllegalArgumentException(new String(typeName)); + int checkPos; + char currentChar = typeName[pos]; + switch (currentChar) { + // primitive type? + case 'b' : + checkPos = checkName(BOOLEAN, typeName, pos, length); + if (checkPos > 0) { + pos = encodeArrayDimension(typeName, checkPos, length, buffer); + buffer.append(C_BOOLEAN); + return pos; + } + checkPos = checkName(BYTE, typeName, pos, length); + if (checkPos > 0) { + pos = encodeArrayDimension(typeName, checkPos, length, buffer); + buffer.append(C_BYTE); + return pos; + } + break; + case 'c': + checkPos = checkName(CHAR, typeName, pos, length); + if (checkPos > 0) { + pos = encodeArrayDimension(typeName, checkPos, length, buffer); + buffer.append(C_CHAR); + return pos; + } + break; + case 'd': + checkPos = checkName(DOUBLE, typeName, pos, length); + if (checkPos > 0) { + pos = encodeArrayDimension(typeName, checkPos, length, buffer); + buffer.append(C_DOUBLE); + return pos; + } + break; + case 'f': + checkPos = checkName(FLOAT, typeName, pos, length); + if (checkPos > 0) { + pos = encodeArrayDimension(typeName, checkPos, length, buffer); + buffer.append(C_FLOAT); + return pos; + } + break; + case 'i': + checkPos = checkName(INT, typeName, pos, length); + if (checkPos > 0) { + pos = encodeArrayDimension(typeName, checkPos, length, buffer); + buffer.append(C_INT); + return pos; + } + break; + case 'l': + checkPos = checkName(LONG, typeName, pos, length); + if (checkPos > 0) { + pos = encodeArrayDimension(typeName, checkPos, length, buffer); + buffer.append(C_LONG); + return pos; + } + break; + case 's': + checkPos = checkName(SHORT, typeName, pos, length); + if (checkPos > 0) { + pos = encodeArrayDimension(typeName, checkPos, length, buffer); + buffer.append(C_SHORT); + return pos; + } + break; + case 'v': + checkPos = checkName(VOID, typeName, pos, length); + if (checkPos > 0) { + pos = encodeArrayDimension(typeName, checkPos, length, buffer); + buffer.append(C_VOID); + return pos; + } + break; + case '?': + // wildcard + pos = consumeWhitespace(typeName, pos+1, length); + checkPos = checkName(EXTENDS, typeName, pos, length); + if (checkPos > 0) { + buffer.append(C_EXTENDS); + pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer); + return pos; + } + checkPos = checkName(SUPER, typeName, pos, length); + if (checkPos > 0) { + buffer.append(C_SUPER); + pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer); + return pos; + } + buffer.append(C_STAR); + return pos; + } + // non primitive type + checkPos = checkArrayDimension(typeName, pos, length); + int end; + if (checkPos > 0) { + end = encodeArrayDimension(typeName, checkPos, length, buffer); + } else { + end = -1; + } + buffer.append(isResolved ? C_RESOLVED : C_UNRESOLVED); + while (true) { // loop on qualifiedName[][.qualifiedName[]* + pos = encodeQualifiedName(typeName, pos, length, buffer); + checkPos = checkNextChar(typeName, '<', pos, length, true); + if (checkPos > 0) { + buffer.append(C_GENERIC_START); + pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer); + while ((checkPos = checkNextChar(typeName, ',', pos, length, true)) > 0) { + pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer); + } + pos = checkNextChar(typeName, '>', pos, length, false); + buffer.append(C_GENERIC_END); + } + checkPos = checkNextChar(typeName, '.', pos, length, true); + if (checkPos > 0) { + buffer.append(C_DOT); + pos = checkPos; + } else { + break; + } + } + buffer.append(C_NAME_END); + if (end > 0) pos = end; // skip array dimension which were preprocessed + return pos; +} + +/** + * Creates a new type signature from the given type name. If the type name is qualified, + * then it is expected to be dot-based. The type name may contain primitive + * types or array types. However, parameterized types are not supported. + *

+ * For example: + *

+ * 
+ * createTypeSignature("int", hucairz) -> "I"
+ * createTypeSignature("java.lang.String", true) -> "Ljava.lang.String;"
+ * createTypeSignature("String", false) -> "QString;"
+ * createTypeSignature("java.lang.String", false) -> "Qjava.lang.String;"
+ * createTypeSignature("int []", false) -> "[I"
+ * 
+ * 
+ *

+ * + * @param typeName the possibly qualified type name + * @param isResolved true if the type name is to be considered + * resolved (for example, a type name from a binary class file), and + * false if the type name is to be considered unresolved + * (for example, a type name found in source code) + * @return the encoded type signature + */ +public static String createTypeSignature(String typeName, boolean isResolved) { + return createTypeSignature(typeName == null ? null : typeName.toCharArray(), isResolved); +} + +/** + * Returns the array count (array nesting depth) of the given type signature. + * + * @param typeSignature the type signature + * @return the array nesting depth, or 0 if not an array + * @exception IllegalArgumentException if the signature is not syntactically + * correct + * + * @since 2.0 + */ +public static int getArrayCount(char[] typeSignature) throws IllegalArgumentException { + try { + int count = 0; + while (typeSignature[count] == C_ARRAY) { + ++count; + } + return count; + } catch (ArrayIndexOutOfBoundsException e) { // signature is syntactically incorrect if last character is C_ARRAY + throw new IllegalArgumentException(); + } +} +/** + * Returns the array count (array nesting depth) of the given type signature. + * + * @param typeSignature the type signature + * @return the array nesting depth, or 0 if not an array + * @exception IllegalArgumentException if the signature is not syntactically + * correct + */ +public static int getArrayCount(String typeSignature) throws IllegalArgumentException { + return getArrayCount(typeSignature.toCharArray()); +} +/** + * Returns the type signature without any array nesting. + *

+ * For example: + *

+ * 
+ * getElementType({'[', '[', 'I'}) --> {'I'}.
+ * 
+ * 
+ *

+ * + * @param typeSignature the type signature + * @return the type signature without arrays + * @exception IllegalArgumentException if the signature is not syntactically + * correct + * + * @since 2.0 + */ +public static char[] getElementType(char[] typeSignature) throws IllegalArgumentException { + int count = getArrayCount(typeSignature); + if (count == 0) return typeSignature; + int length = typeSignature.length; + char[] result = new char[length-count]; + System.arraycopy(typeSignature, count, result, 0, length-count); + return result; +} +/** + * Returns the type signature without any array nesting. + *

+ * For example: + *

+ * 
+ * getElementType("[[I") --> "I".
+ * 
+ * 
+ *

+ * + * @param typeSignature the type signature + * @return the type signature without arrays + * @exception IllegalArgumentException if the signature is not syntactically + * correct + */ +public static String getElementType(String typeSignature) throws IllegalArgumentException { + return new String(getElementType(typeSignature.toCharArray())); +} +/** + * Returns the number of parameter types in the given method signature. + * + * @param methodSignature the method signature + * @return the number of parameters + * @exception IllegalArgumentException if the signature is not syntactically + * correct + * @since 2.0 + */ +public static int getParameterCount(char[] methodSignature) throws IllegalArgumentException { + try { + int count = 0; + int i = CharOperation.indexOf(C_PARAM_START, methodSignature); + if (i < 0) { + throw new IllegalArgumentException(); + } else { + i++; + } + for (;;) { + if (methodSignature[i] == C_PARAM_END) { + return count; + } + int e= scanTypeSignature(methodSignature, i); + if (e < 0) { + throw new IllegalArgumentException(); + } else { + i = e + 1; + } + count++; + } + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException(); + } +} + +/** + * Returns the kind of type signature encoded by the given string. + * + * @param typeSignature the type signature string + * @return the kind of type signature; one of the kind constants: + * {@link #ARRAY_TYPE_SIGNATURE}, {@link #CLASS_TYPE_SIGNATURE}, + * {@link #BASE_TYPE_SIGNATURE}, or {@link #TYPE_VARIABLE_SIGNATURE} + * @exception IllegalArgumentException if this is not a type signature + * @since 3.0 + */ +public static int getTypeSignatureKind(char[] typeSignature) { + // need a minimum 1 char + if (typeSignature.length < 1) { + throw new IllegalArgumentException(); + } + char c = typeSignature[0]; + switch (c) { + case C_ARRAY : + return ARRAY_TYPE_SIGNATURE; + case C_RESOLVED : + case C_UNRESOLVED : + return CLASS_TYPE_SIGNATURE; + case C_TYPE_VARIABLE : + return TYPE_VARIABLE_SIGNATURE; + case C_BOOLEAN : + case C_BYTE : + case C_CHAR : + case C_DOUBLE : + case C_FLOAT : + case C_INT : + case C_LONG : + case C_SHORT : + case C_VOID : + return BASE_TYPE_SIGNATURE; + default : + throw new IllegalArgumentException(); + } +} + +/** + * Returns the kind of type signature encoded by the given string. + * + * @param typeSignature the type signature string + * @return the kind of type signature; one of the kind constants: + * {@link #ARRAY_TYPE_SIGNATURE}, {@link #CLASS_TYPE_SIGNATURE}, + * {@link #BASE_TYPE_SIGNATURE}, or {@link #TYPE_VARIABLE_SIGNATURE} + * @exception IllegalArgumentException if this is not a type signature + * @since 3.0 + */ +public static int getTypeSignatureKind(String typeSignature) { + // need a minimum 1 char + if (typeSignature.length() < 1) { + throw new IllegalArgumentException(); + } + char c = typeSignature.charAt(0); + switch (c) { + case C_ARRAY : + return ARRAY_TYPE_SIGNATURE; + case C_RESOLVED : + case C_UNRESOLVED : + return CLASS_TYPE_SIGNATURE; + case C_TYPE_VARIABLE : + return TYPE_VARIABLE_SIGNATURE; + case C_BOOLEAN : + case C_BYTE : + case C_CHAR : + case C_DOUBLE : + case C_FLOAT : + case C_INT : + case C_LONG : + case C_SHORT : + case C_VOID : + return BASE_TYPE_SIGNATURE; + default : + throw new IllegalArgumentException(); + } +} + +/** + * Scans the given string for a type signature starting at the given index + * and returns the index of the last character. + *
+ * TypeSignature:
+ *  |  BaseTypeSignature
+ *  |  ArrayTypeSignature
+ *  |  ClassTypeSignature
+ *  |  TypeVariableSignature
+ * 
+ * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a type signature + * @see #appendTypeSignature(char[], int, boolean, StringBuffer) + */ +private static int scanTypeSignature(char[] string, int start) { + // need a minimum 1 char + if (start >= string.length) { + throw new IllegalArgumentException(); + } + char c = string[start]; + switch (c) { + case C_ARRAY : + return scanArrayTypeSignature(string, start); + case C_RESOLVED : + case C_UNRESOLVED : + return scanClassTypeSignature(string, start); + case C_TYPE_VARIABLE : + return scanTypeVariableSignature(string, start); + case C_BOOLEAN : + case C_BYTE : + case C_CHAR : + case C_DOUBLE : + case C_FLOAT : + case C_INT : + case C_LONG : + case C_SHORT : + case C_VOID : + return scanBaseTypeSignature(string, start); + default : + throw new IllegalArgumentException(); + } +} + +/** + * Scans the given string for a base type signature starting at the given index + * and returns the index of the last character. + *
+ * BaseTypeSignature:
+ *     B | C | D | F | I
+ *   | J | S | V | Z
+ * 
+ * Note that although the base type "V" is only allowed in method return types, + * there is no syntactic ambiguity. This method will accept them anywhere + * without complaint. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a base type signature + */ +private static int scanBaseTypeSignature(char[] string, int start) { + // need a minimum 1 char + if (start >= string.length) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if ("BCDFIJSVZ".indexOf(c) >= 0) { //$NON-NLS-1$ + return start; + } else { + throw new IllegalArgumentException(); + } +} + +/** + * Scans the given string for an array type signature starting at the given + * index and returns the index of the last character. + *
+ * ArrayTypeSignature:
+ *     [ TypeSignature
+ * 
+ * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not an array type signature + * @see #appendArrayTypeSignature(char[], int, boolean, StringBuffer) + */ +private static int scanArrayTypeSignature(char[] string, int start) { + // need a minimum 2 char + if (start >= string.length - 1) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if (c != C_ARRAY) { //$NON-NLS-1$ + throw new IllegalArgumentException(); + } + return scanTypeSignature(string, start + 1); +} + +/** + * Scans the given string for a type variable signature starting at the given + * index and returns the index of the last character. + *
+ * TypeVariableSignature:
+ *     T Identifier ;
+ * 
+ * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a type variable signature + */ +private static int scanTypeVariableSignature(char[] string, int start) { + // need a minimum 3 chars "Tx;" + if (start >= string.length - 2) { + throw new IllegalArgumentException(); + } + // must start in "T" + char c = string[start]; + if (c != C_TYPE_VARIABLE) { + throw new IllegalArgumentException(); + } + int id = scanIdentifier(string, start + 1); + c = string[id + 1]; + if (c == C_SEMICOLON) { + return id + 1; + } else { + throw new IllegalArgumentException(); + } +} + +/** + * Scans the given string for an identifier starting at the given + * index and returns the index of the last character. + * Stop characters are: ";", ":", "<", ">", "/", ".". + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not an identifier + */ +private static int scanIdentifier(char[] string, int start) { + // need a minimum 1 char + if (start >= string.length) { + throw new IllegalArgumentException(); + } + int p = start; + while (true) { + char c = string[p]; + if (c == '<' || c == '>' || c == ':' || c == ';' || c == '.' || c == '/') { + return p - 1; + } + p++; + if (p == string.length) { + return p - 1; + } + } +} + +/** + * Scans the given string for a class type signature starting at the given + * index and returns the index of the last character. + *
+ * ClassTypeSignature:
+ *     { L | Q } Identifier
+ *           { { / | . Identifier [ < TypeArgumentSignature* > ] }
+ *           ;
+ * 
+ * Note that although all "/"-identifiers most come before "."-identifiers, + * there is no syntactic ambiguity. This method will accept them without + * complaint. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a class type signature + * @see #appendClassTypeSignature(char[], int, boolean, StringBuffer) + */ +private static int scanClassTypeSignature(char[] string, int start) { + // need a minimum 3 chars "Lx;" + if (start >= string.length - 2) { + throw new IllegalArgumentException(); + } + // must start in "L" or "Q" + char c = string[start]; + if (c != C_RESOLVED && c != C_UNRESOLVED) { + return -1; + } + int p = start + 1; + while (true) { + if (p >= string.length) { + throw new IllegalArgumentException(); + } + c = string[p]; + if (c == C_SEMICOLON) { + // all done + return p; + } else if (c == C_GENERIC_START) { + int e = scanTypeArgumentSignatures(string, p); + p = e; + } else if (c == C_DOT || c == '/') { + int id = scanIdentifier(string, p + 1); + p = id; + } + p++; + } +} + +/** + * Scans the given string for a list of type argument signatures starting at + * the given index and returns the index of the last character. + *
+ * TypeArgumentSignatures:
+ *     < TypeArgumentSignature* >
+ * 
+ * Note that although there is supposed to be at least one type argument, there + * is no syntactic ambiguity if there are none. This method will accept zero + * type argument signatures without complaint. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a list of type arguments + * signatures + * @see #appendTypeArgumentSignatures(char[], int, boolean, StringBuffer) + */ +private static int scanTypeArgumentSignatures(char[] string, int start) { + // need a minimum 2 char "<>" + if (start >= string.length - 1) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if (c != C_GENERIC_START) { + throw new IllegalArgumentException(); + } + int p = start + 1; + while (true) { + if (p >= string.length) { + throw new IllegalArgumentException(); + } + c = string[p]; + if (c == C_GENERIC_END) { + return p; + } + int e = scanTypeArgumentSignature(string, p); + p = e + 1; + } +} + +/** + * Scans the given string for a type argument signature starting at the given + * index and returns the index of the last character. + *
+ * TypeArgumentSignature:
+ *     *
+ *  |  + TypeSignature
+ *  |  - TypeSignature
+ *  |  TypeSignature
+ * 
+ * Note that although base types are not allowed in type arguments, there is + * no syntactic ambiguity. This method will accept them without complaint. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a type argument signature + * @see #appendTypeArgumentSignature(char[], int, boolean, StringBuffer) + */ +private static int scanTypeArgumentSignature(char[] string, int start) { + // need a minimum 1 char + if (start >= string.length) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if (c == C_STAR) { + return start; + } + if (c == '+' || c == '-') { + return scanTypeSignature(string, start + 1); + } else { + return scanTypeSignature(string, start); + } +} + +/** + * Returns the number of parameter types in the given method signature. + * + * @param methodSignature the method signature + * @return the number of parameters + * @exception IllegalArgumentException if the signature is not syntactically + * correct + */ +public static int getParameterCount(String methodSignature) throws IllegalArgumentException { + return getParameterCount(methodSignature.toCharArray()); +} + +/** + * Extracts the parameter type signatures from the given method signature. + * The method signature is expected to be dot-based. + * + * @param methodSignature the method signature + * @return the list of parameter type signatures + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * + * @since 2.0 + */ +public static char[][] getParameterTypes(char[] methodSignature) throws IllegalArgumentException { + try { + int count = getParameterCount(methodSignature); + char[][] result = new char[count][]; + if (count == 0) { + return result; + } + int i = CharOperation.indexOf(C_PARAM_START, methodSignature); + if (i < 0) { + throw new IllegalArgumentException(); + } else { + i++; + } + int t = 0; + for (;;) { + if (methodSignature[i] == C_PARAM_END) { + return result; + } + int e = scanTypeSignature(methodSignature, i); + if (e < 0) { + throw new IllegalArgumentException(); + } + result[t] = CharOperation.subarray(methodSignature, i, e + 1); + t++; + i = e + 1; + } + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException(); + } +} + +/** + * Extracts the parameter type signatures from the given method signature. + * The method signature is expected to be dot-based. + * + * @param methodSignature the method signature + * @return the list of parameter type signatures + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + */ +public static String[] getParameterTypes(String methodSignature) throws IllegalArgumentException { + char[][] parameterTypes = getParameterTypes(methodSignature.toCharArray()); + return CharOperation.toStrings(parameterTypes); +} + +/** + * Extracts the thrown exception type signatures from the given method signature if any + * The method signature is expected to be dot-based. + * + * @param methodSignature the method signature + * @return the list of thrown exception type signatures + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + */ +public static String[] getThrownExceptionTypes(String methodSignature) throws IllegalArgumentException { + char[][] parameterTypes = getThrownExceptionTypes(methodSignature.toCharArray()); + return CharOperation.toStrings(parameterTypes); +} + +/** + * Extracts the thrown exception type signatures from the given method signature if any + * The method signature is expected to be dot-based. + * + * @param methodSignature the method signature + * @return the list of thrown exception type signatures + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + */ +public static char[][] getThrownExceptionTypes(char[] methodSignature) throws IllegalArgumentException { + // skip type parameters + int paren = CharOperation.lastIndexOf(C_PARAM_END, methodSignature); + if (paren == -1) { + throw new IllegalArgumentException(); + } + // ignore return type + int exceptionStart = scanTypeSignature(methodSignature, paren+1) + 1; + int length = methodSignature.length; + if (exceptionStart == length) return CharOperation.NO_CHAR_CHAR; + + ArrayList exceptionList = new ArrayList(1); + int i = exceptionStart; + while (i < length) { + i = scanTypeSignature(methodSignature, i) + 1; + exceptionList.add(CharOperation.subarray(methodSignature, exceptionStart,i)); + exceptionStart = i; + } + char[][] result; + exceptionList.toArray(result = new char[exceptionList.size()][]); + return result; +} + +/** + * Extracts the type argument signatures from the given type signature. + * Returns an empty array if the type signature is not a parameterized type signature. + * + * @param parameterizedTypeSignature the parameterized type signature + * @return the signatures of the type arguments + * @exception IllegalArgumentException if the signature is syntactically incorrect + * + * @since 3.1 + */ +public static char[][] getTypeArguments(char[] parameterizedTypeSignature) throws IllegalArgumentException { + int length = parameterizedTypeSignature.length; + if (length < 2 || parameterizedTypeSignature[length-2] != C_GENERIC_END) + // cannot have type arguments otherwise signature would end by ">;" + return CharOperation.NO_CHAR_CHAR; + int count = 1; // start to count generic end/start peers + int start = length - 2; + while (start >= 0 && count > 0) { + switch (parameterizedTypeSignature[--start]) { + case C_GENERIC_START: + count--; + break; + case C_GENERIC_END: + count++; + break; + } + } + if (start < 0) // invalid number of generic start/end + throw new IllegalArgumentException(); + ArrayList args = new ArrayList(); + int p = start + 1; + while (true) { + if (p >= parameterizedTypeSignature.length) { + throw new IllegalArgumentException(); + } + char c = parameterizedTypeSignature[p]; + if (c == C_GENERIC_END) { + int size = args.size(); + char[][] result = new char[size][]; + args.toArray(result); + return result; + } + int e = scanTypeArgumentSignature(parameterizedTypeSignature, p); + args.add(CharOperation.subarray(parameterizedTypeSignature, p, e+1)); + p = e + 1; + } +} + +/** + * Extracts the type argument signatures from the given type signature. + * Returns an empty array if the type signature is not a parameterized type signature. + * + * @param parameterizedTypeSignature the parameterized type signature + * @return the signatures of the type arguments + * @exception IllegalArgumentException if the signature is syntactically incorrect + * + * @since 3.1 + */ +public static String[] getTypeArguments(String parameterizedTypeSignature) throws IllegalArgumentException { + char[][] args = getTypeArguments(parameterizedTypeSignature.toCharArray()); + return CharOperation.toStrings(args); +} + +/** + * Extracts the type erasure signature from the given parameterized type signature. + * Returns the given type signature if it is not parameterized. + * + * @param parameterizedTypeSignature the parameterized type signature + * @return the signature of the type erasure + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * + * @since 3.1 + */ +public static char[] getTypeErasure(char[] parameterizedTypeSignature) throws IllegalArgumentException { + int end = CharOperation.indexOf(C_GENERIC_START, parameterizedTypeSignature); + if (end == -1) return parameterizedTypeSignature; + int length = parameterizedTypeSignature.length; + char[] result = new char[length]; + int pos = 0; + int start = 0; + int deep= 0; + for (int idx=end; idx 0) throw new IllegalArgumentException(); + int size = pos+length-start; + char[] resized = new char[size]; + System.arraycopy(result, 0, resized, 0, pos); + System.arraycopy(parameterizedTypeSignature, start, resized, pos, length-start); + return resized; +} + +/** + * Extracts the type erasure signature from the given parameterized type signature. + * Returns the given type signature if it is not parameterized. + * + * @param parameterizedTypeSignature the parameterized type signature + * @return the signature of the type erasure + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * + * @since 3.1 + */ +public static String getTypeErasure(String parameterizedTypeSignature) throws IllegalArgumentException { + return new String(getTypeErasure(parameterizedTypeSignature.toCharArray())); +} + +/** + * Extracts the type parameter signatures from the given method or type signature. + * The method or type signature is expected to be dot-based. + * + * @param methodOrTypeSignature the method or type signature + * @return the list of type parameter signatures + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * + * @since 3.1 + */ +public static char[][] getTypeParameters(char[] methodOrTypeSignature) throws IllegalArgumentException { + try { + int length = methodOrTypeSignature.length; + if (length == 0) return CharOperation.NO_CHAR_CHAR; + if (methodOrTypeSignature[0] != C_GENERIC_START) return CharOperation.NO_CHAR_CHAR; + + ArrayList paramList = new ArrayList(1); + int paramStart = 1, i = 1; // start after leading '<' + while (i < length) { + if (methodOrTypeSignature[i] == C_GENERIC_END) { + int size = paramList.size(); + if (size == 0) throw new IllegalArgumentException(); + char[][] result; + paramList.toArray(result = new char[size][]); + return result; + } + i = CharOperation.indexOf(C_COLON, methodOrTypeSignature, i); + if (i < 0 || i >= length) throw new IllegalArgumentException(); + // iterate over bounds + nextBound: while (methodOrTypeSignature[i] == ':') { + i++; // skip colon + if (methodOrTypeSignature[i] == ':') { + continue nextBound; // empty bound + } + i = scanTypeSignature(methodOrTypeSignature, i); + i++; // position at start of next param if any + } + paramList.add(CharOperation.subarray(methodOrTypeSignature, paramStart, i)); + paramStart = i; // next param start from here + } + } catch (ArrayIndexOutOfBoundsException e) { + // invalid signature, fall through + } + throw new IllegalArgumentException(); +} +/** + * Extracts the type parameter signatures from the given method or type signature. + * The method or type signature is expected to be dot-based. + * + * @param methodOrTypeSignature the method or type signature + * @return the list of type parameter signatures + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * + * @since 3.1 + */ +public static String[] getTypeParameters(String methodOrTypeSignature) throws IllegalArgumentException { + char[][] params = getTypeParameters(methodOrTypeSignature.toCharArray()); + return CharOperation.toStrings(params); +} + +/** + * Extracts the type variable name from the given formal type parameter + * signature. The signature is expected to be dot-based. + * + * @param formalTypeParameterSignature the formal type parameter signature + * @return the name of the type variable + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * @since 3.0 + */ +public static String getTypeVariable(String formalTypeParameterSignature) throws IllegalArgumentException { + return new String(getTypeVariable(formalTypeParameterSignature.toCharArray())); +} + +/** + * Extracts the type variable name from the given formal type parameter + * signature. The signature is expected to be dot-based. + * + * @param formalTypeParameterSignature the formal type parameter signature + * @return the name of the type variable + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * @since 3.0 + */ +public static char[] getTypeVariable(char[] formalTypeParameterSignature) throws IllegalArgumentException { + int p = CharOperation.indexOf(C_COLON, formalTypeParameterSignature); + if (p < 0) { + // no ":" means can't be a formal type parameter signature + throw new IllegalArgumentException(); + } + return CharOperation.subarray(formalTypeParameterSignature, 0, p); +} + +/** + * Extracts the class and interface bounds from the given formal type + * parameter signature. The class bound, if present, is listed before + * the interface bounds. The signature is expected to be dot-based. + * + * @param formalTypeParameterSignature the formal type parameter signature + * @return the (possibly empty) list of type signatures for the bounds + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * @since 3.0 + */ +public static char[][] getTypeParameterBounds(char[] formalTypeParameterSignature) throws IllegalArgumentException { + int p1 = CharOperation.indexOf(C_COLON, formalTypeParameterSignature); + if (p1 < 0) { + // no ":" means can't be a formal type parameter signature + throw new IllegalArgumentException(); + } + if (p1 == formalTypeParameterSignature.length - 1) { + // no class or interface bounds + return CharOperation.NO_CHAR_CHAR; + } + int p2 = CharOperation.indexOf(C_COLON, formalTypeParameterSignature, p1 + 1); + char[] classBound; + if (p2 < 0) { + // no interface bounds + classBound = CharOperation.subarray(formalTypeParameterSignature, p1 + 1, formalTypeParameterSignature.length); + return new char[][] {classBound}; + } + if (p2 == p1 + 1) { + // no class bound, but 1 or more interface bounds + classBound = null; + } else { + classBound = CharOperation.subarray(formalTypeParameterSignature, p1 + 1, p2); + } + char[][] interfaceBounds = CharOperation.splitOn(C_COLON, formalTypeParameterSignature, p2 + 1, formalTypeParameterSignature.length); + if (classBound == null) { + return interfaceBounds; + } + int resultLength = interfaceBounds.length + 1; + char[][] result = new char[resultLength][]; + result[0] = classBound; + System.arraycopy(interfaceBounds, 0, result, 1, interfaceBounds.length); + return result; +} + +/** + * Extracts the class and interface bounds from the given formal type + * parameter signature. The class bound, if present, is listed before + * the interface bounds. The signature is expected to be dot-based. + * + * @param formalTypeParameterSignature the formal type parameter signature + * @return the (possibly empty) list of type signatures for the bounds + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * @since 3.0 + */ +public static String[] getTypeParameterBounds(String formalTypeParameterSignature) throws IllegalArgumentException { + char[][] bounds = getTypeParameterBounds(formalTypeParameterSignature.toCharArray()); + return CharOperation.toStrings(bounds); +} + +/** + * Returns a char array containing all but the last segment of the given + * dot-separated qualified name. Returns the empty char array if it is not qualified. + *

+ * For example: + *

+ * 
+ * getQualifier({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g'}
+ * getQualifier({'O', 'u', 't', 'e', 'r', '.', 'I', 'n', 'n', 'e', 'r'}) -> {'O', 'u', 't', 'e', 'r'}
+ * getQualifier({'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'L', 'i', 's', 't', '<', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '>'}) -> {'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l'}
+ * 
+ * 
+ *

+ * + * @param name the name + * @return the qualifier prefix, or the empty char array if the name contains no + * dots + * @exception NullPointerException if name is null + * @since 2.0 + */ +public static char[] getQualifier(char[] name) { + int firstGenericStart = CharOperation.indexOf(C_GENERIC_START, name); + int lastDot = CharOperation.lastIndexOf(C_DOT, name, 0, firstGenericStart == -1 ? name.length-1 : firstGenericStart); + if (lastDot == -1) { + return CharOperation.NO_CHAR; + } + return CharOperation.subarray(name, 0, lastDot); +} +/** + * Returns a string containing all but the last segment of the given + * dot-separated qualified name. Returns the empty string if it is not qualified. + *

+ * For example: + *

+ * 
+ * getQualifier("java.lang.Object") -> "java.lang"
+ * getQualifier("Outer.Inner") -> "Outer"
+ * getQualifier("java.util.List") -> "java.util"
+ * 
+ * 
+ *

+ * + * @param name the name + * @return the qualifier prefix, or the empty string if the name contains no + * dots + * @exception NullPointerException if name is null + */ +public static String getQualifier(String name) { + char[] qualifier = getQualifier(name.toCharArray()); + if (qualifier.length == 0) return EMPTY; + return new String(qualifier); +} +/** + * Extracts the return type from the given method signature. The method signature is + * expected to be dot-based. + * + * @param methodSignature the method signature + * @return the type signature of the return type + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * + * @since 2.0 + */ +public static char[] getReturnType(char[] methodSignature) throws IllegalArgumentException { + // skip type parameters + int paren = CharOperation.lastIndexOf(C_PARAM_END, methodSignature); + if (paren == -1) { + throw new IllegalArgumentException(); + } + // there could be thrown exceptions behind, thus scan one type exactly + int last = scanTypeSignature(methodSignature, paren+1); + return CharOperation.subarray(methodSignature, paren + 1, last+1); +} +/** + * Extracts the return type from the given method signature. The method signature is + * expected to be dot-based. + * + * @param methodSignature the method signature + * @return the type signature of the return type + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + */ +public static String getReturnType(String methodSignature) throws IllegalArgumentException { + return new String(getReturnType(methodSignature.toCharArray())); +} +/** + * Returns package fragment of a type signature. The package fragment separator must be '.' + * and the type fragment separator must be '$'. + *

+ * For example: + *

+ * 
+ * getSignatureQualifier({'L', 'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'M', 'a', 'p', '$', 'E', 'n', 't', 'r', 'y', ';'}) -> {'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l'}
+ * 
+ * 
+ *

+ * + * @param typeSignature the type signature + * @return the package fragment (separators are '.') + * @since 3.1 + */ +public static char[] getSignatureQualifier(char[] typeSignature) { + if(typeSignature == null) return CharOperation.NO_CHAR; + + char[] qualifiedType = Signature.toCharArray(typeSignature); + + int dotCount = 0; + indexFound: for(int i = 0; i < typeSignature.length; i++) { + switch(typeSignature[i]) { + case C_DOT: + dotCount++; + break; + case C_GENERIC_START: + break indexFound; + case C_DOLLAR: + break indexFound; + } + } + + if(dotCount > 0) { + for(int i = 0; i < qualifiedType.length; i++) { + if(qualifiedType[i] == '.') { + dotCount--; + } + if(dotCount <= 0) { + return CharOperation.subarray(qualifiedType, 0, i); + } + } + } + return CharOperation.NO_CHAR; +} +/** + * Returns package fragment of a type signature. The package fragment separator must be '.' + * and the type fragment separator must be '$'. + *

+ * For example: + *

+ * 
+ * getSignatureQualifier("Ljava.util.Map$Entry") -> "java.util"
+ * 
+ * 
+ *

+ * + * @param typeSignature the type signature + * @return the package fragment (separators are '.') + * @since 3.1 + */ +public static String getSignatureQualifier(String typeSignature) { + return new String(getSignatureQualifier(typeSignature == null ? null : typeSignature.toCharArray())); +} +/** + * Returns type fragment of a type signature. The package fragment separator must be '.' + * and the type fragment separator must be '$'. + *

+ * For example: + *

+ * 
+ * getSignatureSimpleName({'L', 'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'M', 'a', 'p', '$', 'E', 'n', 't', 'r', 'y', ';'}) -> {'M', 'a', 'p', '.', 'E', 'n', 't', 'r', 'y'}
+ * 
+ * 
+ *

+ * + * @param typeSignature the type signature + * @return the type fragment (separators are '.') + * @since 3.1 + */ +public static char[] getSignatureSimpleName(char[] typeSignature) { + if(typeSignature == null) return CharOperation.NO_CHAR; + + char[] qualifiedType = Signature.toCharArray(typeSignature); + + int dotCount = 0; + indexFound: for(int i = 0; i < typeSignature.length; i++) { + switch(typeSignature[i]) { + case C_DOT: + dotCount++; + break; + case C_GENERIC_START: + break indexFound; + case C_DOLLAR: + break indexFound; + } + } + + if(dotCount > 0) { + for(int i = 0; i < qualifiedType.length; i++) { + if(qualifiedType[i] == '.') { + dotCount--; + } + if(dotCount <= 0) { + return CharOperation.subarray(qualifiedType, i + 1, qualifiedType.length); + } + } + } + return qualifiedType; +} +/** + * Returns type fragment of a type signature. The package fragment separator must be '.' + * and the type fragment separator must be '$'. + *

+ * For example: + *

+ * 
+ * getSignatureSimpleName("Ljava.util.Map$Entry") -> "Map.Entry"
+ * 
+ * 
+ *

+ * + * @param typeSignature the type signature + * @return the type fragment (separators are '.') + * @since 3.1 + */ +public static String getSignatureSimpleName(String typeSignature) { + return new String(getSignatureSimpleName(typeSignature == null ? null : typeSignature.toCharArray())); +} + +/** + * Returns the last segment of the given dot-separated qualified name. + * Returns the given name if it is not qualified. + *

+ * For example: + *

+ * 
+ * getSimpleName({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {'O', 'b', 'j', 'e', 'c', 't'}
+ * 
+ * 
+ *

+ * + * @param name the name + * @return the last segment of the qualified name + * @exception NullPointerException if name is null + * @since 2.0 + */ +public static char[] getSimpleName(char[] name) { + + int lastDot = -1, lastGenericStart = -1, lastGenericEnd = -1; + int depth = 0; + int length = name.length; + lastDotLookup: for (int i = length -1; i >= 0; i--) { + switch (name[i]) { + case '.': + if (depth == 0) { + lastDot = i; + break lastDotLookup; + } + break; + case '<': + depth--; + if (depth == 0) lastGenericStart = i; + break; + case '>': + if (depth == 0) lastGenericEnd = i; + depth++; + break; + } + } + if (lastGenericStart < 0) { + if (lastDot < 0) { + return name; + } + return CharOperation.subarray(name, lastDot + 1, length); + } + StringBuffer buffer = new StringBuffer(10); + int nameStart = lastDot < 0 ? 0 : lastDot+1; + buffer.append(name, nameStart, lastGenericStart - nameStart); + appendArgumentSimpleNames(name, lastGenericStart, lastGenericEnd, buffer); + buffer.append(name, lastGenericEnd+1, length-lastGenericEnd-1); // copy trailing portion, may contain dimensions + char[] result = new char[length = buffer.length()]; + buffer.getChars(0, length, result, 0); + return result; +} +/** + * Returns the last segment of the given dot-separated qualified name. + * Returns the given name if it is not qualified. + *

+ * For example: + *

+ * 
+ * getSimpleName("java.lang.Object") -> "Object"
+ * 
+ * 
+ * getSimpleName("java.util.Map") -> "Map"
+ * 
+ * 
+ *

+ * + * @param name the name + * @return the last segment of the qualified name + * @exception NullPointerException if name is null + */ +public static String getSimpleName(String name) { + int lastDot = -1, lastGenericStart = -1, lastGenericEnd = -1; + int depth = 0; + int length = name.length(); + lastDotLookup: for (int i = length -1; i >= 0; i--) { + switch (name.charAt(i)) { + case '.': + if (depth == 0) { + lastDot = i; + break lastDotLookup; + } + break; + case '<': + depth--; + if (depth == 0) lastGenericStart = i; + break; + case '>': + if (depth == 0) lastGenericEnd = i; + depth++; + break; + } + } + if (lastGenericStart < 0) { + if (lastDot < 0) { + return name; + } + return name.substring(lastDot + 1, length); + } + StringBuffer buffer = new StringBuffer(10); + char[] nameChars = name.toCharArray(); + int nameStart = lastDot < 0 ? 0 : lastDot+1; + buffer.append(nameChars, nameStart, lastGenericStart - nameStart); + appendArgumentSimpleNames(nameChars, lastGenericStart, lastGenericEnd, buffer); + buffer.append(nameChars, lastGenericEnd+1, length-lastGenericEnd-1); // copy trailing portion, may contain dimensions + return buffer.toString(); +} + +private static void appendSimpleName(char[] name, int start, int end, StringBuffer buffer) { + int lastDot = -1, lastGenericStart = -1, lastGenericEnd = -1; + int depth = 0; + if (name[start] == '?') { // wildcard + buffer.append("? "); //$NON-NLS-1$ + int index = consumeWhitespace(name, start+1, end+1); + switch (name[index]) { + case 'e' : + int checkPos = checkName(EXTENDS, name, index, end); + if (checkPos > 0) { + buffer.append(EXTENDS).append(' '); + index = consumeWhitespace(name, checkPos, end+1); + } + break; + case 's' : + checkPos = checkName(SUPER, name, index, end+1); + if (checkPos > 0) { + buffer.append(SUPER).append(' '); + index = consumeWhitespace(name, checkPos, end+1); + } + break; + } + start = index; // leading segment got processed + } + lastDotLookup: for (int i = end; i >= start; i--) { + switch (name[i]) { + case '.': + if (depth == 0) { + lastDot = i; + break lastDotLookup; + } + break; + case '<': + depth--; + if (depth == 0) lastGenericStart = i; + break; + case '>': + if (depth == 0) lastGenericEnd = i; + depth++; + break; + } + } + int nameStart = lastDot < 0 ? start : lastDot+1; + int nameEnd = lastGenericStart < 0 ? end+1 : lastGenericStart; + buffer.append(name, nameStart, nameEnd - nameStart); + if (lastGenericStart >= 0) { + appendArgumentSimpleNames(name, lastGenericStart, lastGenericEnd, buffer); + buffer.append(name, lastGenericEnd+1, end - lastGenericEnd); // copy trailing portion, may contain dimensions + } +} +// .d> --> > +private static void appendArgumentSimpleNames(char[] name, int start, int end, StringBuffer buffer) { + buffer.append('<'); + int depth = 0; + int argumentStart = -1; + int argumentCount = 0; + for (int i = start; i <= end; i++) { + switch(name[i]) { + case '<' : + depth++; + if (depth == 1) { + argumentStart = i+1; + } + break; + case '>' : + if (depth == 1) { + if (argumentCount > 0) buffer.append(','); + appendSimpleName(name, argumentStart, i-1, buffer); + argumentCount++; + } + depth--; + break; + case ',' : + if (depth == 1) { + if (argumentCount > 0) buffer.append(','); + appendSimpleName(name, argumentStart, i-1, buffer); + argumentCount++; + argumentStart = i+1; + } + break; + } + } + buffer.append('>'); +} +/** + * Returns all segments of the given dot-separated qualified name. + * Returns an array with only the given name if it is not qualified. + * Returns an empty array if the name is empty. + *

+ * For example: + *

+ * 
+ * getSimpleNames({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'O', 'b', 'j', 'e', 'c', 't'}}
+ * getSimpleNames({'O', 'b', 'j', 'e', 'c', 't'}) -> {{'O', 'b', 'j', 'e', 'c', 't'}}
+ * getSimpleNames({}) -> {}
+ * getSimpleNames({'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'L', 'i', 's', 't', '<', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '>'}) -> {{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'L', 'i', 's', 't', '<', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g'}}
+ * 
+ * 
+ * + * @param name the name + * @return the list of simple names, possibly empty + * @exception NullPointerException if name is null + * @since 2.0 + */ +public static char[][] getSimpleNames(char[] name) { + int length = name == null ? 0 : name.length; + if (length == 0) + return CharOperation.NO_CHAR_CHAR; + + int wordCount = 1; + countingWords: for (int i = 0; i < length; i++) + switch(name[i]) { + case C_DOT: + wordCount++; + break; + case C_GENERIC_START: + break countingWords; + } + char[][] split = new char[wordCount][]; + int last = 0, currentWord = 0; + for (int i = 0; i < length; i++) { + if (name[i] == C_GENERIC_START) break; + if (name[i] == C_DOT) { + split[currentWord] = new char[i - last]; + System.arraycopy( + name, + last, + split[currentWord++], + 0, + i - last); + last = i + 1; + } + } + split[currentWord] = new char[length - last]; + System.arraycopy(name, last, split[currentWord], 0, length - last); + return split; +} +/** + * Returns all segments of the given dot-separated qualified name. + * Returns an array with only the given name if it is not qualified. + * Returns an empty array if the name is empty. + *

+ * For example: + *

+ * 
+ * getSimpleNames("java.lang.Object") -> {"java", "lang", "Object"}
+ * getSimpleNames("Object") -> {"Object"}
+ * getSimpleNames("") -> {}
+ * getSimpleNames("java.util.List") -> {"java", "lang", "List
+ * 
+ * + * @param name the name + * @return the list of simple names, possibly empty + * @exception NullPointerException if name is null + */ +public static String[] getSimpleNames(String name) { + return CharOperation.toStrings(getSimpleNames(name.toCharArray())); +} +/** + * Converts the given method signature to a readable form. The method signature is expected to + * be dot-based. + *

+ * For example: + *

+ * 
+ * toString("([Ljava.lang.String;)V", "main", new String[] {"args"}, false, true) -> "void main(String[] args)"
+ * 
+ * 
+ *

+ * + * @param methodSignature the method signature to convert + * @param methodName the name of the method to insert in the result, or + * null if no method name is to be included + * @param parameterNames the parameter names to insert in the result, or + * null if no parameter names are to be included; if supplied, + * the number of parameter names must match that of the method signature + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param includeReturnType true if the return type is to be + * included + * @return the char array representation of the method signature + * + * @since 2.0 + */ +public static char[] toCharArray(char[] methodSignature, char[] methodName, char[][] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType) { + return toCharArray(methodSignature, methodName, parameterNames, fullyQualifyTypeNames, includeReturnType, false); +} +/** + * Converts the given method signature to a readable form. The method signature is expected to + * be dot-based. + *

+ * For example: + *

+ * 
+ * toString("([Ljava.lang.String;)V", "main", new String[] {"args"}, false, true) -> "void main(String[] args)"
+ * 
+ * 
+ *

+ * + * @param methodSignature the method signature to convert + * @param methodName the name of the method to insert in the result, or + * null if no method name is to be included + * @param parameterNames the parameter names to insert in the result, or + * null if no parameter names are to be included; if supplied, + * the number of parameter names must match that of the method signature + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param includeReturnType true if the return type is to be + * included + * @param isVargArgs true if the last argument should be displayed as a + * variable argument, false otherwise. + * @return the char array representation of the method signature + * + * @since 3.1 + */ +public static char[] toCharArray(char[] methodSignature, char[] methodName, char[][] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType, boolean isVargArgs) { + int firstParen = CharOperation.indexOf(C_PARAM_START, methodSignature); + if (firstParen == -1) { + throw new IllegalArgumentException(); + } + + StringBuffer buffer = new StringBuffer(methodSignature.length + 10); + + // return type + if (includeReturnType) { + char[] rts = getReturnType(methodSignature); + appendTypeSignature(rts, 0 , fullyQualifyTypeNames, buffer); + buffer.append(' '); + } + + // selector + if (methodName != null) { + buffer.append(methodName); + } + + // parameters + buffer.append('('); + char[][] pts = getParameterTypes(methodSignature); + for (int i = 0, max = pts.length; i < max; i++) { + if (i == max - 1) { + appendTypeSignature(pts[i], 0 , fullyQualifyTypeNames, buffer, isVargArgs); + } else { + appendTypeSignature(pts[i], 0 , fullyQualifyTypeNames, buffer); + } + if (parameterNames != null) { + buffer.append(' '); + buffer.append(parameterNames[i]); + } + if (i != pts.length - 1) { + buffer.append(','); + buffer.append(' '); + } + } + buffer.append(')'); + char[] result = new char[buffer.length()]; + buffer.getChars(0, buffer.length(), result, 0); + return result; +} +/** + * Converts the given type signature to a readable string. The signature is expected to + * be dot-based. + * + *

+ * For example: + *

+ * 
+ * toString({'[', 'L', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', ';'}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '[', ']'}
+ * toString({'I'}) -> {'i', 'n', 't'}
+ * toString({'+', 'L', 'O', 'b', 'j', 'e', 'c', 't', ';'}) -> {'?', ' ', 'e', 'x', 't', 'e', 'n', 'd', 's', ' ', 'O', 'b', 'j', 'e', 'c', 't'}
+ * 
+ * 
+ *

+ *

+ * Note: This method assumes that a type signature containing a '$' + * is an inner type signature. While this is correct in most cases, someone could + * define a non-inner type name containing a '$'. Handling this + * correctly in all cases would have required resolving the signature, which + * generally not feasible. + *

+ * + * @param signature the type signature + * @return the string representation of the type + * @exception IllegalArgumentException if the signature is not syntactically + * correct + * + * @since 2.0 + */ +public static char[] toCharArray(char[] signature) throws IllegalArgumentException { + int sigLength = signature.length; + if (sigLength == 0 || signature[0] == C_PARAM_START || signature[0] == C_GENERIC_START) { + return toCharArray(signature, CharOperation.NO_CHAR, null, true, true); + } + + StringBuffer buffer = new StringBuffer(signature.length + 10); + appendTypeSignature(signature, 0, true, buffer); + char[] result = new char[buffer.length()]; + buffer.getChars(0, buffer.length(), result, 0); + return result; +} + +/** + * Scans the given string for a type signature starting at the given + * index and appends it to the given buffer, and returns the index of the last + * character. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param buffer the string buffer to append to + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a type signature + * @see #scanTypeSignature(char[], int) + */ +private static int appendTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { + return appendTypeSignature(string, start, fullyQualifyTypeNames, buffer, false); +} +/** + * Scans the given string for a type signature starting at the given + * index and appends it to the given buffer, and returns the index of the last + * character. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param buffer the string buffer to append to + * @param isVarArgs true if the type must be displayed as a + * variable argument, false otherwise. In this case, the type must be an array type + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a type signature, or if isVarArgs is true, + * and the type is not an array type signature. + * @see #scanTypeSignature(char[], int) + */ +private static int appendTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer, boolean isVarArgs) { + // need a minimum 1 char + if (start >= string.length) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if (isVarArgs) { + switch (c) { + case C_ARRAY : + return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer, true); + case C_RESOLVED : + case C_UNRESOLVED : + case C_TYPE_VARIABLE : + case C_BOOLEAN : + case C_BYTE : + case C_CHAR : + case C_DOUBLE : + case C_FLOAT : + case C_INT : + case C_LONG : + case C_SHORT : + case C_VOID : + case C_STAR: + case C_EXTENDS: + case C_SUPER: + default: + throw new IllegalArgumentException(); // a var args is an array type + } + } else { + switch (c) { + case C_ARRAY : + return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer); + case C_RESOLVED : + case C_UNRESOLVED : + return appendClassTypeSignature(string, start, fullyQualifyTypeNames, buffer); + case C_TYPE_VARIABLE : + int e = scanTypeVariableSignature(string, start); + buffer.append(CharOperation.subarray(string, start + 1, e)); + return e; + case C_BOOLEAN : + buffer.append(BOOLEAN); + return start; + case C_BYTE : + buffer.append(BYTE); + return start; + case C_CHAR : + buffer.append(CHAR); + return start; + case C_DOUBLE : + buffer.append(DOUBLE); + return start; + case C_FLOAT : + buffer.append(FLOAT); + return start; + case C_INT : + buffer.append(INT); + return start; + case C_LONG : + buffer.append(LONG); + return start; + case C_SHORT : + buffer.append(SHORT); + return start; + case C_VOID : + buffer.append(VOID); + return start; + case C_STAR: + case C_EXTENDS: + case C_SUPER: + return appendTypeArgumentSignature(string, start, fullyQualifyTypeNames, buffer); + default : + throw new IllegalArgumentException(); + } + } +} +/** + * Scans the given string for an array type signature starting at the given + * index and appends it to the given buffer, and returns the index of the last + * character. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not an array type signature + * @see #scanArrayTypeSignature(char[], int) + */ +private static int appendArrayTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { + return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer, false); +} +/** + * Scans the given string for an array type signature starting at the given + * index and appends it to the given buffer, and returns the index of the last + * character. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param isVarArgs true if the array type must be displayed as a + * variable argument, false otherwise + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not an array type signature + * @see #scanArrayTypeSignature(char[], int) + */ +private static int appendArrayTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer, boolean isVarArgs) { + // need a minimum 2 char + if (start >= string.length - 1) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if (c != C_ARRAY) { //$NON-NLS-1$ + throw new IllegalArgumentException(); + } + int e = appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer); + if (isVarArgs) { + buffer.append('.').append('.').append('.'); + } else { + buffer.append('[').append(']'); + } + return e; +} +/** + * Scans the given string for a class type signature starting at the given + * index and appends it to the given buffer, and returns the index of the last + * character. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param buffer the string buffer to append to + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a class type signature + * @see #scanClassTypeSignature(char[], int) + */ +private static int appendClassTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { + // need a minimum 3 chars "Lx;" + if (start >= string.length - 2) { + throw new IllegalArgumentException(); + } + // must start in "L" or "Q" + char c = string[start]; + if (c != C_RESOLVED && c != C_UNRESOLVED) { + throw new IllegalArgumentException(); + } + boolean resolved = (c == C_RESOLVED); + boolean removePackageQualifiers = !fullyQualifyTypeNames; + if (!resolved) { + // keep everything in an unresolved name + removePackageQualifiers = false; + } + int p = start + 1; + int checkpoint = buffer.length(); + while (true) { + if (p >= string.length) { + throw new IllegalArgumentException(); + } + c = string[p]; + switch(c) { + case C_SEMICOLON : + // all done + return p; + case C_GENERIC_START : + int e = appendTypeArgumentSignatures(string, p, fullyQualifyTypeNames, buffer); + // once we hit type arguments there are no more package prefixes + removePackageQualifiers = false; + p = e; + break; + case C_DOT : + if (removePackageQualifiers) { + // erase package prefix + buffer.setLength(checkpoint); + } else { + buffer.append('.'); + } + break; + case '/' : + if (removePackageQualifiers) { + // erase package prefix + buffer.setLength(checkpoint); + } else { + buffer.append('/'); + } + break; + case C_DOLLAR : + if (resolved) { + // once we hit "$" there are no more package prefixes + removePackageQualifiers = false; + /** + * Convert '$' in resolved type signatures into '.'. + * NOTE: This assumes that the type signature is an inner type + * signature. This is true in most cases, but someone can define a + * non-inner type name containing a '$'. + */ + buffer.append('.'); + } + break; + default : + buffer.append(c); + } + p++; + } +} + +/** + * Scans the given string for a list of type arguments signature starting at the + * given index and appends it to the given buffer, and returns the index of the + * last character. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param buffer the string buffer to append to + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a list of type argument + * signatures + * @see #scanTypeArgumentSignatures(char[], int) + */ +private static int appendTypeArgumentSignatures(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { + // need a minimum 2 char "<>" + if (start >= string.length - 1) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if (c != C_GENERIC_START) { + throw new IllegalArgumentException(); + } + buffer.append('<'); + int p = start + 1; + int count = 0; + while (true) { + if (p >= string.length) { + throw new IllegalArgumentException(); + } + c = string[p]; + if (c == C_GENERIC_END) { + buffer.append('>'); + return p; + } + if (count != 0) { + buffer.append(','); + } + int e = appendTypeArgumentSignature(string, p, fullyQualifyTypeNames, buffer); + count++; + p = e + 1; + } +} + +/** + * Scans the given string for a type argument signature starting at the given + * index and appends it to the given buffer, and returns the index of the last + * character. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param buffer the string buffer to append to + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a type argument signature + * @see #scanTypeArgumentSignature(char[], int) + */ +private static int appendTypeArgumentSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { + // need a minimum 1 char + if (start >= string.length) { + throw new IllegalArgumentException(); + } + char c = string[start]; + switch(c) { + case C_STAR : + buffer.append('?'); + return start; + case C_EXTENDS : + buffer.append("? extends "); //$NON-NLS-1$ + return appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer); + case C_SUPER : + buffer.append("? super "); //$NON-NLS-1$ + return appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer); + default : + return appendTypeSignature(string, start, fullyQualifyTypeNames, buffer); + } +} + +/** + * Converts the given array of qualified name segments to a qualified name. + *

+ * For example: + *

+ * 
+ * toQualifiedName({{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'O', 'b', 'j', 'e', 'c', 't'}}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}
+ * toQualifiedName({{'O', 'b', 'j', 'e', 'c', 't'}}) -> {'O', 'b', 'j', 'e', 'c', 't'}
+ * toQualifiedName({{}}) -> {}
+ * 
+ * 
+ *

+ * + * @param segments the list of name segments, possibly empty + * @return the dot-separated qualified name, or the empty string + * + * @since 2.0 + */ +public static char[] toQualifiedName(char[][] segments) { + int length = segments.length; + if (length == 0) return CharOperation.NO_CHAR; + if (length == 1) return segments[0]; + + int resultLength = 0; + for (int i = 0; i < length; i++) { + resultLength += segments[i].length+1; + } + resultLength--; + char[] result = new char[resultLength]; + int index = 0; + for (int i = 0; i < length; i++) { + char[] segment = segments[i]; + int segmentLength = segment.length; + System.arraycopy(segment, 0, result, index, segmentLength); + index += segmentLength; + if (i != length-1) { + result[index++] = C_DOT; + } + } + return result; +} +/** + * Converts the given array of qualified name segments to a qualified name. + *

+ * For example: + *

+ * 
+ * toQualifiedName(new String[] {"java", "lang", "Object"}) -> "java.lang.Object"
+ * toQualifiedName(new String[] {"Object"}) -> "Object"
+ * toQualifiedName(new String[0]) -> ""
+ * 
+ * 
+ *

+ * + * @param segments the list of name segments, possibly empty + * @return the dot-separated qualified name, or the empty string + */ +public static String toQualifiedName(String[] segments) { + int length = segments.length; + char[][] charArrays = new char[length][]; + for (int i = 0; i < length; i++) { + charArrays[i] = segments[i].toCharArray(); + } + return new String(toQualifiedName(charArrays)); +} +/** + * Converts the given type signature to a readable string. The signature is expected to + * be dot-based. + * + *

+ * For example: + *

+ * 
+ * toString("[Ljava.lang.String;") -> "java.lang.String[]"
+ * toString("I") -> "int"
+ * toString("+QObject;") -> "? extends Object"
+ * 
+ * 
+ *

+ *

+ * Note: This method assumes that a type signature containing a '$' + * is an inner type signature. While this is correct in most cases, someone could + * define a non-inner type name containing a '$'. Handling this + * correctly in all cases would have required resolving the signature, which + * generally not feasible. + *

+ * + * @param signature the type signature + * @return the string representation of the type + * @exception IllegalArgumentException if the signature is not syntactically + * correct + */ +public static String toString(String signature) throws IllegalArgumentException { + return new String(toCharArray(signature.toCharArray())); +} +/** + * Converts the given method signature to a readable string. The method signature is expected to + * be dot-based. + * + * @param methodSignature the method signature to convert + * @param methodName the name of the method to insert in the result, or + * null if no method name is to be included + * @param parameterNames the parameter names to insert in the result, or + * null if no parameter names are to be included; if supplied, + * the number of parameter names must match that of the method signature + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param includeReturnType true if the return type is to be + * included + * @see #toCharArray(char[], char[], char[][], boolean, boolean) + * @return the string representation of the method signature + */ +public static String toString(String methodSignature, String methodName, String[] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType) { + return toString(methodSignature, methodName, parameterNames, fullyQualifyTypeNames, includeReturnType, false); +} +/** + * Converts the given method signature to a readable string. The method signature is expected to + * be dot-based. + * + * @param methodSignature the method signature to convert + * @param methodName the name of the method to insert in the result, or + * null if no method name is to be included + * @param parameterNames the parameter names to insert in the result, or + * null if no parameter names are to be included; if supplied, + * the number of parameter names must match that of the method signature + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param includeReturnType true if the return type is to be + * included + * @param isVarArgs true if the last argument should be displayed as a + * variable argument, false otherwise + * @see #toCharArray(char[], char[], char[][], boolean, boolean) + * @return the string representation of the method signature + */ +public static String toString(String methodSignature, String methodName, String[] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType, boolean isVarArgs) { + char[][] params; + if (parameterNames == null) { + params = null; + } else { + int paramLength = parameterNames.length; + params = new char[paramLength][]; + for (int i = 0; i < paramLength; i++) { + params[i] = parameterNames[i].toCharArray(); + } + } + return new String(toCharArray(methodSignature.toCharArray(), methodName == null ? null : methodName.toCharArray(), params, fullyQualifyTypeNames, includeReturnType, isVarArgs)); +} +} diff --git a/src/org/eclipse/jdt/core/compiler/CharOperation.java b/src/org/eclipse/jdt/core/compiler/CharOperation.java index 07d23e3..b21cdf4 100644 --- a/src/org/eclipse/jdt/core/compiler/CharOperation.java +++ b/src/org/eclipse/jdt/core/compiler/CharOperation.java @@ -28,6 +28,12 @@ public final class CharOperation { public static final char[][] NO_CHAR_CHAR = new char[0][]; /** + * Constant for an empty String array. + * @since 3.1 + */ + public static final String[] NO_STRINGS = new String[0]; + + /** * Answers a new array with appending the suffix character at the end of the array. *
*
@@ -171,13 +177,14 @@ public final class CharOperation { * @since 3.0 */ public static String[] charArrayToStringArray(char[][] charArrays) { - if (charArrays == null) { + if (charArrays == null) return null; - } - String[] strings= new String[charArrays.length]; - for (int i= 0; i < charArrays.length; i++) { + int length = charArrays.length; + if (length == 0) + return NO_STRINGS; + String[] strings= new String[length]; + for (int i= 0; i < length; i++) strings[i]= new String(charArrays[i]); - } return strings; } /** @@ -490,7 +497,7 @@ public final class CharOperation { /** * Answers the concatenation of the three arrays inserting the sep1 character between the - * two arrays and sep2 between the last two. + * first two arrays and sep2 between the last two. * It answers null if the three arrays are null. * If the first array is null, then it answers the concatenation of second and third inserting * the sep2 character between them. @@ -867,6 +874,40 @@ public final class CharOperation { return true; return false; } + + /** + * Answers true if the array contains an occurrence of one of the characters, false otherwise. + * + *
+ *
+ * For example: + *
    + *
  1. +	 *    characters = { 'c', 'd' }
    +	 *    array = { 'a', ' b'  }
    +	 *    result => false
    +	 * 
    + *
  2. + *
  3. +	 *    characters = { 'c', 'd' }
    +	 *    array = { 'a', ' b', 'c'  }
    +	 *    result => true
    +	 * 
    + *
  4. + *
+ * + * @param characters the characters to search + * @param array the array in which the search is done + * @return true if the array contains an occurrence of one of the characters, false otherwise. + * @throws NullPointerException if array is null. + */ + public static final boolean contains(char[] characters, char[] array) { + for (int i = array.length; --i >= 0;) + for (int j = characters.length; --j >= 0;) + if (array[i] == characters[j]) + return true; + return false; + } /** * Answers a deep copy of the toCopy array. @@ -2269,6 +2310,58 @@ public final class CharOperation { System.arraycopy(array, inStart, result, outStart, max - inStart); return result; } + + /** + * Replace all occurrence of the character to be replaced with the remplacement character + * in a copy of the given array. Returns the given array if no occurrences of the character + * to be replaced are found. + *
+ *
+ * For example: + *
    + *
  1. +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    toBeReplaced = 'b'
    +	 *    replacementChar = 'a'
    +	 *    result => A new array that is equals to { 'a' , 'a', 'a', 'a', 'a', 'a' }
    +	 * 
    + *
  2. + *
  3. +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    toBeReplaced = 'c'
    +	 *    replacementChar = 'a'
    +	 *    result => The original array that remains unchanged.
    +	 * 
    + *
  4. + *
+ * + * @param array the given array + * @param toBeReplaced the character to be replaced + * @param replacementChar the replacement character + * @throws NullPointerException if the given array is null + * @since 3.1 + */ + public static final char[] replaceOnCopy( + char[] array, + char toBeReplaced, + char replacementChar) { + + char[] result = null; + for (int i = 0, length = array.length; i < length; i++) { + char c = array[i]; + if (c == toBeReplaced) { + if (result == null) { + result = new char[length]; + System.arraycopy(array, 0, result, 0, i); + } + result[i] = replacementChar; + } else if (result != null) { + result[i] = c; + } + } + if (result == null) return array; + return result; + } /** * Return a new array which is the split of the given array using the given divider and triming each subarray to remove @@ -2677,7 +2770,9 @@ public final class CharOperation { * @since 3.0 */ final static public String[] toStrings(char[][] array) { + if (array == null) return NO_STRINGS; int length = array.length; + if (length == 0) return NO_STRINGS; String[] result = new String[length]; for (int i = 0; i < length; i++) result[i] = new String(array[i]); diff --git a/src/org/eclipse/jdt/core/compiler/IProblem.java b/src/org/eclipse/jdt/core/compiler/IProblem.java index 7be59f1..48c5422 100644 --- a/src/org/eclipse/jdt/core/compiler/IProblem.java +++ b/src/org/eclipse/jdt/core/compiler/IProblem.java @@ -69,6 +69,9 @@ * JavadocInvalidTag * JavadocMessagePrefix * EmptyControlFlowStatement + * IBM Corporation - added the following constants + * IllegalUsageOfQualifiedTypeReference + * InvalidDigit ****************************************************************************/ package org.eclipse.jdt.core.compiler; @@ -229,7 +232,7 @@ public interface IProblem { int InternalTypeNameProvided = TypeRelated + 6; /** @since 2.1 */ int UnusedPrivateType = Internal + TypeRelated + 7; - + int IncompatibleTypesInEqualityOperator = TypeRelated + 15; int IncompatibleTypesInConditionalOperator = TypeRelated + 16; int TypeMismatch = TypeRelated + 17; @@ -301,42 +304,21 @@ public interface IProblem { int DuplicateBlankFinalFieldInitialization = FieldRelated + 82; // variable hiding - /** - * The local variable {0} is hiding another local variable defined in an enclosing type scope - * @since 3.0 - */ + /** @since 3.0 */ int LocalVariableHidingLocalVariable = Internal + 90; - - /** - * The local variable {0} is hiding the field {1}.{2} - * @since 3.0 - */ + /** @since 3.0 */ int LocalVariableHidingField = Internal + FieldRelated + 91; - - /** - * The field {0}.{1} is hiding another local variable defined in an enclosing type scope - * @since 3.0 - */ + /** @since 3.0 */ int FieldHidingLocalVariable = Internal + FieldRelated + 92; - - /** - * The field {0}.{1} is hiding the field {2}.{3} - * @since 3.0 - */ + /** @since 3.0 */ int FieldHidingField = Internal + FieldRelated + 93; - - /** - * The argument {0} is hiding another local variable defined in an enclosing type scope - * @since 3.0 - */ + /** @since 3.0 */ int ArgumentHidingLocalVariable = Internal + 94; - - /** - * The argument {0} is hiding the field {2}.{3} - * @since 3.0 - */ + /** @since 3.0 */ int ArgumentHidingField = Internal + 95; - + /** @since 3.1 */ + int MissingSerialVersion = Internal + 96; + // methods int UndefinedMethod = MethodRelated + 100; int NotVisibleMethod = MethodRelated + 101; @@ -361,7 +343,6 @@ public interface IProblem { /** @since 3.0 */ int IndirectAccessToStaticMethod = Internal + MethodRelated + 119; - // constructors int UndefinedConstructor = ConstructorRelated + 130; int NotVisibleConstructor = ConstructorRelated + 131; @@ -445,7 +426,7 @@ public interface IProblem { /** @since 3.0 */ int UnusedConstructorDeclaredThrownException = Internal + 186; /** @since 3.0 */ - int InvalidCatchBlockSequence = Internal + TypeRelated + 187; + int InvalidCatchBlockSequence = Internal + TypeRelated + 187; /** @since 3.0 */ int EmptyControlFlowStatement = Internal + TypeRelated + 188; /** @since 3.0 */ @@ -462,6 +443,9 @@ public interface IProblem { int InheritedFieldHidesEnclosingName = FieldRelated + 196; int InheritedTypeHidesEnclosingName = TypeRelated + 197; + /** @since 3.1 */ + int IllegalUsageOfQualifiedTypeReference = Internal + Syntax + 198; + // miscellaneous int ThisInStaticContext = Internal + 200; int StaticMethodRequested = Internal + MethodRelated + 201; @@ -526,6 +510,8 @@ public interface IProblem { int NullSourceString = Syntax + Internal + 258; int UnterminatedString = Syntax + Internal + 259; int UnterminatedComment = Syntax + Internal + 260; + /** @since 3.1 */ + int InvalidDigit = Syntax + Internal + 262; // type related problems int InterfaceCannotHaveInitializers = TypeRelated + 300; @@ -535,7 +521,8 @@ public interface IProblem { int IllegalModifierForMemberClass = TypeRelated + 304; int IllegalModifierForMemberInterface = TypeRelated + 305; int IllegalModifierForLocalClass = TypeRelated + 306; - + /** @since 3.1 */ + int ForbiddenReference = TypeRelated + 307; int IllegalModifierCombinationFinalAbstractForClass = TypeRelated + 308; int IllegalVisibilityModifierForInterfaceMemberType = TypeRelated + 309; int IllegalVisibilityModifierCombinationForMemberType = TypeRelated + 310; @@ -559,19 +546,29 @@ public interface IProblem { int PackageIsNotExpectedPackage = 328; /** @since 2.1 */ int ObjectCannotHaveSuperTypes = 329; + /** @since 3.1 */ + int ObjectMustBeClass = 330; - // int InvalidSuperclassBase = TypeRelated + 329; // reserved to 334 included + /** @deprecated - problem is no longer generated, UndefinedType is used instead */ int SuperclassNotFound = TypeRelated + 329 + ProblemReasons.NotFound; // TypeRelated + 330 + /** @deprecated - problem is no longer generated, NotVisibleType is used instead */ int SuperclassNotVisible = TypeRelated + 329 + ProblemReasons.NotVisible; // TypeRelated + 331 + /** @deprecated - problem is no longer generated, use AmbiguousType is used instead */ int SuperclassAmbiguous = TypeRelated + 329 + ProblemReasons.Ambiguous; // TypeRelated + 332 + /** @deprecated - problem is no longer generated, use InternalTypeNameProvided is used instead */ int SuperclassInternalNameProvided = TypeRelated + 329 + ProblemReasons.InternalNameProvided; // TypeRelated + 333 + /** @deprecated - problem is no longer generated, use InheritedTypeHidesEnclosingName is used instead */ int SuperclassInheritedNameHidesEnclosingName = TypeRelated + 329 + ProblemReasons.InheritedNameHidesEnclosingName; // TypeRelated + 334 - // int InvalidInterfaceBase = TypeRelated + 334; // reserved to 339 included + /** @deprecated - problem is no longer generated, UndefinedType is used instead */ int InterfaceNotFound = TypeRelated + 334 + ProblemReasons.NotFound; // TypeRelated + 335 + /** @deprecated - problem is no longer generated, NotVisibleType is used instead */ int InterfaceNotVisible = TypeRelated + 334 + ProblemReasons.NotVisible; // TypeRelated + 336 + /** @deprecated - problem is no longer generated, use AmbiguousType is used instead */ int InterfaceAmbiguous = TypeRelated + 334 + ProblemReasons.Ambiguous; // TypeRelated + 337 + /** @deprecated - problem is no longer generated, use InternalTypeNameProvided is used instead */ int InterfaceInternalNameProvided = TypeRelated + 334 + ProblemReasons.InternalNameProvided; // TypeRelated + 338 + /** @deprecated - problem is no longer generated, use InheritedTypeHidesEnclosingName is used instead */ int InterfaceInheritedNameHidesEnclosingName = TypeRelated + 334 + ProblemReasons.InheritedNameHidesEnclosingName; // TypeRelated + 339 // field related problems @@ -583,11 +580,15 @@ public interface IProblem { int IllegalModifierCombinationFinalVolatileForField = FieldRelated + 345; int UnexpectedStaticModifierForField = FieldRelated + 346; - // int FieldTypeProblemBase = FieldRelated + 349; //reserved to 354 + /** @deprecated - problem is no longer generated, UndefinedType is used instead */ int FieldTypeNotFound = FieldRelated + 349 + ProblemReasons.NotFound; // FieldRelated + 350 + /** @deprecated - problem is no longer generated, NotVisibleType is used instead */ int FieldTypeNotVisible = FieldRelated + 349 + ProblemReasons.NotVisible; // FieldRelated + 351 + /** @deprecated - problem is no longer generated, use AmbiguousType is used instead */ int FieldTypeAmbiguous = FieldRelated + 349 + ProblemReasons.Ambiguous; // FieldRelated + 352 + /** @deprecated - problem is no longer generated, use InternalTypeNameProvided is used instead */ int FieldTypeInternalNameProvided = FieldRelated + 349 + ProblemReasons.InternalNameProvided; // FieldRelated + 353 + /** @deprecated - problem is no longer generated, use InheritedTypeHidesEnclosingName is used instead */ int FieldTypeInheritedNameHidesEnclosingName = FieldRelated + 349 + ProblemReasons.InheritedNameHidesEnclosingName; // FieldRelated + 354 // method related problems @@ -606,25 +607,37 @@ public interface IProblem { int NativeMethodsCannotBeStrictfp = MethodRelated + 367; int DuplicateModifierForArgument = MethodRelated + 368; - // int ArgumentProblemBase = MethodRelated + 369; // reserved to 374 included. + /** @deprecated - problem is no longer generated, UndefinedType is used instead */ int ArgumentTypeNotFound = MethodRelated + 369 + ProblemReasons.NotFound; // MethodRelated + 370 + /** @deprecated - problem is no longer generated, NotVisibleType is used instead */ int ArgumentTypeNotVisible = MethodRelated + 369 + ProblemReasons.NotVisible; // MethodRelated + 371 + /** @deprecated - problem is no longer generated, use AmbiguousType is used instead */ int ArgumentTypeAmbiguous = MethodRelated + 369 + ProblemReasons.Ambiguous; // MethodRelated + 372 + /** @deprecated - problem is no longer generated, use InternalTypeNameProvided is used instead */ int ArgumentTypeInternalNameProvided = MethodRelated + 369 + ProblemReasons.InternalNameProvided; // MethodRelated + 373 + /** @deprecated - problem is no longer generated, use InheritedTypeHidesEnclosingName is used instead */ int ArgumentTypeInheritedNameHidesEnclosingName = MethodRelated + 369 + ProblemReasons.InheritedNameHidesEnclosingName; // MethodRelated + 374 - // int ExceptionTypeProblemBase = MethodRelated + 374; // reserved to 379 included. + /** @deprecated - problem is no longer generated, UndefinedType is used instead */ int ExceptionTypeNotFound = MethodRelated + 374 + ProblemReasons.NotFound; // MethodRelated + 375 + /** @deprecated - problem is no longer generated, NotVisibleType is used instead */ int ExceptionTypeNotVisible = MethodRelated + 374 + ProblemReasons.NotVisible; // MethodRelated + 376 + /** @deprecated - problem is no longer generated, use AmbiguousType is used instead */ int ExceptionTypeAmbiguous = MethodRelated + 374 + ProblemReasons.Ambiguous; // MethodRelated + 377 + /** @deprecated - problem is no longer generated, use InternalTypeNameProvided is used instead */ int ExceptionTypeInternalNameProvided = MethodRelated + 374 + ProblemReasons.InternalNameProvided; // MethodRelated + 378 + /** @deprecated - problem is no longer generated, use InheritedTypeHidesEnclosingName is used instead */ int ExceptionTypeInheritedNameHidesEnclosingName = MethodRelated + 374 + ProblemReasons.InheritedNameHidesEnclosingName; // MethodRelated + 379 - // int ReturnTypeProblemBase = MethodRelated + 379; + /** @deprecated - problem is no longer generated, UndefinedType is used instead */ int ReturnTypeNotFound = MethodRelated + 379 + ProblemReasons.NotFound; // MethodRelated + 380 + /** @deprecated - problem is no longer generated, NotVisibleType is used instead */ int ReturnTypeNotVisible = MethodRelated + 379 + ProblemReasons.NotVisible; // MethodRelated + 381 + /** @deprecated - problem is no longer generated, use AmbiguousType is used instead */ int ReturnTypeAmbiguous = MethodRelated + 379 + ProblemReasons.Ambiguous; // MethodRelated + 382 + /** @deprecated - problem is no longer generated, use InternalTypeNameProvided is used instead */ int ReturnTypeInternalNameProvided = MethodRelated + 379 + ProblemReasons.InternalNameProvided; // MethodRelated + 383 + /** @deprecated - problem is no longer generated, use InheritedTypeHidesEnclosingName is used instead */ int ReturnTypeInheritedNameHidesEnclosingName = MethodRelated + 379 + ProblemReasons.InheritedNameHidesEnclosingName; // MethodRelated + 384 // import related problems @@ -633,16 +646,26 @@ public interface IProblem { int CannotImportPackage = ImportRelated + 387; int UnusedImport = ImportRelated + 388; - // int ImportProblemBase = ImportRelated + 389; int ImportNotFound = ImportRelated + 389 + ProblemReasons.NotFound; // ImportRelated + 390 + /** @deprecated - problem is no longer generated, NotVisibleType is used instead */ int ImportNotVisible = ImportRelated + 389 + ProblemReasons.NotVisible; // ImportRelated + 391 + /** @deprecated - problem is no longer generated, use AmbiguousType is used instead */ int ImportAmbiguous = ImportRelated + 389 + ProblemReasons.Ambiguous; // ImportRelated + 392 + /** @deprecated - problem is no longer generated, use InternalTypeNameProvided is used instead */ int ImportInternalNameProvided = ImportRelated + 389 + ProblemReasons.InternalNameProvided; // ImportRelated + 393 + /** @deprecated - problem is no longer generated, use InheritedTypeHidesEnclosingName is used instead */ int ImportInheritedNameHidesEnclosingName = ImportRelated + 389 + ProblemReasons.InheritedNameHidesEnclosingName; // ImportRelated + 394 + /** @since 3.1 */ + int InvalidTypeForStaticImport = ImportRelated + 391; + // local variable related problems int DuplicateModifierForVariable = MethodRelated + 395; int IllegalModifierForVariable = MethodRelated + 396; + /** @since 3.1 */ + int LocalVariableCannotBeNull = MethodRelated + 397; + /** @since 3.1 */ + int LocalVariableCanOnlyBeNull = MethodRelated + 398; // method verifier problems int AbstractMethodMustBeImplemented = MethodRelated + 400; @@ -662,7 +685,9 @@ public interface IProblem { int IncompatibleReturnTypeForNonInheritedInterfaceMethod = MethodRelated + 413; /** @since 2.1 */ int IncompatibleExceptionInThrowsClauseForNonInheritedInterfaceMethod = MethodRelated + 414; - + /** @since 3.0 */ + int IllegalVararg = MethodRelated + 415; + // code snippet support int CodeSnippetMissingClass = Internal + 420; int CodeSnippetMissingMethod = Internal + 421; @@ -684,6 +709,9 @@ public interface IProblem { // assertion warning int UseAssertAsAnIdentifier = Internal + 440; + // 1.5 features + int UseEnumAsAnIdentifier = Internal + 441; + // detected task /** @since 2.1 */ int Task = Internal + 450; @@ -720,9 +748,9 @@ public interface IProblem { /** @since 3.0 */ int JavadocInvalidThrowsClassName = Javadoc + Internal + 481; /** @since 3.0 */ - int JavadocMissingSeeReference = Javadoc + Internal + 482; + int JavadocMissingReference = Javadoc + Internal + 482; /** @since 3.0 */ - int JavadocInvalidSeeReference = Javadoc + Internal + 483; + int JavadocInvalidReference = Javadoc + Internal + 483; /** @since 3.0 */ int JavadocInvalidSeeHref = Javadoc + Internal + 484; /** @since 3.0 */ @@ -794,7 +822,235 @@ public interface IProblem { /** @since 3.0 */ int JavadocUnterminatedInlineTag = Javadoc + Internal + 512; /** @since 3.0 */ - int JavadocMalformedSeeReference = Javadoc + Internal + 513; + int JavadocMissingHashCharacter = Javadoc + Internal + 513; /** @since 3.0 */ - int JavadocMessagePrefix = Internal + 515; -} + int JavadocMalformedSeeReference = Javadoc + Internal + 514; + /** @since 3.0 */ + int JavadocEmptyReturnTag = Javadoc + Internal + 515; + /** @since 3.1 */ + int JavadocInvalidValueReference = Javadoc + Internal + 516; + /** @since 3.1 */ + int JavadocUnexpectedText = Javadoc + Internal + 517; + /** @since 3.1 */ + int JavadocInvalidParamTagName = Javadoc + Internal + 518; + /** @since 3.1 */ + int JavadocInvalidParamTagTypeParameter = Javadoc + Internal + 469; + /** @since 3.0 */ + int JavadocMessagePrefix = Internal + 519; + + /** + * Generics + */ + /** @since 3.1 */ + int DuplicateTypeVariable = Internal + 520; + /** @since 3.1 */ + int IllegalTypeVariableSuperReference = Internal + 521; + /** @since 3.1 */ + int TypeVariableReferenceFromStaticContext = Internal + 522; + /** @since 3.1 */ + int ObjectCannotBeGeneric = Internal + 523; + /** @since 3.1 */ + int NonGenericType = TypeRelated + 524; + /** @since 3.1 */ + int IncorrectArityForParameterizedType = TypeRelated + 525; + /** @since 3.1 */ + int TypeArgumentMismatch = TypeRelated + 526; + /** @since 3.1 */ + int DuplicateMethodErasure = TypeRelated + 527; + /** @since 3.1 */ + int ReferenceToForwardTypeVariable = TypeRelated + 528; + /** @since 3.1 */ + int BoundsMustBeAnInterface = TypeRelated + 529; + /** @since 3.1 */ + int UnsafeRawConstructorInvocation = TypeRelated + 530; + /** @since 3.1 */ + int UnsafeRawMethodInvocation = TypeRelated + 531; + /** @since 3.1 */ + int UnsafeRawConversion = TypeRelated + 532; + /** @since 3.1 */ + int InvalidTypeVariableExceptionType = TypeRelated + 533; + /** @since 3.1 */ + int InvalidParameterizedExceptionType = TypeRelated + 534; + /** @since 3.1 */ + int IllegalGenericArray = TypeRelated + 535; + /** @since 3.1 */ + int UnsafeRawFieldAssignment = TypeRelated + 536; + /** @since 3.1 */ + int FinalBoundForTypeVariable = TypeRelated + 537; + /** @since 3.1 */ + int UndefinedTypeVariable = Internal + 538; + /** @since 3.1 */ + int SuperInterfacesCollide = TypeRelated + 539; + /** @since 3.1 */ + int WildcardConstructorInvocation = TypeRelated + 540; + /** @since 3.1 */ + int WildcardMethodInvocation = TypeRelated + 541; + /** @since 3.1 */ + int WildcardFieldAssignment = TypeRelated + 542; + /** @since 3.1 */ + int GenericMethodTypeArgumentMismatch = TypeRelated + 543; + /** @since 3.1 */ + int GenericConstructorTypeArgumentMismatch = TypeRelated + 544; + /** @since 3.1 */ + int UnsafeGenericCast = TypeRelated + 545; + /** @since 3.1 */ + int IllegalInstanceofParameterizedType = Internal + 546; + /** @since 3.1 */ + int IllegalInstanceofTypeParameter = Internal + 547; + /** @since 3.1 */ + int NonGenericMethod = TypeRelated + 548; + /** @since 3.1 */ + int IncorrectArityForParameterizedMethod = TypeRelated + 549; + /** @since 3.1 */ + int ParameterizedMethodArgumentTypeMismatch = TypeRelated + 550; + /** @since 3.1 */ + int NonGenericConstructor = TypeRelated + 551; + /** @since 3.1 */ + int IncorrectArityForParameterizedConstructor = TypeRelated + 552; + /** @since 3.1 */ + int ParameterizedConstructorArgumentTypeMismatch = TypeRelated + 553; + /** @since 3.1 */ + int TypeArgumentsForRawGenericMethod = TypeRelated + 554; + /** @since 3.1 */ + int TypeArgumentsForRawGenericConstructor = TypeRelated + 555; + /** @since 3.1 */ + int SuperTypeUsingWildcard = TypeRelated + 556; + /** @since 3.1 */ + int GenericTypeCannotExtendThrowable = TypeRelated + 557; + /** @since 3.1 */ + int IllegalClassLiteralForTypeVariable = TypeRelated + 558; + /** @since 3.1 */ + int UnsafeReturnTypeOverride = MethodRelated + 559; + /** @since 3.1 */ + int MethodNameClash = MethodRelated + 560; + /** @since 3.1 */ + int RawMemberTypeCannotBeParameterized = TypeRelated + 561; + /** @since 3.1 */ + int MissingArgumentsForParameterizedMemberType = TypeRelated + 562; + /** @since 3.1 */ + int StaticMemberOfParameterizedType = TypeRelated + 563; + /** @since 3.1 */ + int BoundHasConflictingArguments = TypeRelated + 564; + /** @since 3.1 */ + int DuplicateParameterizedMethods = MethodRelated + 565; + + /** + * Foreach + */ + /** @since 3.1 */ + int IncompatibleTypesInForeach = TypeRelated + 580; + /** @since 3.1 */ + int InvalidTypeForCollection = Internal + 581; + + /** + * 1.5 Syntax errors (when source level < 1.5) + */ + /** @since 3.1 */ + int InvalidUsageOfTypeParameters = Syntax + Internal + 590; + /** @since 3.1 */ + int InvalidUsageOfStaticImports = Syntax + Internal + 591; + /** @since 3.1 */ + int InvalidUsageOfForeachStatements = Syntax + Internal + 592; + /** @since 3.1 */ + int InvalidUsageOfTypeArguments = Syntax + Internal + 593; + /** @since 3.1 */ + int InvalidUsageOfEnumDeclarations = Syntax + Internal + 594; + /** @since 3.1 */ + int InvalidUsageOfVarargs = Syntax + Internal + 595; + /** @since 3.1 */ + int InvalidUsageOfAnnotations = Syntax + Internal + 596; + /** @since 3.1 */ + int InvalidUsageOfAnnotationDeclarations = Syntax + Internal + 597; + + /** + * Annotation + */ + /** @since 3.1 */ + int IllegalModifierForAnnotationMethod = MethodRelated + 600; + /** @since 3.1 */ + int IllegalExtendedDimensions = MethodRelated + 601; + /** @since 3.1 */ + int InvalidFileNameForPackageAnnotations = Syntax + Internal + 602; + /** @since 3.1 */ + int IllegalModifierForAnnotationType = TypeRelated + 603; + /** @since 3.1 */ + int IllegalModifierForAnnotationMemberType = TypeRelated + 604; + /** @since 3.1 */ + int InvalidAnnotationMemberType = TypeRelated + 605; + /** @since 3.1 */ + int AnnotationCircularitySelfReference = TypeRelated + 606; + /** @since 3.1 */ + int AnnotationCircularity = TypeRelated + 607; + /** @since 3.1 */ + int DuplicateAnnotation = TypeRelated + 608; + /** @since 3.1 */ + int MissingValueForAnnotationMember = TypeRelated + 609; + /** @since 3.1 */ + int DuplicateAnnotationMember = Internal + 610; + /** @since 3.1 */ + int UndefinedAnnotationMember = MethodRelated + 611; + /** @since 3.1 */ + int AnnotationValueMustBeClassLiteral = Internal + 612; + /** @since 3.1 */ + int AnnotationValueMustBeConstant = Internal + 613; + /** @since 3.1 */ + int AnnotationFieldNeedConstantInitialization = Internal + 614; + /** @since 3.1 */ + int IllegalModifierForAnnotationField = Internal + 615; + /** @since 3.1 */ + int AnnotationCannotOverrideMethod = MethodRelated + 616; + /** @since 3.1 */ + int AnnotationMembersCannotHaveParameters = Syntax + Internal + 617; + /** @since 3.1 */ + int AnnotationMembersCannotHaveTypeParameters = Syntax + Internal + 618; + /** @since 3.1 */ + int AnnotationTypeDeclarationCannotHaveSuperclass = Syntax + Internal + 619; + /** @since 3.1 */ + int AnnotationTypeDeclarationCannotHaveSuperinterfaces = Syntax + Internal + 620; + /** @since 3.1 */ + int DuplicateTargetInTargetAnnotation = Internal + 621; + /** @since 3.1 */ + int DisallowedTargetForAnnotation = TypeRelated + 622; + /** @since 3.1 */ + int MethodMustOverride = TypeRelated + 623; + /** @since 3.1 */ + int AnnotationTypeDeclarationCannotHaveConstructor = Syntax + Internal + 624; + + /** + * Corrupted binaries + */ + /** @since 3.1 */ + int CorruptedSignature = Internal + 700; + + /** + * Enum + */ + /** @since 3.1 */ + int IllegalModifierForEnum = TypeRelated + 750; + /** @since 3.1 */ + int IllegalModifierForEnumConstant = FieldRelated + 751; + /** @since 3.1 */ + int IllegalModifierForLocalEnum = TypeRelated + 752; + /** @since 3.1 */ + int IllegalModifierForMemberEnum = TypeRelated + 753; + /** @since 3.1 */ + int CannotDeclareEnumSpecialMethod = MethodRelated + 754; + /** @since 3.1 */ + int IllegalQualifiedEnumConstantLabel = FieldRelated + 755; + /** @since 3.1 */ + int CannotExtendEnum = TypeRelated + 756; + /** @since 3.1 */ + int CannotInvokeSuperConstructorInEnum = MethodRelated + 757; + /** @since 3.1 */ + int EnumAbstractMethodMustBeImplemented = MethodRelated + 758; + + /** + * Var args + */ + /** @since 3.1 */ + int IllegalExtendedDimensionsForVarArgs = Syntax + Internal + 800; + /** @since 3.1 */ + int MethodVarargsArgumentNeedCast = MethodRelated + 801; + /** @since 3.1 */ + int ConstructorVarargsArgumentNeedCast = ConstructorRelated + 802; +} \ No newline at end of file diff --git a/src/org/eclipse/jdt/core/compiler/InvalidInputException.java b/src/org/eclipse/jdt/core/compiler/InvalidInputException.java index 7055b43..508f522 100644 --- a/src/org/eclipse/jdt/core/compiler/InvalidInputException.java +++ b/src/org/eclipse/jdt/core/compiler/InvalidInputException.java @@ -16,8 +16,8 @@ package org.eclipse.jdt.core.compiler; */ public class InvalidInputException extends Exception { - private static final long serialVersionUID = 2909732853499731592L; // backward compatible - + private static final long serialVersionUID = 2909732853499731592L; // backward compatible + /** * Creates a new exception with no detail message. */ diff --git a/src/org/eclipse/jdt/internal/compiler/ASTVisitor.java b/src/org/eclipse/jdt/internal/compiler/ASTVisitor.java index 9e1166d..1a105fb 100644 --- a/src/org/eclipse/jdt/internal/compiler/ASTVisitor.java +++ b/src/org/eclipse/jdt/internal/compiler/ASTVisitor.java @@ -30,33 +30,11 @@ public abstract class ASTVisitor { public void endVisit(AND_AND_Expression and_and_Expression, BlockScope scope) { // do nothing by default } - public void endVisit(JavadocArrayQualifiedTypeReference typeRef, BlockScope scope) { - // do nothing by default - } - public void endVisit(JavadocArraySingleTypeReference typeRef, BlockScope scope) { - // do nothing by default - } - public void endVisit(JavadocArgumentExpression expression, BlockScope scope) { - // do nothing by default - } - public void endVisit(JavadocFieldReference fieldRef, BlockScope scope) { - // do nothing by default - } - public void endVisit(JavadocMessageSend messageSend, BlockScope scope) { - // do nothing by default - } - public void endVisit(JavadocQualifiedTypeReference typeRef, BlockScope scope) { - // do nothing by default - } - public void endVisit(JavadocReturnStatement statement, BlockScope scope) { - // do nothing by default - } - public void endVisit(JavadocSingleNameReference argument, BlockScope scope) { - // do nothing by default - } - public void endVisit(JavadocSingleTypeReference typeRef, BlockScope scope) { - // do nothing by default - } + public void endVisit( + AnnotationMethodDeclaration annotationTypeDeclaration, + ClassScope classScope) { + // do nothing by default + } public void endVisit(Argument argument, BlockScope scope) { // do nothing by default } @@ -87,10 +65,10 @@ public abstract class ASTVisitor { public void endVisit(ArrayTypeReference arrayTypeReference, ClassScope scope) { // do nothing by default } - public void endVisit(Assignment assignment, BlockScope scope) { + public void endVisit(AssertStatement assertStatement, BlockScope scope) { // do nothing by default } - public void endVisit(AssertStatement assertStatement, BlockScope scope) { + public void endVisit(Assignment assignment, BlockScope scope) { // do nothing by default } public void endVisit(BinaryExpression binaryExpression, BlockScope scope) { @@ -144,6 +122,9 @@ public abstract class ASTVisitor { public void endVisit(DoubleLiteral doubleLiteral, BlockScope scope) { // do nothing by default } + public void endVisit(EmptyStatement emptyStatement, BlockScope scope) { + // do nothing by default + } public void endVisit(EqualExpression equalExpression, BlockScope scope) { // do nothing by default } @@ -169,7 +150,7 @@ public abstract class ASTVisitor { public void endVisit(FloatLiteral floatLiteral, BlockScope scope) { // do nothing by default } - public void endVisit(EmptyStatement emptyStatement, BlockScope scope) { + public void endVisit(ForeachStatement forStatement, BlockScope scope) { // do nothing by default } public void endVisit(ForStatement forStatement, BlockScope scope) { @@ -192,6 +173,33 @@ public abstract class ASTVisitor { public void endVisit(IntLiteral intLiteral, BlockScope scope) { // do nothing by default } + public void endVisit(JavadocArgumentExpression expression, BlockScope scope) { + // do nothing by default + } + public void endVisit(JavadocArrayQualifiedTypeReference typeRef, BlockScope scope) { + // do nothing by default + } + public void endVisit(JavadocArraySingleTypeReference typeRef, BlockScope scope) { + // do nothing by default + } + public void endVisit(JavadocFieldReference fieldRef, BlockScope scope) { + // do nothing by default + } + public void endVisit(JavadocMessageSend messageSend, BlockScope scope) { + // do nothing by default + } + public void endVisit(JavadocQualifiedTypeReference typeRef, BlockScope scope) { + // do nothing by default + } + public void endVisit(JavadocReturnStatement statement, BlockScope scope) { + // do nothing by default + } + public void endVisit(JavadocSingleNameReference argument, BlockScope scope) { + // do nothing by default + } + public void endVisit(JavadocSingleTypeReference typeRef, BlockScope scope) { + // do nothing by default + } public void endVisit(LabeledStatement labeledStatement, BlockScope scope) { // do nothing by default } @@ -201,6 +209,36 @@ public abstract class ASTVisitor { public void endVisit(LongLiteral longLiteral, BlockScope scope) { // do nothing by default } + /** + * @param annotation + * @param scope + * @since 3.1 + */ + public void endVisit(MarkerAnnotation annotation, BlockScope scope) { + // do nothing by default + } + /** + * @param annotation + * @param scope + * @since 3.1 + */ + public void endVisit(MarkerAnnotation annotation, CompilationUnitScope scope) { + // do nothing by default + } + /** + * @param pair + * @param scope + */ + public void endVisit(MemberValuePair pair, BlockScope scope) { + // do nothing by default + } + /** + * @param pair + * @param scope + */ + public void endVisit(MemberValuePair pair, CompilationUnitScope scope) { + // do nothing by default + } public void endVisit(MessageSend messageSend, BlockScope scope) { // do nothing by default } @@ -210,12 +248,40 @@ public abstract class ASTVisitor { public void endVisit(StringLiteralConcatenation literal, BlockScope scope) { // do nothing by default } + /** + * @param annotation + * @param scope + * @since 3.1 + */ + public void endVisit(NormalAnnotation annotation, BlockScope scope) { + // do nothing by default + } + /** + * @param annotation + * @param scope + * @since 3.1 + */ + public void endVisit(NormalAnnotation annotation, CompilationUnitScope scope) { + // do nothing by default + } public void endVisit(NullLiteral nullLiteral, BlockScope scope) { // do nothing by default } public void endVisit(OR_OR_Expression or_or_Expression, BlockScope scope) { // do nothing by default } + public void endVisit(ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference, BlockScope scope) { + // do nothing by default + } + public void endVisit(ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference, ClassScope scope) { + // do nothing by default + } + public void endVisit(ParameterizedSingleTypeReference parameterizedSingleTypeReference, BlockScope scope) { + // do nothing by default + } + public void endVisit(ParameterizedSingleTypeReference parameterizedSingleTypeReference, ClassScope scope) { + // do nothing by default + } public void endVisit(PostfixExpression postfixExpression, BlockScope scope) { // do nothing by default } @@ -255,6 +321,22 @@ public abstract class ASTVisitor { public void endVisit(ReturnStatement returnStatement, BlockScope scope) { // do nothing by default } + /** + * @param annotation + * @param scope + * @since 3.1 + */ + public void endVisit(SingleMemberAnnotation annotation, BlockScope scope) { + // do nothing by default + } + /** + * @param annotation + * @param scope + * @since 3.1 + */ + public void endVisit(SingleMemberAnnotation annotation, CompilationUnitScope scope) { + // do nothing by default + } public void endVisit( SingleNameReference singleNameReference, BlockScope scope) { @@ -311,48 +393,37 @@ public abstract class ASTVisitor { CompilationUnitScope scope) { // do nothing by default } + public void endVisit(TypeParameter typeParameter, BlockScope scope) { + // do nothing by default + } + public void endVisit(TypeParameter typeParameter, ClassScope scope) { + // do nothing by default + } public void endVisit(UnaryExpression unaryExpression, BlockScope scope) { // do nothing by default } public void endVisit(WhileStatement whileStatement, BlockScope scope) { // do nothing by default } + public void endVisit(Wildcard wildcard, BlockScope scope) { + // do nothing by default + } + public void endVisit(Wildcard wildcard, ClassScope scope) { + // do nothing by default + } public boolean visit( AllocationExpression allocationExpression, BlockScope scope) { return true; // do nothing by default, keep traversing - // do nothing by default } public boolean visit(AND_AND_Expression and_and_Expression, BlockScope scope) { return true; // do nothing by default, keep traversing } - public boolean visit(JavadocArrayQualifiedTypeReference typeRef, BlockScope scope) { - return true; // do nothing by default, keep traversing - } - public boolean visit(JavadocArraySingleTypeReference typeRef, BlockScope scope) { - return true; // do nothing by default, keep traversing - } - public boolean visit(JavadocArgumentExpression expression, BlockScope scope) { - return true; // do nothing by default, keep traversing - } - public boolean visit(JavadocFieldReference fieldRef, BlockScope scope) { - return true; // do nothing by default, keep traversing - } - public boolean visit(JavadocMessageSend messageSend, BlockScope scope) { - return true; // do nothing by default, keep traversing - } - public boolean visit(JavadocQualifiedTypeReference typeRef, BlockScope scope) { - return true; // do nothing by default, keep traversing - } - public boolean visit(JavadocReturnStatement statement, BlockScope scope) { - return true; // do nothing by default, keep traversing - } - public boolean visit(JavadocSingleNameReference argument, BlockScope scope) { - return true; // do nothing by default, keep traversing - } - public boolean visit(JavadocSingleTypeReference typeRef, BlockScope scope) { + public boolean visit( + AnnotationMethodDeclaration annotationTypeDeclaration, + ClassScope classScope) { return true; // do nothing by default, keep traversing - } + } public boolean visit(Argument argument, BlockScope scope) { return true; // do nothing by default, keep traversing } @@ -383,10 +454,10 @@ public abstract class ASTVisitor { public boolean visit(ArrayTypeReference arrayTypeReference, ClassScope scope) { return true; // do nothing by default, keep traversing } - public boolean visit(Assignment assignment, BlockScope scope) { + public boolean visit(AssertStatement assertStatement, BlockScope scope) { return true; // do nothing by default, keep traversing } - public boolean visit(AssertStatement assertStatement, BlockScope scope) { + public boolean visit(Assignment assignment, BlockScope scope) { return true; // do nothing by default, keep traversing } public boolean visit(BinaryExpression binaryExpression, BlockScope scope) { @@ -440,10 +511,10 @@ public abstract class ASTVisitor { public boolean visit(DoubleLiteral doubleLiteral, BlockScope scope) { return true; // do nothing by default, keep traversing } - public boolean visit(EqualExpression equalExpression, BlockScope scope) { + public boolean visit(EmptyStatement emptyStatement, BlockScope scope) { return true; // do nothing by default, keep traversing } - public boolean visit(EmptyStatement emptyStatement, BlockScope scope) { + public boolean visit(EqualExpression equalExpression, BlockScope scope) { return true; // do nothing by default, keep traversing } public boolean visit( @@ -468,6 +539,9 @@ public abstract class ASTVisitor { public boolean visit(FloatLiteral floatLiteral, BlockScope scope) { return true; // do nothing by default, keep traversing } + public boolean visit(ForeachStatement forStatement, BlockScope scope) { + return true; // do nothing by default, keep traversing + } public boolean visit(ForStatement forStatement, BlockScope scope) { return true; // do nothing by default, keep traversing } @@ -488,6 +562,33 @@ public abstract class ASTVisitor { public boolean visit(IntLiteral intLiteral, BlockScope scope) { return true; // do nothing by default, keep traversing } + public boolean visit(JavadocArgumentExpression expression, BlockScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(JavadocArrayQualifiedTypeReference typeRef, BlockScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(JavadocArraySingleTypeReference typeRef, BlockScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(JavadocFieldReference fieldRef, BlockScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(JavadocMessageSend messageSend, BlockScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(JavadocQualifiedTypeReference typeRef, BlockScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(JavadocReturnStatement statement, BlockScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(JavadocSingleNameReference argument, BlockScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(JavadocSingleTypeReference typeRef, BlockScope scope) { + return true; // do nothing by default, keep traversing + } public boolean visit(LabeledStatement labeledStatement, BlockScope scope) { return true; // do nothing by default, keep traversing } @@ -497,6 +598,38 @@ public abstract class ASTVisitor { public boolean visit(LongLiteral longLiteral, BlockScope scope) { return true; // do nothing by default, keep traversing } + /** + * @param annotation + * @param scope + * @since 3.1 + */ + public boolean visit(MarkerAnnotation annotation, BlockScope scope) { + return true; + } + /** + * @param annotation + * @param scope + * @since 3.1 + */ + public boolean visit(MarkerAnnotation annotation, CompilationUnitScope scope) { + return true; + } + /** + * @param pair + * @param scope + * @since 3.1 + */ + public boolean visit(MemberValuePair pair, BlockScope scope) { + return true; + } + /** + * @param pair + * @param scope + * @since 3.1 + */ + public boolean visit(MemberValuePair pair, CompilationUnitScope scope) { + return true; + } public boolean visit(MessageSend messageSend, BlockScope scope) { return true; // do nothing by default, keep traversing } @@ -508,12 +641,40 @@ public abstract class ASTVisitor { BlockScope scope) { return true; // do nothing by default, keep traversing } + /** + * @param annotation + * @param scope + * @since 3.1 + */ + public boolean visit(NormalAnnotation annotation, BlockScope scope) { + return true; + } + /** + * @param annotation + * @param scope + * @since 3.1 + */ + public boolean visit(NormalAnnotation annotation, CompilationUnitScope scope) { + return true; + } public boolean visit(NullLiteral nullLiteral, BlockScope scope) { return true; // do nothing by default, keep traversing } public boolean visit(OR_OR_Expression or_or_Expression, BlockScope scope) { return true; // do nothing by default, keep traversing } + public boolean visit(ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference, BlockScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference, ClassScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(ParameterizedSingleTypeReference parameterizedSingleTypeReference, BlockScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(ParameterizedSingleTypeReference parameterizedSingleTypeReference, ClassScope scope) { + return true; // do nothing by default, keep traversing + } public boolean visit(PostfixExpression postfixExpression, BlockScope scope) { return true; // do nothing by default, keep traversing } @@ -553,6 +714,22 @@ public abstract class ASTVisitor { public boolean visit(ReturnStatement returnStatement, BlockScope scope) { return true; // do nothing by default, keep traversing } + /** + * @param annotation + * @param scope + * @since 3.1 + */ + public boolean visit(SingleMemberAnnotation annotation, BlockScope scope) { + return true; + } + /** + * @param annotation + * @param scope + * @since 3.1 + */ + public boolean visit(SingleMemberAnnotation annotation, CompilationUnitScope scope) { + return true; + } public boolean visit( SingleNameReference singleNameReference, BlockScope scope) { @@ -609,10 +786,22 @@ public abstract class ASTVisitor { CompilationUnitScope scope) { return true; // do nothing by default, keep traversing } + public boolean visit(TypeParameter typeParameter, BlockScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(TypeParameter typeParameter, ClassScope scope) { + return true; // do nothing by default, keep traversing + } public boolean visit(UnaryExpression unaryExpression, BlockScope scope) { return true; // do nothing by default, keep traversing } public boolean visit(WhileStatement whileStatement, BlockScope scope) { return true; // do nothing by default, keep traversing } + public boolean visit(Wildcard wildcard, BlockScope scope) { + return true; // do nothing by default, keep traversing + } + public boolean visit(Wildcard wildcard, ClassScope scope) { + return true; // do nothing by default, keep traversing + } } diff --git a/src/org/eclipse/jdt/internal/compiler/ClassFile.java b/src/org/eclipse/jdt/internal/compiler/ClassFile.java index e575af5..dbed323 100644 --- a/src/org/eclipse/jdt/internal/compiler/ClassFile.java +++ b/src/org/eclipse/jdt/internal/compiler/ClassFile.java @@ -20,6 +20,7 @@ import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.*; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.impl.StringConstant; import org.eclipse.jdt.internal.compiler.lookup.*; import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; @@ -45,29 +46,285 @@ import org.eclipse.jdt.internal.compiler.util.Util; */ public class ClassFile implements AttributeNamesConstants, CompilerModifiers, TypeConstants, TypeIds { - public SourceTypeBinding referenceBinding; - public ConstantPool constantPool; - public ClassFile enclosingClassFile; - // used to generate private access methods - public int produceDebugAttributes; - public ReferenceBinding[] innerClassesBindings; - public int numberOfInnerClasses; - public byte[] header; + public static final int INITIAL_CONTENTS_SIZE = 400; + public static final int INITIAL_HEADER_SIZE = 1500; + public static final int INNER_CLASSES_SIZE = 5; + + /** + * INTERNAL USE-ONLY + * Build all the directories and subdirectories corresponding to the packages names + * into the directory specified in parameters. + * + * outputPath is formed like: + * c:\temp\ the last character is a file separator + * relativeFileName is formed like: + * java\lang\String.class * + * + * @param outputPath java.lang.String + * @param relativeFileName java.lang.String + * @return java.lang.String + */ + public static String buildAllDirectoriesInto( + String outputPath, + String relativeFileName) + throws IOException { + char fileSeparatorChar = File.separatorChar; + String fileSeparator = File.separator; + File f; + // First we ensure that the outputPath exists + outputPath = outputPath.replace('/', fileSeparatorChar); + // To be able to pass the mkdirs() method we need to remove the extra file separator at the end of the outDir name + if (outputPath.endsWith(fileSeparator)) { + outputPath = outputPath.substring(0, outputPath.length() - 1); + } + f = new File(outputPath); + if (f.exists()) { + if (!f.isDirectory()) { + System.out.println(Util.bind("output.isFile" , f.getAbsolutePath())); //$NON-NLS-1$ + throw new IOException(Util.bind("output.isFileNotDirectory" )); //$NON-NLS-1$ + } + } else { + // we have to create that directory + if (!f.mkdirs()) { + System.out.println(Util.bind("output.dirName" , f.getAbsolutePath())); //$NON-NLS-1$ + throw new IOException(Util.bind("output.notValidAll" )); //$NON-NLS-1$ + } + } + StringBuffer outDir = new StringBuffer(outputPath); + outDir.append(fileSeparator); + StringTokenizer tokenizer = + new StringTokenizer(relativeFileName, fileSeparator); + String token = tokenizer.nextToken(); + while (tokenizer.hasMoreTokens()) { + f = new File(outDir.append(token).append(fileSeparator).toString()); + if (f.exists()) { + // The outDir already exists, so we proceed the next entry + // System.out.println("outDir: " + outDir + " already exists."); + } else { + // Need to add the outDir + if (!f.mkdir()) { + System.out.println(Util.bind("output.fileName" , f.getName())); //$NON-NLS-1$ + throw new IOException(Util.bind("output.notValid" )); //$NON-NLS-1$ + } + } + token = tokenizer.nextToken(); + } + // token contains the last one + return outDir.append(token).toString(); + } + + /** + * 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) { + SourceTypeBinding typeBinding = typeDeclaration.binding; + ClassFile classFile = new ClassFile(typeBinding, null, true); + + // TODO (olivier) handle cases where a field cannot be generated (name too long) + // TODO (olivier) handle too many methods + // inner attributes + if (typeBinding.isMemberType()) + classFile.recordEnclosingTypeAttributes(typeBinding); + + // add its fields + FieldBinding[] fields = typeBinding.fields; + if ((fields != null) && (fields != NoFields)) { + for (int i = 0, max = fields.length; i < max; i++) { + if (fields[i].constant() == null) { + FieldReference.getConstantFor(fields[i], null, false, null); + } + } + classFile.addFieldInfos(); + } else { + // we have to set the number of fields to be equals to 0 + classFile.contents[classFile.contentsOffset++] = 0; + classFile.contents[classFile.contentsOffset++] = 0; + } + // leave some space for the methodCount + classFile.setForMethodInfos(); + // add its user defined methods + MethodBinding[] methods = typeBinding.methods; + AbstractMethodDeclaration[] methodDeclarations = typeDeclaration.methods; + int maxMethodDecl = methodDeclarations == null ? 0 : methodDeclarations.length; + int problemsLength; + IProblem[] problems = unitResult.getErrors(); + if (problems == null) { + problems = new IProblem[0]; + } + IProblem[] problemsCopy = new IProblem[problemsLength = problems.length]; + System.arraycopy(problems, 0, problemsCopy, 0, problemsLength); + if (methods != null) { + if (typeBinding.isInterface()) { + // we cannot create problem methods for an interface. So we have to generate a clinit + // which should contain all the problem + classFile.addProblemClinit(problemsCopy); + for (int i = 0, max = methods.length; i < max; i++) { + MethodBinding methodBinding; + if ((methodBinding = methods[i]) != null) { + // find the corresponding method declaration + for (int j = 0; j < maxMethodDecl; j++) { + if ((methodDeclarations[j] != null) + && (methodDeclarations[j].binding == methods[i])) { + if (!methodBinding.isConstructor()) { + classFile.addAbstractMethod(methodDeclarations[j], methodBinding); + } + break; + } + } + } + } + } else { + for (int i = 0, max = methods.length; i < max; i++) { + MethodBinding methodBinding; + if ((methodBinding = methods[i]) != null) { + // find the corresponding method declaration + for (int j = 0; j < maxMethodDecl; j++) { + if ((methodDeclarations[j] != null) + && (methodDeclarations[j].binding == methods[i])) { + AbstractMethodDeclaration methodDecl; + if ((methodDecl = methodDeclarations[j]).isConstructor()) { + classFile.addProblemConstructor(methodDecl, methodBinding, problemsCopy); + } else { + classFile.addProblemMethod(methodDecl, methodBinding, problemsCopy); + } + break; + } + } + } + } + } + // 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.recordNestedMemberAttribute(memberType.binding); + ClassFile.createProblemType(memberType, unitResult); + } + } + } + classFile.addAttributes(); + unitResult.record(typeBinding.constantPoolName(), classFile); + } + + /** + * INTERNAL USE-ONLY + * Search the line number corresponding to a specific position + */ + public static final int searchLineNumber( + int[] startLineIndexes, + int position) { + // this code is completely useless, but it is the same implementation than + // org.eclipse.jdt.internal.compiler.problem.ProblemHandler.searchLineNumber(int[], int) + // if (startLineIndexes == null) + // return 1; + int length = startLineIndexes.length; + if (length == 0) + return 1; + int g = 0, d = length - 1; + int m = 0; + while (g <= d) { + m = (g + d) / 2; + if (position < startLineIndexes[m]) { + d = m - 1; + } else + if (position > startLineIndexes[m]) { + g = m + 1; + } else { + return m + 1; + } + } + if (position < startLineIndexes[m]) { + return m + 1; + } + return m + 2; + } + + /** + * INTERNAL USE-ONLY + * outputPath is formed like: + * c:\temp\ the last character is a file separator + * relativeFileName is formed like: + * java\lang\String.class + * @param generatePackagesStructure a flag to know if the packages structure has to be generated. + * @param outputPath the output directory + * @param relativeFileName java.lang.String + * @param contents byte[] + * + */ + public static void writeToDisk( + boolean generatePackagesStructure, + String outputPath, + String relativeFileName, + byte[] contents) + throws IOException { + + BufferedOutputStream output = null; + if (generatePackagesStructure) { + output = new BufferedOutputStream( + new FileOutputStream( + new File(buildAllDirectoriesInto(outputPath, relativeFileName)))); + } else { + String fileName = null; + char fileSeparatorChar = File.separatorChar; + String fileSeparator = File.separator; + // First we ensure that the outputPath exists + outputPath = outputPath.replace('/', fileSeparatorChar); + // To be able to pass the mkdirs() method we need to remove the extra file separator at the end of the outDir name + int indexOfPackageSeparator = relativeFileName.lastIndexOf(fileSeparatorChar); + if (indexOfPackageSeparator == -1) { + if (outputPath.endsWith(fileSeparator)) { + fileName = outputPath + relativeFileName; + } else { + fileName = outputPath + fileSeparator + relativeFileName; + } + } else { + int length = relativeFileName.length(); + if (outputPath.endsWith(fileSeparator)) { + fileName = outputPath + relativeFileName.substring(indexOfPackageSeparator + 1, length); + } else { + fileName = outputPath + fileSeparator + relativeFileName.substring(indexOfPackageSeparator + 1, length); + } + } + output = new BufferedOutputStream( + new FileOutputStream( + new File(fileName))); + } + try { + output.write(contents); + } finally { + output.flush(); + output.close(); + } + } + 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 int contentsOffset; - public int constantPoolOffset; - public int methodCountOffset; + public ReferenceBinding[] innerClassesBindings; public int methodCount; - protected boolean creatingProblemType; - public static final int INITIAL_CONTENTS_SIZE = 400; - public static final int INITIAL_HEADER_SIZE = 1500; + public int methodCountOffset; + public int numberOfInnerClasses; public boolean ownSharedArrays = false; // flag set when header/contents are set to shared arrays - public static final int INNER_CLASSES_SIZE = 5; - public CodeStream codeStream; - protected int problemLine; // used to create line number attributes for problem methods + // used to generate private access methods + public int produceDebugAttributes; + public SourceTypeBinding referenceBinding; public long targetJDK; /** @@ -91,7 +348,7 @@ public class ClassFile ClassFile enclosingClassFile, boolean creatingProblemType) { - referenceBinding = aType; + this.referenceBinding = aType; initByteArrays(); // generate the magic numbers inside the header @@ -100,7 +357,8 @@ public class ClassFile header[headerOffset++] = (byte) (0xCAFEBABEL >> 8); header[headerOffset++] = (byte) (0xCAFEBABEL >> 0); - this.targetJDK = referenceBinding.scope.environment().options.targetJDK; + final CompilerOptions options = aType.scope.environment().options; + this.targetJDK = options.targetJDK; header[headerOffset++] = (byte) (this.targetJDK >> 8); // minor high header[headerOffset++] = (byte) (this.targetJDK >> 0); // minor low header[headerOffset++] = (byte) (this.targetJDK >> 24); // major high @@ -139,15 +397,15 @@ public class ClassFile // now we continue to generate the bytes inside the contents array contents[contentsOffset++] = (byte) (accessFlags >> 8); contents[contentsOffset++] = (byte) accessFlags; - int classNameIndex = constantPool.literalIndex(aType); + int classNameIndex = constantPool.literalIndexForType(aType.constantPoolName()); contents[contentsOffset++] = (byte) (classNameIndex >> 8); contents[contentsOffset++] = (byte) classNameIndex; int superclassNameIndex; if (aType.isInterface()) { - superclassNameIndex = constantPool.literalIndexForJavaLangObject(); + superclassNameIndex = constantPool.literalIndexForType(ConstantPool.JavaLangObjectConstantPoolName); } else { superclassNameIndex = - (aType.superclass == null ? 0 : constantPool.literalIndex(aType.superclass)); + (aType.superclass == null ? 0 : constantPool.literalIndexForType(aType.superclass.constantPoolName())); } contents[contentsOffset++] = (byte) (superclassNameIndex >> 8); contents[contentsOffset++] = (byte) superclassNameIndex; @@ -156,14 +414,14 @@ public class ClassFile contents[contentsOffset++] = (byte) (interfacesCount >> 8); contents[contentsOffset++] = (byte) interfacesCount; for (int i = 0; i < interfacesCount; i++) { - int interfaceIndex = constantPool.literalIndex(superInterfacesBinding[i]); + int interfaceIndex = constantPool.literalIndexForType(superInterfacesBinding[i].constantPoolName()); contents[contentsOffset++] = (byte) (interfaceIndex >> 8); contents[contentsOffset++] = (byte) interfaceIndex; } - produceDebugAttributes = referenceBinding.scope.environment().options.produceDebugAttributes; + produceDebugAttributes = options.produceDebugAttributes; innerClassesBindings = new ReferenceBinding[INNER_CLASSES_SIZE]; this.creatingProblemType = creatingProblemType; - codeStream = new CodeStream(this); + codeStream = new CodeStream(this, this.targetJDK); // retrieve the enclosing one guaranteed to be the one matching the propagated flow info // 1FF9ZBU: LFCOM:ALL - Local variable attributes busted (Sanity check) @@ -284,14 +542,14 @@ public class ClassFile for (int i = 0; i < numberOfInnerClasses; i++) { ReferenceBinding innerClass = innerClassesBindings[i]; int accessFlags = innerClass.getAccessFlags(); - int innerClassIndex = constantPool.literalIndex(innerClass); + int innerClassIndex = constantPool.literalIndexForType(innerClass.constantPoolName()); // inner class index contents[contentsOffset++] = (byte) (innerClassIndex >> 8); contents[contentsOffset++] = (byte) innerClassIndex; // outer class index: anonymous and local have no outer class index if (innerClass.isMemberType()) { // member or member of local - int outerClassIndex = constantPool.literalIndex(innerClass.enclosingType()); + int outerClassIndex = constantPool.literalIndexForType(innerClass.enclosingType().constantPoolName()); contents[contentsOffset++] = (byte) (outerClassIndex >> 8); contents[contentsOffset++] = (byte) outerClassIndex; } else { @@ -312,15 +570,86 @@ public class ClassFile // access flag if (innerClass.isAnonymousType()) { accessFlags |= AccPrivate; - } else - if (innerClass.isLocalType() && !innerClass.isMemberType()) { - accessFlags |= AccPrivate; - } + } else if (innerClass.isLocalType() && !innerClass.isMemberType()) { + accessFlags |= AccPrivate; + } else if (innerClass.isMemberType() && (innerClass.isInterface() || innerClass.isAnnotationType())) { + accessFlags |= AccStatic; // implicitely static + } contents[contentsOffset++] = (byte) (accessFlags >> 8); contents[contentsOffset++] = (byte) accessFlags; } attributeNumber++; } + // add signature attribute + char[] genericSignature = referenceBinding.genericSignature(); + if (genericSignature != null) { + // check that there is enough space to write all the bytes for the field info corresponding + // to the @fieldBinding + if (contentsOffset + 8 >= contents.length) { + resizeContents(8); + } + int signatureAttributeNameIndex = + constantPool.literalIndex(AttributeNamesConstants.SignatureName); + contents[contentsOffset++] = (byte) (signatureAttributeNameIndex >> 8); + contents[contentsOffset++] = (byte) signatureAttributeNameIndex; + // the length of a signature attribute is equals to 2 + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 2; + int signatureIndex = + constantPool.literalIndex(genericSignature); + contents[contentsOffset++] = (byte) (signatureIndex >> 8); + contents[contentsOffset++] = (byte) signatureIndex; + attributeNumber++; + } + if (targetJDK >= ClassFileConstants.JDK1_5 + && (this.referenceBinding.isAnonymousType() || this.referenceBinding.isLocalType())) { + // add enclosing method attribute (1.5 mode only) + if (contentsOffset + 10 >= contents.length) { + resizeContents(10); + } + int enclosingMethodAttributeNameIndex = + constantPool.literalIndex(AttributeNamesConstants.EnclosingMethodName); + contents[contentsOffset++] = (byte) (enclosingMethodAttributeNameIndex >> 8); + contents[contentsOffset++] = (byte) enclosingMethodAttributeNameIndex; + // the length of a signature attribute is equals to 2 + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 4; + + int enclosingTypeIndex = constantPool.literalIndexForType(this.referenceBinding.enclosingType().constantPoolName()); + contents[contentsOffset++] = (byte) (enclosingTypeIndex >> 8); + contents[contentsOffset++] = (byte) enclosingTypeIndex; + byte methodIndexByte1 = 0; + byte methodIndexByte2 = 0; + if (this.referenceBinding.scope != null) { + MethodScope methodScope = this.referenceBinding.scope.methodScope(); + if (methodScope != null) { + ReferenceContext referenceContext = methodScope.referenceContext; + if (referenceContext instanceof AbstractMethodDeclaration) { + AbstractMethodDeclaration methodDeclaration = (AbstractMethodDeclaration) referenceContext; + MethodBinding methodBinding = methodDeclaration.binding; + int enclosingMethodIndex = constantPool.literalIndexForMethod(methodBinding.selector, methodBinding.signature()); + methodIndexByte1 = (byte) (enclosingMethodIndex >> 8); + methodIndexByte2 = (byte) enclosingMethodIndex; + } + } + } + contents[contentsOffset++] = methodIndexByte1; + contents[contentsOffset++] = methodIndexByte2; + attributeNumber++; + } + if (this.targetJDK >= ClassFileConstants.JDK1_5 && !this.creatingProblemType) { + TypeDeclaration typeDeclaration = referenceBinding.scope.referenceContext; + if (typeDeclaration != null) { + final Annotation[] annotations = typeDeclaration.annotations; + if (annotations != null) { + attributeNumber += generateRuntimeAnnotations(annotations); + } + } + } // update the number of attributes if (attributeOffset + 2 >= this.contents.length) { resizeContents(2); @@ -335,7 +664,7 @@ public class ClassFile header[constantPoolOffset++] = (byte) (constantPoolCount >> 8); header[constantPoolOffset] = (byte) constantPoolCount; } - + /** * INTERNAL USE-ONLY * This methods generate all the default abstract method infos that correpond to @@ -352,37 +681,14 @@ public class ClassFile } } - /** - * INTERNAL USE-ONLY - * This methods generates the bytes for the field binding passed like a parameter - * @param fieldBinding org.eclipse.jdt.internal.compiler.lookup.FieldBinding - */ - public void addFieldInfo(FieldBinding fieldBinding) { - int attributeNumber = 0; - // check that there is enough space to write all the bytes for the field info corresponding - // to the @fieldBinding - if (contentsOffset + 30 >= contents.length) { - resizeContents(30); - } - // Generate two attribute: constantValueAttribute and SyntheticAttribute - // Now we can generate all entries into the byte array - // First the accessFlags - int accessFlags = fieldBinding.getAccessFlags(); - contents[contentsOffset++] = (byte) (accessFlags >> 8); - contents[contentsOffset++] = (byte) accessFlags; - // Then the nameIndex - int nameIndex = constantPool.literalIndex(fieldBinding.name); - contents[contentsOffset++] = (byte) (nameIndex >> 8); - contents[contentsOffset++] = (byte) nameIndex; - // Then the descriptorIndex - int descriptorIndex = constantPool.literalIndex(fieldBinding.type.signature()); - contents[contentsOffset++] = (byte) (descriptorIndex >> 8); - contents[contentsOffset++] = (byte) descriptorIndex; - // leave some space for the number of attributes - int fieldAttributeOffset = contentsOffset; - contentsOffset += 2; + private int addFieldAttributes(FieldBinding fieldBinding, int fieldAttributeOffset) { + int attributesNumber = 0; // 4.7.2 only static constant fields get a ConstantAttribute - if (fieldBinding.constant != Constant.NotAConstant){ + // Generate the constantValueAttribute + if (fieldBinding.isConstantValue()){ + if (contentsOffset + 8 >= contents.length) { + resizeContents(8); + } // Now we generate the constant attribute corresponding to the fieldBinding int constantValueNameIndex = constantPool.literalIndex(AttributeNamesConstants.ConstantValueName); @@ -393,12 +699,13 @@ public class ClassFile contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; contents[contentsOffset++] = 2; - attributeNumber++; + attributesNumber++; // Need to add the constant_value_index - switch (fieldBinding.constant.typeID()) { + Constant fieldConstant = fieldBinding.constant(); + switch (fieldConstant.typeID()) { case T_boolean : int booleanValueIndex = - constantPool.literalIndex(fieldBinding.constant.booleanValue() ? 1 : 0); + constantPool.literalIndex(fieldConstant.booleanValue() ? 1 : 0); contents[contentsOffset++] = (byte) (booleanValueIndex >> 8); contents[contentsOffset++] = (byte) booleanValueIndex; break; @@ -407,32 +714,32 @@ public class ClassFile case T_int : case T_short : int integerValueIndex = - constantPool.literalIndex(fieldBinding.constant.intValue()); + constantPool.literalIndex(fieldConstant.intValue()); contents[contentsOffset++] = (byte) (integerValueIndex >> 8); contents[contentsOffset++] = (byte) integerValueIndex; break; case T_float : int floatValueIndex = - constantPool.literalIndex(fieldBinding.constant.floatValue()); + constantPool.literalIndex(fieldConstant.floatValue()); contents[contentsOffset++] = (byte) (floatValueIndex >> 8); contents[contentsOffset++] = (byte) floatValueIndex; break; case T_double : int doubleValueIndex = - constantPool.literalIndex(fieldBinding.constant.doubleValue()); + constantPool.literalIndex(fieldConstant.doubleValue()); contents[contentsOffset++] = (byte) (doubleValueIndex >> 8); contents[contentsOffset++] = (byte) doubleValueIndex; break; case T_long : int longValueIndex = - constantPool.literalIndex(fieldBinding.constant.longValue()); + constantPool.literalIndex(fieldConstant.longValue()); contents[contentsOffset++] = (byte) (longValueIndex >> 8); contents[contentsOffset++] = (byte) longValueIndex; break; - case T_String : + case T_JavaLangString : int stringValueIndex = constantPool.literalIndex( - ((StringConstant) fieldBinding.constant).stringValue()); + ((StringConstant) fieldConstant).stringValue()); if (stringValueIndex == -1) { if (!creatingProblemType) { // report an error and abort: will lead to a problem type classfile creation @@ -447,9 +754,7 @@ public class ClassFile } } else { // already inside a problem type creation : no constant for this field - contentsOffset = fieldAttributeOffset + 2; - // +2 is necessary to keep the two byte space for the attribute number - attributeNumber--; + contentsOffset = fieldAttributeOffset; } } else { contents[contentsOffset++] = (byte) (stringValueIndex >> 8); @@ -457,7 +762,10 @@ public class ClassFile } } } - if (fieldBinding.isSynthetic()) { + if (this.targetJDK < ClassFileConstants.JDK1_5 && fieldBinding.isSynthetic()) { + if (contentsOffset + 6 >= contents.length) { + resizeContents(6); + } int syntheticAttributeNameIndex = constantPool.literalIndex(AttributeNamesConstants.SyntheticName); contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); @@ -467,9 +775,12 @@ public class ClassFile contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; - attributeNumber++; + attributesNumber++; } if (fieldBinding.isDeprecated()) { + if (contentsOffset + 6 >= contents.length) { + resizeContents(6); + } int deprecatedAttributeNameIndex = constantPool.literalIndex(AttributeNamesConstants.DeprecatedName); contents[contentsOffset++] = (byte) (deprecatedAttributeNameIndex >> 8); @@ -479,8 +790,75 @@ public class ClassFile contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; - attributeNumber++; + attributesNumber++; + } + // add signature attribute + char[] genericSignature = fieldBinding.genericSignature(); + if (genericSignature != null) { + // check that there is enough space to write all the bytes for the field info corresponding + // to the @fieldBinding + if (contentsOffset + 8 >= contents.length) { + resizeContents(8); + } + int signatureAttributeNameIndex = + constantPool.literalIndex(AttributeNamesConstants.SignatureName); + contents[contentsOffset++] = (byte) (signatureAttributeNameIndex >> 8); + contents[contentsOffset++] = (byte) signatureAttributeNameIndex; + // the length of a signature attribute is equals to 2 + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 2; + int signatureIndex = + constantPool.literalIndex(genericSignature); + contents[contentsOffset++] = (byte) (signatureIndex >> 8); + contents[contentsOffset++] = (byte) signatureIndex; + attributesNumber++; + } + if (this.targetJDK >= ClassFileConstants.JDK1_5 && !this.creatingProblemType) { + FieldDeclaration fieldDeclaration = fieldBinding.sourceField(); + if (fieldDeclaration != null) { + Annotation[] annotations = fieldDeclaration.annotations; + if (annotations != null) { + attributesNumber += generateRuntimeAnnotations(annotations); + } + } + } + return attributesNumber; + } + /** + * 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 (contentsOffset + 8 >= contents.length) { + resizeContents(8); } + // Now we can generate all entries into the byte array + // First the accessFlags + int accessFlags = fieldBinding.getAccessFlags(); + if (targetJDK < ClassFileConstants.JDK1_5) { + // pre 1.5, synthetic was an attribute, not a modifier + accessFlags &= ~AccSynthetic; + } + contents[contentsOffset++] = (byte) (accessFlags >> 8); + contents[contentsOffset++] = (byte) accessFlags; + // Then the nameIndex + int nameIndex = constantPool.literalIndex(fieldBinding.name); + contents[contentsOffset++] = (byte) (nameIndex >> 8); + contents[contentsOffset++] = (byte) nameIndex; + // Then the descriptorIndex + int descriptorIndex = constantPool.literalIndex(fieldBinding.type.signature()); + contents[contentsOffset++] = (byte) (descriptorIndex >> 8); + contents[contentsOffset++] = (byte) descriptorIndex; + int fieldAttributeOffset = contentsOffset; + int attributeNumber = 0; + // leave some space for the number of attributes + contentsOffset += 2; + attributeNumber += addFieldAttributes(fieldBinding, fieldAttributeOffset); contents[fieldAttributeOffset++] = (byte) (attributeNumber >> 8); contents[fieldAttributeOffset] = (byte) attributeNumber; } @@ -492,6 +870,13 @@ public class ClassFile * - 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 = referenceBinding; FieldBinding[] syntheticFields = currentBinding.syntheticFields(); @@ -523,7 +908,7 @@ public class ClassFile * have to be generated for the inner classes attributes. * @param refBinding org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding */ - public void addInnerClasses(ReferenceBinding refBinding) { + private void addInnerClasses(ReferenceBinding refBinding) { // check first if that reference binding is there for (int i = 0; i < numberOfInnerClasses; i++) { if (innerClassesBindings[i] == refBinding) @@ -540,6 +925,38 @@ public class ClassFile } innerClassesBindings[numberOfInnerClasses++] = refBinding; } + + private void addMissingAbstractProblemMethod(MethodDeclaration methodDeclaration, MethodBinding methodBinding, IProblem problem, CompilationResult compilationResult) { + // always clear the strictfp/native/abstract bit for a problem method + generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(AccStrictfp | AccNative | AccAbstract)); + int methodAttributeOffset = contentsOffset; + int attributeNumber = generateMethodInfoAttribute(methodBinding); + + // Code attribute + attributeNumber++; + + int codeAttributeOffset = contentsOffset; + generateCodeAttributeHeader(); + StringBuffer buffer = new StringBuffer(25); + buffer.append("\t" + problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$ + buffer.insert(0, Util.bind("compilation.unresolvedProblem" )); //$NON-NLS-1$ + String problemString = buffer.toString(); + + codeStream.init(this); + codeStream.preserveUnusedLocals = true; + codeStream.initializeMaxLocals(methodBinding); + + // return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "") + codeStream.generateCodeAttributeForProblemMethod(problemString); + + completeCodeAttributeForMissingAbstractProblemMethod( + methodBinding, + codeAttributeOffset, + compilationResult.lineSeparatorPositions, + problem.getSourceLineNumber()); + + completeMethodInfo(methodAttributeOffset, attributeNumber); + } /** * INTERNAL USE-ONLY @@ -559,6 +976,7 @@ public class ClassFile generateCodeAttributeHeader(); codeStream.resetForProblemClinit(this); String problemString = "" ; //$NON-NLS-1$ + int problemLine = 0; if (problems != null) { int max = problems.length; StringBuffer buffer = new StringBuffer(25); @@ -591,7 +1009,8 @@ public class ClassFile .scope .referenceCompilationUnit() .compilationResult - .lineSeparatorPositions); + .lineSeparatorPositions, + problemLine); contents[attributeOffset++] = (byte) (attributeNumber >> 8); contents[attributeOffset] = (byte) attributeNumber; } @@ -612,7 +1031,7 @@ public class ClassFile // always clear the strictfp/native/abstract bit for a problem method generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(AccStrictfp | AccNative | AccAbstract)); int methodAttributeOffset = contentsOffset; - int attributeNumber = generateMethodInfoAttribute(methodBinding); + int attributeNumber = generateMethodInfoAttribute(methodBinding, true); // Code attribute attributeNumber++; @@ -620,6 +1039,7 @@ public class ClassFile generateCodeAttributeHeader(); codeStream.reset(method, this); String problemString = "" ; //$NON-NLS-1$ + int problemLine = 0; if (problems != null) { int max = problems.length; StringBuffer buffer = new StringBuffer(25); @@ -652,7 +1072,8 @@ public class ClassFile .scope .referenceCompilationUnit() .compilationResult - .lineSeparatorPositions); + .lineSeparatorPositions, + problemLine); completeMethodInfo(methodAttributeOffset, attributeNumber); } @@ -695,7 +1116,7 @@ public class ClassFile // always clear the strictfp/native/abstract bit for a problem method generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(AccStrictfp | AccNative | AccAbstract)); int methodAttributeOffset = contentsOffset; - int attributeNumber = generateMethodInfoAttribute(methodBinding); + int attributeNumber = generateMethodInfoAttribute(methodBinding, true); // Code attribute attributeNumber++; @@ -704,6 +1125,7 @@ public class ClassFile generateCodeAttributeHeader(); codeStream.reset(method, this); String problemString = "" ; //$NON-NLS-1$ + int problemLine = 0; if (problems != null) { int max = problems.length; StringBuffer buffer = new StringBuffer(25); @@ -740,7 +1162,8 @@ public class ClassFile .scope .referenceCompilationUnit() .compilationResult - .lineSeparatorPositions); + .lineSeparatorPositions, + problemLine); completeMethodInfo(methodAttributeOffset, attributeNumber); } @@ -761,206 +1184,77 @@ public class ClassFile int savedOffset) { // we need to move back the contentsOffset to the value at the beginning of the method contentsOffset = savedOffset; - 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 - */ - public void addSpecialMethods() { - // add all methods (default abstract methods and synthetic) - - // default abstract methods - SourceTypeBinding currentBinding = referenceBinding; - MethodBinding[] defaultAbstractMethods = - currentBinding.getDefaultAbstractMethods(); - for (int i = 0, max = defaultAbstractMethods.length; i < max; i++) { - generateMethodInfoHeader(defaultAbstractMethods[i]); - int methodAttributeOffset = contentsOffset; - int attributeNumber = generateMethodInfoAttribute(defaultAbstractMethods[i]); - completeMethodInfo(methodAttributeOffset, attributeNumber); - } - // add synthetic methods infos - SyntheticAccessMethodBinding[] syntheticAccessMethods = - currentBinding.syntheticAccessMethods(); - if (syntheticAccessMethods != null) { - for (int i = 0, max = syntheticAccessMethods.length; i < max; i++) { - SyntheticAccessMethodBinding accessMethodBinding = syntheticAccessMethods[i]; - switch (accessMethodBinding.accessType) { - case SyntheticAccessMethodBinding.FieldReadAccess : - // generate a method info to emulate an reading access to - // a non-accessible field - addSyntheticFieldReadAccessMethod(syntheticAccessMethods[i]); - break; - case SyntheticAccessMethodBinding.FieldWriteAccess : - // generate a method info to emulate an writing access to - // a non-accessible field - addSyntheticFieldWriteAccessMethod(syntheticAccessMethods[i]); - break; - case SyntheticAccessMethodBinding.MethodAccess : - case SyntheticAccessMethodBinding.SuperMethodAccess : - // generate a method info to emulate an access to a non-accessible method / super-method - addSyntheticMethodAccessMethod(syntheticAccessMethods[i]); - break; - case SyntheticAccessMethodBinding.ConstructorAccess : - // generate a method info to emulate an access to a non-accessible constructor - addSyntheticConstructorAccessMethod(syntheticAccessMethods[i]); - } - } - } - } - - /** - * 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) { - for (int i = 0, max = methodDeclarations.length; i < max; i++) { - MethodDeclaration methodDeclaration = methodDeclarations[i]; - MethodBinding methodBinding = methodDeclaration.binding; - String readableName = new String(methodBinding.readableName()); - IProblem[] problems = compilationResult.problems; - int problemsCount = compilationResult.problemCount; - for (int j = 0; j < problemsCount; j++) { - IProblem problem = problems[j]; - if (problem != null - && problem.getID() == IProblem.AbstractMethodMustBeImplemented - && problem.getMessage().indexOf(readableName) != -1) { - // we found a match - addMissingAbstractProblemMethod(methodDeclaration, methodBinding, problem, compilationResult); - } - } - } - } - } - - private void addMissingAbstractProblemMethod(MethodDeclaration methodDeclaration, MethodBinding methodBinding, IProblem problem, CompilationResult compilationResult) { - // always clear the strictfp/native/abstract bit for a problem method - generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(AccStrictfp | AccNative | AccAbstract)); - int methodAttributeOffset = contentsOffset; - int attributeNumber = generateMethodInfoAttribute(methodBinding); - - // Code attribute - attributeNumber++; - - int codeAttributeOffset = contentsOffset; - generateCodeAttributeHeader(); - StringBuffer buffer = new StringBuffer(25); - buffer.append("\t" + problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$ - buffer.insert(0, Util.bind("compilation.unresolvedProblem" )); //$NON-NLS-1$ - String problemString = buffer.toString(); - this.problemLine = problem.getSourceLineNumber(); - - codeStream.init(this); - codeStream.preserveUnusedLocals = true; - codeStream.initializeMaxLocals(methodBinding); - - // return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "") - codeStream.generateCodeAttributeForProblemMethod(problemString); - - completeCodeAttributeForMissingAbstractProblemMethod( - methodBinding, - codeAttributeOffset, - compilationResult.lineSeparatorPositions); - - completeMethodInfo(methodAttributeOffset, attributeNumber); + 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 */ - public void completeCodeAttributeForMissingAbstractProblemMethod( - MethodBinding binding, - int codeAttributeOffset, - int[] startLineIndexes) { - // reinitialize the localContents with the byte modified by the code stream - this.contents = codeStream.bCodeStream; - int localContentsOffset = 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 = codeStream.stackMax; - this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); - this.contents[codeAttributeOffset + 7] = (byte) max_stack; - int max_locals = codeStream.maxLocals; - this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); - this.contents[codeAttributeOffset + 9] = (byte) max_locals; - int code_length = 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 attributeNumber = 0; // leave two bytes for the attribute_length - localContentsOffset += 2; // first we handle the linenumber attribute + public void addSpecialMethods() { + + // add all methods (default abstract methods and synthetic) - if (codeStream.generateLineNumberAttributes) { - /* 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 = - 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; - if (problemLine == 0) { - problemLine = searchLineNumber(startLineIndexes, binding.sourceStart()); - } - // 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 - attributeNumber++; + // default abstract methods + generateMissingAbstractMethods(referenceBinding.scope.referenceType().missingAbstractMethods, referenceBinding.scope.referenceCompilationUnit().compilationResult); + + MethodBinding[] defaultAbstractMethods = this.referenceBinding.getDefaultAbstractMethods(); + for (int i = 0, max = defaultAbstractMethods.length; i < max; i++) { + generateMethodInfoHeader(defaultAbstractMethods[i]); + int methodAttributeOffset = contentsOffset; + int attributeNumber = generateMethodInfoAttribute(defaultAbstractMethods[i]); + completeMethodInfo(methodAttributeOffset, attributeNumber); } - - // 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); + // add synthetic methods infos + SyntheticMethodBinding[] syntheticMethods = this.referenceBinding.syntheticMethods(); + if (syntheticMethods != null) { + for (int i = 0, max = syntheticMethods.length; i < max; i++) { + SyntheticMethodBinding syntheticMethod = syntheticMethods[i]; + switch (syntheticMethod.kind) { + case SyntheticMethodBinding.FieldReadAccess : + // generate a method info to emulate an reading access to + // a non-accessible field + addSyntheticFieldReadAccessMethod(syntheticMethod); + break; + case SyntheticMethodBinding.FieldWriteAccess : + // 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; + } + } } - this.contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); - this.contents[codeAttributeAttributeOffset] = (byte) attributeNumber; - // update the attribute length - int codeAttributeLength = localContentsOffset - (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; - contentsOffset = localContentsOffset; } - + /** * INTERNAL USE-ONLY - * Generate the byte for a problem method info that correspond to a synthetic method that - * generate an access to a private constructor. + * 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(SyntheticAccessMethodBinding methodBinding) { + public void addSyntheticConstructorAccessMethod(SyntheticMethodBinding methodBinding) { generateMethodInfoHeader(methodBinding); // We know that we won't get more than 2 attribute: the code attribute + synthetic attribute contents[contentsOffset++] = 0; @@ -970,7 +1264,7 @@ public class ClassFile generateCodeAttributeHeader(); codeStream.init(this); codeStream.generateSyntheticBodyForConstructorAccess(methodBinding); - completeCodeAttributeForSyntheticAccessMethod( + completeCodeAttributeForSyntheticMethod( methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass) @@ -992,12 +1286,88 @@ public class ClassFile /** * 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); + // We know that we won't get more than 1 attribute: the code attribute + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 1; + // Code attribute + int codeAttributeOffset = contentsOffset; + generateCodeAttributeHeader(); + codeStream.init(this); + codeStream.generateSyntheticBodyForEnumValueOf(methodBinding); + completeCodeAttributeForSyntheticMethod( + 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 + * + * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding + */ + public void addSyntheticEnumValuesMethod(SyntheticMethodBinding methodBinding) { + + generateMethodInfoHeader(methodBinding); + // We know that we won't get more than 1 attribute: the code attribute + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 1; + // Code attribute + int codeAttributeOffset = contentsOffset; + generateCodeAttributeHeader(); + codeStream.init(this); + codeStream.generateSyntheticBodyForEnumValues(methodBinding); + completeCodeAttributeForSyntheticMethod( + 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 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(SyntheticAccessMethodBinding methodBinding) { + public void addSyntheticFieldReadAccessMethod(SyntheticMethodBinding methodBinding) { generateMethodInfoHeader(methodBinding); // We know that we won't get more than 2 attribute: the code attribute + synthetic attribute contents[contentsOffset++] = 0; @@ -1007,7 +1377,7 @@ public class ClassFile generateCodeAttributeHeader(); codeStream.init(this); codeStream.generateSyntheticBodyForFieldReadAccess(methodBinding); - completeCodeAttributeForSyntheticAccessMethod( + completeCodeAttributeForSyntheticMethod( methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass) @@ -1034,7 +1404,7 @@ public class ClassFile * * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding */ - public void addSyntheticFieldWriteAccessMethod(SyntheticAccessMethodBinding methodBinding) { + public void addSyntheticFieldWriteAccessMethod(SyntheticMethodBinding methodBinding) { generateMethodInfoHeader(methodBinding); // We know that we won't get more than 2 attribute: the code attribute + synthetic attribute contents[contentsOffset++] = 0; @@ -1044,7 +1414,7 @@ public class ClassFile generateCodeAttributeHeader(); codeStream.init(this); codeStream.generateSyntheticBodyForFieldWriteAccess(methodBinding); - completeCodeAttributeForSyntheticAccessMethod( + completeCodeAttributeForSyntheticMethod( methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass) @@ -1066,12 +1436,11 @@ public class ClassFile /** * INTERNAL USE-ONLY - * Generate the byte for a problem method info that correspond to a synthetic method that - * generate an access to a private method. + * 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(SyntheticAccessMethodBinding methodBinding) { + public void addSyntheticMethodAccessMethod(SyntheticMethodBinding methodBinding) { generateMethodInfoHeader(methodBinding); // We know that we won't get more than 2 attribute: the code attribute + synthetic attribute contents[contentsOffset++] = 0; @@ -1081,7 +1450,7 @@ public class ClassFile generateCodeAttributeHeader(); codeStream.init(this); codeStream.generateSyntheticBodyForMethodAccess(methodBinding); - completeCodeAttributeForSyntheticAccessMethod( + completeCodeAttributeForSyntheticMethod( methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass) @@ -1103,69 +1472,6 @@ public class ClassFile /** * INTERNAL USE-ONLY - * Build all the directories and subdirectories corresponding to the packages names - * into the directory specified in parameters. - * - * outputPath is formed like: - * c:\temp\ the last character is a file separator - * relativeFileName is formed like: - * java\lang\String.class * - * - * @param outputPath java.lang.String - * @param relativeFileName java.lang.String - * @return java.lang.String - */ - public static String buildAllDirectoriesInto( - String outputPath, - String relativeFileName) - throws IOException { - char fileSeparatorChar = File.separatorChar; - String fileSeparator = File.separator; - File f; - // First we ensure that the outputPath exists - outputPath = outputPath.replace('/', fileSeparatorChar); - // To be able to pass the mkdirs() method we need to remove the extra file separator at the end of the outDir name - if (outputPath.endsWith(fileSeparator)) { - outputPath = outputPath.substring(0, outputPath.length() - 1); - } - f = new File(outputPath); - if (f.exists()) { - if (!f.isDirectory()) { - System.out.println(Util.bind("output.isFile" , f.getAbsolutePath())); //$NON-NLS-1$ - throw new IOException(Util.bind("output.isFileNotDirectory" )); //$NON-NLS-1$ - } - } else { - // we have to create that directory - if (!f.mkdirs()) { - System.out.println(Util.bind("output.dirName" , f.getAbsolutePath())); //$NON-NLS-1$ - throw new IOException(Util.bind("output.notValidAll" )); //$NON-NLS-1$ - } - } - StringBuffer outDir = new StringBuffer(outputPath); - outDir.append(fileSeparator); - StringTokenizer tokenizer = - new StringTokenizer(relativeFileName, fileSeparator); - String token = tokenizer.nextToken(); - while (tokenizer.hasMoreTokens()) { - f = new File(outDir.append(token).append(fileSeparator).toString()); - if (f.exists()) { - // The outDir already exists, so we proceed the next entry - // System.out.println("outDir: " + outDir + " already exists."); - } else { - // Need to add the outDir - if (!f.mkdir()) { - System.out.println(Util.bind("output.fileName" , f.getName())); //$NON-NLS-1$ - throw new IOException(Util.bind("output.notValid" )); //$NON-NLS-1$ - } - } - token = tokenizer.nextToken(); - } - // token contains the last one - return outDir.append(token).toString(); - } - - /** - * INTERNAL USE-ONLY * That method completes the creation of the code attribute by setting * - the attribute_length * - max_stack @@ -1204,7 +1510,7 @@ public class ClassFile this.contents[codeAttributeOffset + 13] = (byte) code_length; // write the exception table - int exceptionHandlersNumber = codeStream.exceptionHandlersNumber; + int exceptionHandlersNumber = codeStream.exceptionHandlersCounter; ExceptionLabel[] exceptionHandlers = codeStream.exceptionHandlers; int exSize = exceptionHandlersNumber * 8 + 2; if (exSize + localContentsOffset >= this.contents.length) { @@ -1214,31 +1520,33 @@ public class ClassFile // on the attribute generation this.contents[localContentsOffset++] = (byte) (exceptionHandlersNumber >> 8); this.contents[localContentsOffset++] = (byte) exceptionHandlersNumber; - for (int i = 0; i < exceptionHandlersNumber; i++) { + for (int i = 0, max = codeStream.exceptionHandlersIndex; i < max; i++) { ExceptionLabel exceptionHandler = exceptionHandlers[i]; - 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; - if (exceptionHandler.exceptionType == BaseTypes.NullBinding) { - /* represents ClassNotFoundException, see class literal access*/ - nameIndex = constantPool.literalIndexForJavaLangClassNotFoundException(); + 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 { - nameIndex = constantPool.literalIndex(exceptionHandler.exceptionType); + int nameIndex; + if (exceptionHandler.exceptionType == BaseTypes.NullBinding) { + /* represents ClassNotFoundException, see class literal access*/ + nameIndex = constantPool.literalIndexForType(ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName); + } else { + nameIndex = constantPool.literalIndexForType(exceptionHandler.exceptionType.constantPoolName()); + } + this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); + this.contents[localContentsOffset++] = (byte) nameIndex; } - this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); - this.contents[localContentsOffset++] = (byte) nameIndex; } } // debug attributes @@ -1300,8 +1608,14 @@ public class ClassFile int numberOfEntries = 0; int localVariableNameIndex = constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName); - if (localContentsOffset + 8 >= this.contents.length) { - resizeContents(8); + final boolean methodDeclarationIsStatic = codeStream.methodDeclaration.isStatic(); + int maxOfEntries = 8 + 10 * (methodDeclarationIsStatic ? 0 : 1); + for (int i = 0; i < codeStream.allLocalsCounter; i++) { + maxOfEntries += 10 * codeStream.locals[i].initializationCount; + } + // reserve enough space + if (localContentsOffset + maxOfEntries >= this.contents.length) { + resizeContents(maxOfEntries); } this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); this.contents[localContentsOffset++] = (byte) localVariableNameIndex; @@ -1309,28 +1623,41 @@ public class ClassFile // leave space for attribute_length and local_variable_table_length int nameIndex; int descriptorIndex; - if (!codeStream.methodDeclaration.isStatic()) { + SourceTypeBinding declaringClassBinding = null; + if (!methodDeclarationIsStatic) { numberOfEntries++; - if (localContentsOffset + 10 >= this.contents.length) { - resizeContents(10); - } 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 = constantPool.literalIndex(QualifiedNamesConstants.This); + nameIndex = constantPool.literalIndex(ConstantPool.This); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; + declaringClassBinding = (SourceTypeBinding) codeStream.methodDeclaration.binding.declaringClass; descriptorIndex = constantPool.literalIndex( - codeStream.methodDeclaration.binding.declaringClass.signature()); + 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; } - for (int i = 0; i < codeStream.allLocalsCounter; i++) { + // used to remember the local variable with a generic type + int genericLocalVariablesCounter = 0; + LocalVariableBinding[] genericLocalVariables = null; + int numberOfGenericEntries = 0; + + for (int i = 0, max = codeStream.allLocalsCounter; i < max; i++) { LocalVariableBinding localVariable = codeStream.locals[i]; + final TypeBinding localVariableTypeBinding = localVariable.type; + boolean isParameterizedType = localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable(); + if (localVariable.initializationCount != 0 && isParameterizedType) { + if (genericLocalVariables == null) { + // we cannot have more than max locals + genericLocalVariables = new LocalVariableBinding[max]; + } + genericLocalVariables[genericLocalVariablesCounter++] = localVariable; + } for (int j = 0; j < localVariable.initializationCount; j++) { int startPC = localVariable.initializationPCs[j << 1]; int endPC = localVariable.initializationPCs[(j << 1) + 1]; @@ -1340,8 +1667,8 @@ public class ClassFile Util.bind("abort.invalidAttribute" , new String(localVariable.name)), //$NON-NLS-1$ (ASTNode) localVariable.declaringScope.methodScope().referenceContext); } - if (localContentsOffset + 10 >= this.contents.length) { - resizeContents(10); + if (isParameterizedType) { + numberOfGenericEntries++; } // now we can safely add the local entry numberOfEntries++; @@ -1353,7 +1680,7 @@ public class ClassFile nameIndex = constantPool.literalIndex(localVariable.name); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; - descriptorIndex = constantPool.literalIndex(localVariable.type.signature()); + descriptorIndex = constantPool.literalIndex(localVariableTypeBinding.signature()); this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); this.contents[localContentsOffset++] = (byte) descriptorIndex; int resolvedPosition = localVariable.resolvedPosition; @@ -1371,6 +1698,72 @@ public class ClassFile this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); this.contents[localVariableTableOffset] = (byte) numberOfEntries; attributeNumber++; + + final boolean currentInstanceIsGeneric = + !methodDeclarationIsStatic + && declaringClassBinding != null + && declaringClassBinding.typeVariables != NoTypeVariables; + 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 = + 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 = constantPool.literalIndex(ConstantPool.This); + this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); + this.contents[localContentsOffset++] = (byte) nameIndex; + descriptorIndex = 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 = constantPool.literalIndex(localVariable.name); + this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); + this.contents[localContentsOffset++] = (byte) nameIndex; + descriptorIndex = 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; + } + } + } + attributeNumber++; + } } // update the number of attributes // ensure first that there is enough space available inside the localContents array @@ -1429,7 +1822,7 @@ public class ClassFile this.contents[codeAttributeOffset + 13] = (byte) code_length; // write the exception table - int exceptionHandlersNumber = codeStream.exceptionHandlersNumber; + int exceptionHandlersNumber = codeStream.exceptionHandlersCounter; ExceptionLabel[] exceptionHandlers = codeStream.exceptionHandlers; int exSize = exceptionHandlersNumber * 8 + 2; if (exSize + localContentsOffset >= this.contents.length) { @@ -1439,31 +1832,33 @@ public class ClassFile // on the attribute generation this.contents[localContentsOffset++] = (byte) (exceptionHandlersNumber >> 8); this.contents[localContentsOffset++] = (byte) exceptionHandlersNumber; - for (int i = 0; i < exceptionHandlersNumber; i++) { + for (int i = 0, max = codeStream.exceptionHandlersIndex; i < max; i++) { ExceptionLabel exceptionHandler = exceptionHandlers[i]; - 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; - if (exceptionHandler.exceptionType == BaseTypes.NullBinding) { - /* represents denote ClassNotFoundException, see class literal access*/ - nameIndex = constantPool.literalIndexForJavaLangClassNotFoundException(); + 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 { - nameIndex = constantPool.literalIndex(exceptionHandler.exceptionType); + int nameIndex; + if (exceptionHandler.exceptionType == BaseTypes.NullBinding) { + /* represents denote ClassNotFoundException, see class literal access*/ + nameIndex = constantPool.literalIndexForType(ConstantPool.JavaLangClassNotFoundExceptionConstantPoolName); + } else { + nameIndex = constantPool.literalIndexForType(exceptionHandler.exceptionType.constantPoolName()); + } + this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); + this.contents[localContentsOffset++] = (byte) nameIndex; } - this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); - this.contents[localContentsOffset++] = (byte) nameIndex; } } // debug attributes @@ -1534,11 +1929,27 @@ public class ClassFile this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); this.contents[localContentsOffset++] = (byte) localVariableNameIndex; localContentsOffset += 6; + // leave space for attribute_length and local_variable_table_length int nameIndex; int descriptorIndex; - for (int i = 0; i < codeStream.allLocalsCounter; i++) { + + // used to remember the local variable with a generic type + int genericLocalVariablesCounter = 0; + LocalVariableBinding[] genericLocalVariables = null; + int numberOfGenericEntries = 0; + + for (int i = 0, max = codeStream.allLocalsCounter; i < max; i++) { LocalVariableBinding localVariable = codeStream.locals[i]; + final TypeBinding localVariableTypeBinding = localVariable.type; + boolean isParameterizedType = localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable(); + if (localVariable.initializationCount != 0 && isParameterizedType) { + if (genericLocalVariables == null) { + // we cannot have more than max locals + genericLocalVariables = new LocalVariableBinding[max]; + } + genericLocalVariables[genericLocalVariablesCounter++] = localVariable; + } for (int j = 0; j < localVariable.initializationCount; j++) { int startPC = localVariable.initializationPCs[j << 1]; int endPC = localVariable.initializationPCs[(j << 1) + 1]; @@ -1553,6 +1964,9 @@ public class ClassFile } // now we can safely add the local entry numberOfEntries++; + if (isParameterizedType) { + numberOfGenericEntries++; + } this.contents[localContentsOffset++] = (byte) (startPC >> 8); this.contents[localContentsOffset++] = (byte) startPC; int length = endPC - startPC; @@ -1561,7 +1975,7 @@ public class ClassFile nameIndex = constantPool.literalIndex(localVariable.name); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; - descriptorIndex = constantPool.literalIndex(localVariable.type.signature()); + descriptorIndex = constantPool.literalIndex(localVariableTypeBinding.signature()); this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); this.contents[localContentsOffset++] = (byte) descriptorIndex; int resolvedPosition = localVariable.resolvedPosition; @@ -1579,6 +1993,52 @@ public class ClassFile this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); this.contents[localVariableTableOffset] = (byte) numberOfEntries; attributeNumber++; + + if (genericLocalVariablesCounter != 0) { + // add the local variable type table attribute + // reserve enough space + int maxOfEntries = 8 + numberOfGenericEntries * 10; + + if (localContentsOffset + maxOfEntries >= this.contents.length) { + resizeContents(maxOfEntries); + } + int localVariableTypeNameIndex = + 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; + 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 = constantPool.literalIndex(localVariable.name); + this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); + this.contents[localContentsOffset++] = (byte) nameIndex; + descriptorIndex = 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; + } + } + } + attributeNumber++; + } } } // update the number of attributes @@ -1612,7 +2072,8 @@ public class ClassFile */ public void completeCodeAttributeForClinit( int codeAttributeOffset, - int[] startLineIndexes) { + int[] startLineIndexes, + int problemLine) { // reinitialize the contents with the byte modified by the code stream this.contents = codeStream.bCodeStream; int localContentsOffset = codeStream.classFileOffset; @@ -1711,6 +2172,85 @@ public class ClassFile } /** + * + */ + public void completeCodeAttributeForMissingAbstractProblemMethod( + MethodBinding binding, + int codeAttributeOffset, + int[] startLineIndexes, + int problemLine) { + // reinitialize the localContents with the byte modified by the code stream + this.contents = codeStream.bCodeStream; + int localContentsOffset = 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 = codeStream.stackMax; + this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); + this.contents[codeAttributeOffset + 7] = (byte) max_stack; + int max_locals = codeStream.maxLocals; + this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); + this.contents[codeAttributeOffset + 9] = (byte) max_locals; + int code_length = 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 attributeNumber = 0; // leave two bytes for the attribute_length + localContentsOffset += 2; // first we handle the linenumber attribute + + if (codeStream.generateLineNumberAttributes) { + /* 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 = + 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; + if (problemLine == 0) { + problemLine = searchLineNumber(startLineIndexes, binding.sourceStart()); + } + // 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 + attributeNumber++; + } + + // 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) (attributeNumber >> 8); + this.contents[codeAttributeAttributeOffset] = (byte) attributeNumber; + // update the attribute length + int codeAttributeLength = localContentsOffset - (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; + contentsOffset = localContentsOffset; + } + + /** * INTERNAL USE-ONLY * That method completes the creation of the code attribute by setting * - the attribute_length @@ -1726,7 +2266,8 @@ public class ClassFile AbstractMethodDeclaration method, MethodBinding binding, int codeAttributeOffset, - int[] startLineIndexes) { + int[] startLineIndexes, + int problemLine) { // reinitialize the localContents with the byte modified by the code stream this.contents = codeStream.bCodeStream; int localContentsOffset = codeStream.classFileOffset; @@ -1803,7 +2344,10 @@ public class ClassFile localContentsOffset += 6; // leave space for attribute_length and local_variable_table_length int descriptorIndex; - if (!codeStream.methodDeclaration.isStatic()) { + int nameIndex; + SourceTypeBinding declaringClassBinding = null; + final boolean methodDeclarationIsStatic = codeStream.methodDeclaration.isStatic(); + if (!methodDeclarationIsStatic) { numberOfEntries++; if (localContentsOffset + 10 >= this.contents.length) { resizeContents(10); @@ -1812,28 +2356,41 @@ public class ClassFile this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = (byte) (code_length >> 8); this.contents[localContentsOffset++] = (byte) code_length; - int nameIndex = constantPool.literalIndex(QualifiedNamesConstants.This); + nameIndex = constantPool.literalIndex(ConstantPool.This); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; + declaringClassBinding = (SourceTypeBinding) codeStream.methodDeclaration.binding.declaringClass; descriptorIndex = - constantPool.literalIndex( - codeStream.methodDeclaration.binding.declaringClass.signature()); + constantPool.literalIndex(declaringClassBinding.signature()); this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); this.contents[localContentsOffset++] = (byte) descriptorIndex; // the resolved position for this is always 0 this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = 0; } + // used to remember the local variable with a generic type + int genericLocalVariablesCounter = 0; + LocalVariableBinding[] genericLocalVariables = null; + int numberOfGenericEntries = 0; + if (binding.isConstructor()) { ReferenceBinding declaringClass = binding.declaringClass; if (declaringClass.isNestedType()) { NestedTypeBinding methodDeclaringClass = (NestedTypeBinding) declaringClass; argSize = methodDeclaringClass.enclosingInstancesSlotSize; SyntheticArgumentBinding[] syntheticArguments; - if ((syntheticArguments = methodDeclaringClass.syntheticEnclosingInstances()) - != null) { + if ((syntheticArguments = methodDeclaringClass.syntheticEnclosingInstances()) != null) { for (int i = 0, max = syntheticArguments.length; i < max; i++) { LocalVariableBinding localVariable = syntheticArguments[i]; + final TypeBinding localVariableTypeBinding = localVariable.type; + if (localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable()) { + if (genericLocalVariables == null) { + // we cannot have more than max locals + genericLocalVariables = new LocalVariableBinding[max]; + } + genericLocalVariables[genericLocalVariablesCounter++] = localVariable; + numberOfGenericEntries++; + } if (localContentsOffset + 10 >= this.contents.length) { resizeContents(10); } @@ -1843,10 +2400,10 @@ public class ClassFile this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = (byte) (code_length >> 8); this.contents[localContentsOffset++] = (byte) code_length; - int nameIndex = constantPool.literalIndex(localVariable.name); + nameIndex = constantPool.literalIndex(localVariable.name); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; - descriptorIndex = constantPool.literalIndex(localVariable.type.signature()); + descriptorIndex = constantPool.literalIndex(localVariableTypeBinding.signature()); this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); this.contents[localContentsOffset++] = (byte) descriptorIndex; int resolvedPosition = localVariable.resolvedPosition; @@ -1860,6 +2417,12 @@ public class ClassFile } else { argSize = binding.isStatic() ? 0 : 1; } + + int genericArgumentsCounter = 0; + int[] genericArgumentsNameIndexes = null; + int[] genericArgumentsResolvedPositions = null; + TypeBinding[] genericArgumentsTypeBindings = null; + if (method.binding != null) { TypeBinding[] parameters = method.binding.parameters; Argument[] arguments = method.arguments; @@ -1875,13 +2438,24 @@ public class ClassFile this.contents[localContentsOffset++] = 0; this.contents[localContentsOffset++] = (byte) (code_length >> 8); this.contents[localContentsOffset++] = (byte) code_length; - int nameIndex = constantPool.literalIndex(arguments[i].name); + nameIndex = constantPool.literalIndex(arguments[i].name); this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); this.contents[localContentsOffset++] = (byte) nameIndex; + int resolvedPosition = argSize; + if (argumentBinding.isParameterizedType() || argumentBinding.isTypeVariable()) { + if (genericArgumentsCounter == 0) { + // we cannot have more than max locals + genericArgumentsNameIndexes = new int[max]; + genericArgumentsResolvedPositions = new int[max]; + genericArgumentsTypeBindings = new TypeBinding[max]; + } + genericArgumentsNameIndexes[genericArgumentsCounter] = nameIndex; + genericArgumentsResolvedPositions[genericArgumentsCounter] = resolvedPosition; + genericArgumentsTypeBindings[genericArgumentsCounter++] = argumentBinding; + } descriptorIndex = constantPool.literalIndex(argumentBinding.signature()); this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); this.contents[localContentsOffset++] = (byte) descriptorIndex; - int resolvedPosition = argSize; if ((argumentBinding == BaseTypes.LongBinding) || (argumentBinding == BaseTypes.DoubleBinding)) argSize += 2; @@ -1901,6 +2475,79 @@ public class ClassFile this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); this.contents[localVariableTableOffset] = (byte) numberOfEntries; attributeNumber++; + + final boolean currentInstanceIsGeneric = + !methodDeclarationIsStatic + && declaringClassBinding != null + && declaringClassBinding.typeVariables != NoTypeVariables; + if (genericLocalVariablesCounter != 0 || genericArgumentsCounter != 0 || currentInstanceIsGeneric) { + // add the local variable type table attribute + numberOfEntries = numberOfGenericEntries + genericArgumentsCounter + (currentInstanceIsGeneric ? 1 : 0); + // reserve enough space + int maxOfEntries = 8 + numberOfEntries * 10; + if (localContentsOffset + maxOfEntries >= this.contents.length) { + resizeContents(maxOfEntries); + } + int localVariableTypeNameIndex = + constantPool.literalIndex(AttributeNamesConstants.LocalVariableTypeTableName); + this.contents[localContentsOffset++] = (byte) (localVariableTypeNameIndex >> 8); + this.contents[localContentsOffset++] = (byte) localVariableTypeNameIndex; + value = numberOfEntries * 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) (numberOfEntries >> 8); + this.contents[localContentsOffset++] = (byte) numberOfEntries; + if (currentInstanceIsGeneric) { + 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 = constantPool.literalIndex(ConstantPool.This); + this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); + this.contents[localContentsOffset++] = (byte) nameIndex; + descriptorIndex = 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]; + this.contents[localContentsOffset++] = 0; + this.contents[localContentsOffset++] = 0; + this.contents[localContentsOffset++] = (byte) (code_length >> 8); + this.contents[localContentsOffset++] = (byte) code_length; + nameIndex = constantPool.literalIndex(localVariable.name); + this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); + this.contents[localContentsOffset++] = (byte) nameIndex; + descriptorIndex = 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; + } + for (int i = 0; i < genericArgumentsCounter; i++) { + this.contents[localContentsOffset++] = 0; + this.contents[localContentsOffset++] = 0; + this.contents[localContentsOffset++] = (byte) (code_length >> 8); + this.contents[localContentsOffset++] = (byte) code_length; + nameIndex = genericArgumentsNameIndexes[i]; + this.contents[localContentsOffset++] = (byte) (nameIndex >> 8); + this.contents[localContentsOffset++] = (byte) nameIndex; + descriptorIndex = constantPool.literalIndex(genericArgumentsTypeBindings[i].genericTypeSignature()); + this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); + this.contents[localContentsOffset++] = (byte) descriptorIndex; + int resolvedPosition = genericArgumentsResolvedPositions[i]; + this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); + this.contents[localContentsOffset++] = (byte) resolvedPosition; + } + attributeNumber++; + } } // update the number of attributes// ensure first that there is enough space available inside the localContents array if (codeAttributeAttributeOffset + 2 >= this.contents.length) { @@ -1930,8 +2577,8 @@ public class ClassFile * @param binding org.eclipse.jdt.internal.compiler.lookup.SyntheticAccessMethodBinding * @param codeAttributeOffset int */ - public void completeCodeAttributeForSyntheticAccessMethod( - SyntheticAccessMethodBinding binding, + public void completeCodeAttributeForSyntheticMethod( + SyntheticMethodBinding binding, int codeAttributeOffset, int[] startLineIndexes) { // reinitialize the contents with the byte modified by the code stream @@ -2005,8 +2652,23 @@ public class ClassFile // leave space for attribute_length and local_variable_table_length int nameIndex; int descriptorIndex; - for (int i = 0; i < codeStream.allLocalsCounter; i++) { + + // used to remember the local variable with a generic type + int genericLocalVariablesCounter = 0; + LocalVariableBinding[] genericLocalVariables = null; + int numberOfGenericEntries = 0; + + for (int i = 0, max = codeStream.allLocalsCounter; i < max; i++) { LocalVariableBinding localVariable = codeStream.locals[i]; + final TypeBinding localVariableTypeBinding = localVariable.type; + boolean isParameterizedType = localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable(); + if (localVariable.initializationCount != 0 && isParameterizedType) { + if (genericLocalVariables == null) { + // we cannot have more than max locals + genericLocalVariables = new LocalVariableBinding[max]; + } + genericLocalVariables[genericLocalVariablesCounter++] = localVariable; + } for (int j = 0; j < localVariable.initializationCount; j++) { int startPC = localVariable.initializationPCs[j << 1]; int endPC = localVariable.initializationPCs[(j << 1) + 1]; @@ -2021,6 +2683,9 @@ public class ClassFile } // now we can safely add the local entry numberOfEntries++; + if (isParameterizedType) { + numberOfGenericEntries++; + } contents[localContentsOffset++] = (byte) (startPC >> 8); contents[localContentsOffset++] = (byte) startPC; int length = endPC - startPC; @@ -2029,7 +2694,7 @@ public class ClassFile nameIndex = constantPool.literalIndex(localVariable.name); contents[localContentsOffset++] = (byte) (nameIndex >> 8); contents[localContentsOffset++] = (byte) nameIndex; - descriptorIndex = constantPool.literalIndex(localVariable.type.signature()); + descriptorIndex = constantPool.literalIndex(localVariableTypeBinding.signature()); contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); contents[localContentsOffset++] = (byte) descriptorIndex; int resolvedPosition = localVariable.resolvedPosition; @@ -2047,6 +2712,52 @@ public class ClassFile contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); contents[localVariableTableOffset] = (byte) numberOfEntries; attributeNumber++; + + if (genericLocalVariablesCounter != 0) { + // add the local variable type table attribute + int maxOfEntries = 8 + numberOfGenericEntries * 10; + // reserve enough space + if (localContentsOffset + maxOfEntries >= this.contents.length) { + resizeContents(maxOfEntries); + } + int localVariableTypeNameIndex = + constantPool.literalIndex(AttributeNamesConstants.LocalVariableTypeTableName); + contents[localContentsOffset++] = (byte) (localVariableTypeNameIndex >> 8); + contents[localContentsOffset++] = (byte) localVariableTypeNameIndex; + value = numberOfGenericEntries * 10 + 2; + contents[localContentsOffset++] = (byte) (value >> 24); + contents[localContentsOffset++] = (byte) (value >> 16); + contents[localContentsOffset++] = (byte) (value >> 8); + contents[localContentsOffset++] = (byte) value; + contents[localContentsOffset++] = (byte) (numberOfGenericEntries >> 8); + contents[localContentsOffset++] = (byte) numberOfGenericEntries; + + 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 + contents[localContentsOffset++] = (byte) (startPC >> 8); + contents[localContentsOffset++] = (byte) startPC; + int length = endPC - startPC; + contents[localContentsOffset++] = (byte) (length >> 8); + contents[localContentsOffset++] = (byte) length; + nameIndex = constantPool.literalIndex(localVariable.name); + contents[localContentsOffset++] = (byte) (nameIndex >> 8); + contents[localContentsOffset++] = (byte) nameIndex; + descriptorIndex = constantPool.literalIndex(localVariable.type.genericTypeSignature()); + contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); + contents[localContentsOffset++] = (byte) descriptorIndex; + int resolvedPosition = localVariable.resolvedPosition; + contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); + contents[localContentsOffset++] = (byte) resolvedPosition; + } + } + } + attributeNumber++; + } } // update the number of attributes // ensure first that there is enough space available inside the contents array @@ -2082,115 +2793,74 @@ public class ClassFile /** * INTERNAL USE-ONLY - * Request the creation of a ClassFile compatible representation of a problematic type + * This methods returns a char[] representing the file name of the receiver * - * @param typeDeclaration org.eclipse.jdt.internal.compiler.ast.TypeDeclaration - * @param unitResult org.eclipse.jdt.internal.compiler.CompilationUnitResult + * @return char[] */ - public static void createProblemType( - TypeDeclaration typeDeclaration, - CompilationResult unitResult) { - SourceTypeBinding typeBinding = typeDeclaration.binding; - ClassFile classFile = new ClassFile(typeBinding, null, true); - - // TODO (olivier) handle cases where a field cannot be generated (name too long) - // TODO (olivier) handle too many methods - // inner attributes - if (typeBinding.isMemberType()) - classFile.recordEnclosingTypeAttributes(typeBinding); + public char[] fileName() { + return constantPool.UTF8Cache.returnKeyFor(1); + } - // add its fields - FieldBinding[] fields = typeBinding.fields; - if ((fields != null) && (fields != NoFields)) { - for (int i = 0, max = fields.length; i < max; i++) { - if (fields[i].constant == null) { - FieldReference.getConstantFor(fields[i], null, false, null); - } - } - classFile.addFieldInfos(); - } else { - // we have to set the number of fields to be equals to 0 - classFile.contents[classFile.contentsOffset++] = 0; - classFile.contents[classFile.contentsOffset++] = 0; - } - // leave some space for the methodCount - classFile.setForMethodInfos(); - // add its user defined methods - MethodBinding[] methods = typeBinding.methods; - AbstractMethodDeclaration[] methodDeclarations = typeDeclaration.methods; - int maxMethodDecl = methodDeclarations == null ? 0 : methodDeclarations.length; - int problemsLength; - IProblem[] problems = unitResult.getErrors(); - if (problems == null) { - problems = new IProblem[0]; - } - IProblem[] problemsCopy = new IProblem[problemsLength = problems.length]; - System.arraycopy(problems, 0, problemsCopy, 0, problemsLength); - if (methods != null) { - if (typeBinding.isInterface()) { - // we cannot create problem methods for an interface. So we have to generate a clinit - // which should contain all the problem - classFile.addProblemClinit(problemsCopy); - for (int i = 0, max = methods.length; i < max; i++) { - MethodBinding methodBinding; - if ((methodBinding = methods[i]) != null) { - // find the corresponding method declaration - for (int j = 0; j < maxMethodDecl; j++) { - if ((methodDeclarations[j] != null) - && (methodDeclarations[j].binding == methods[i])) { - if (!methodBinding.isConstructor()) { - classFile.addAbstractMethod(methodDeclarations[j], methodBinding); - } - break; - } - } + private void generateAnnotation(Annotation annotation, int attributeOffset) { + if (contentsOffset + 4 >= this.contents.length) { + resizeContents(4); + } + TypeBinding annotationTypeBinding = annotation.resolvedType; + if (annotationTypeBinding == null) { + this.contentsOffset = attributeOffset; + return; + } + final int typeIndex = constantPool.literalIndex(annotationTypeBinding.signature()); + contents[contentsOffset++] = (byte) (typeIndex >> 8); + contents[contentsOffset++] = (byte) typeIndex; + if (annotation instanceof NormalAnnotation) { + NormalAnnotation normalAnnotation = (NormalAnnotation) annotation; + MemberValuePair[] memberValuePairs = normalAnnotation.memberValuePairs; + if (memberValuePairs != null) { + final int memberValuePairsLength = memberValuePairs.length; + contents[contentsOffset++] = (byte) (memberValuePairsLength >> 8); + contents[contentsOffset++] = (byte) memberValuePairsLength; + for (int i = 0; i < memberValuePairsLength; i++) { + MemberValuePair memberValuePair = memberValuePairs[i]; + if (contentsOffset + 2 >= this.contents.length) { + resizeContents(2); } - } - } else { - for (int i = 0, max = methods.length; i < max; i++) { - MethodBinding methodBinding; - if ((methodBinding = methods[i]) != null) { - // find the corresponding method declaration - for (int j = 0; j < maxMethodDecl; j++) { - if ((methodDeclarations[j] != null) - && (methodDeclarations[j].binding == methods[i])) { - AbstractMethodDeclaration methodDecl; - if ((methodDecl = methodDeclarations[j]).isConstructor()) { - classFile.addProblemConstructor(methodDecl, methodBinding, problemsCopy); - } else { - classFile.addProblemMethod(methodDecl, methodBinding, problemsCopy); - } - break; - } - } + final int elementNameIndex = constantPool.literalIndex(memberValuePair.name); + contents[contentsOffset++] = (byte) (elementNameIndex >> 8); + contents[contentsOffset++] = (byte) elementNameIndex; + MethodBinding methodBinding = memberValuePair.binding; + if (methodBinding == null) { + contentsOffset = attributeOffset; + } else { + generateElementValue(memberValuePair.value, methodBinding.returnType, attributeOffset); } } + } else { + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; } - // 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.recordNestedMemberAttribute(memberType.binding); - ClassFile.createProblemType(memberType, unitResult); - } - } - } - classFile.addAttributes(); - unitResult.record(typeBinding.constantPoolName(), classFile); - } - - /** - * INTERNAL USE-ONLY - * This methods returns a char[] representing the file name of the receiver - * - * @return char[] - */ - public char[] fileName() { - return constantPool.UTF8Cache.returnKeyFor(1); + } else if (annotation instanceof SingleMemberAnnotation) { + SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) annotation; + // this is a single member annotation (one member value) + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 1; + if (contentsOffset + 2 >= this.contents.length) { + resizeContents(2); + } + final int elementNameIndex = constantPool.literalIndex(VALUE); + contents[contentsOffset++] = (byte) (elementNameIndex >> 8); + contents[contentsOffset++] = (byte) elementNameIndex; + MethodBinding methodBinding = singleMemberAnnotation.singlePair.binding; + if (methodBinding == null) { + contentsOffset = attributeOffset; + } else { + generateElementValue(singleMemberAnnotation.memberValue, methodBinding.returnType, attributeOffset); + } + } else { + // this is a marker annotation (no member value pairs) + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; + } } /** @@ -2211,6 +2881,187 @@ public class ClassFile contentsOffset += 12; } + private void generateElementValue( + Expression defaultValue, + TypeBinding memberValuePairReturnType, + int attributeOffset) { + Constant constant = defaultValue.constant; + TypeBinding defaultValueBinding = defaultValue.resolvedType; + if (defaultValueBinding == null) { + contentsOffset = attributeOffset; + } else { + if (memberValuePairReturnType.isArrayType() && !defaultValueBinding.isArrayType()) { + // automatic wrapping + if (contentsOffset + 3 >= this.contents.length) { + resizeContents(3); + } + contents[contentsOffset++] = (byte) '['; + contents[contentsOffset++] = (byte) 0; + contents[contentsOffset++] = (byte) 1; + } + if (constant != null && constant != Constant.NotAConstant) { + generateElementValue(attributeOffset, defaultValue, constant, memberValuePairReturnType.leafComponentType()); + } else { + generateElementValueForNonConstantExpression(defaultValue, attributeOffset, defaultValueBinding); + } + } + } + + /** + * @param attributeOffset + */ + private void generateElementValue(int attributeOffset, Expression defaultValue, Constant constant, TypeBinding binding) { + if (contentsOffset + 3 >= this.contents.length) { + resizeContents(3); + } + switch (binding.id) { + case T_boolean : + contents[contentsOffset++] = (byte) 'Z'; + int booleanValueIndex = + constantPool.literalIndex(constant.booleanValue() ? 1 : 0); + contents[contentsOffset++] = (byte) (booleanValueIndex >> 8); + contents[contentsOffset++] = (byte) booleanValueIndex; + break; + case T_byte : + contents[contentsOffset++] = (byte) 'B'; + int integerValueIndex = + constantPool.literalIndex(constant.intValue()); + contents[contentsOffset++] = (byte) (integerValueIndex >> 8); + contents[contentsOffset++] = (byte) integerValueIndex; + break; + case T_char : + contents[contentsOffset++] = (byte) 'C'; + integerValueIndex = + constantPool.literalIndex(constant.intValue()); + contents[contentsOffset++] = (byte) (integerValueIndex >> 8); + contents[contentsOffset++] = (byte) integerValueIndex; + break; + case T_int : + contents[contentsOffset++] = (byte) 'I'; + integerValueIndex = + constantPool.literalIndex(constant.intValue()); + contents[contentsOffset++] = (byte) (integerValueIndex >> 8); + contents[contentsOffset++] = (byte) integerValueIndex; + break; + case T_short : + contents[contentsOffset++] = (byte) 'S'; + integerValueIndex = + constantPool.literalIndex(constant.intValue()); + contents[contentsOffset++] = (byte) (integerValueIndex >> 8); + contents[contentsOffset++] = (byte) integerValueIndex; + break; + case T_float : + contents[contentsOffset++] = (byte) 'F'; + int floatValueIndex = + constantPool.literalIndex(constant.floatValue()); + contents[contentsOffset++] = (byte) (floatValueIndex >> 8); + contents[contentsOffset++] = (byte) floatValueIndex; + break; + case T_double : + contents[contentsOffset++] = (byte) 'D'; + int doubleValueIndex = + constantPool.literalIndex(constant.doubleValue()); + contents[contentsOffset++] = (byte) (doubleValueIndex >> 8); + contents[contentsOffset++] = (byte) doubleValueIndex; + break; + case T_long : + contents[contentsOffset++] = (byte) 'J'; + int longValueIndex = + constantPool.literalIndex(constant.longValue()); + contents[contentsOffset++] = (byte) (longValueIndex >> 8); + contents[contentsOffset++] = (byte) longValueIndex; + break; + case T_JavaLangString : + contents[contentsOffset++] = (byte) 's'; + int stringValueIndex = + constantPool.literalIndex(((StringConstant) constant).stringValue().toCharArray()); + if (stringValueIndex == -1) { + if (!creatingProblemType) { + // report an error and abort: will lead to a problem type classfile creation + TypeDeclaration typeDeclaration = referenceBinding.scope.referenceContext; + typeDeclaration.scope.problemReporter().stringConstantIsExceedingUtf8Limit(defaultValue); + } else { + // already inside a problem type creation : no attribute + contentsOffset = attributeOffset; + } + } else { + contents[contentsOffset++] = (byte) (stringValueIndex >> 8); + contents[contentsOffset++] = (byte) stringValueIndex; + } + } + } + + private void generateElementValueForNonConstantExpression(Expression defaultValue, int attributeOffset, TypeBinding defaultValueBinding) { + if (defaultValueBinding != null) { + if (defaultValueBinding.isEnum()) { + if (contentsOffset + 5 >= this.contents.length) { + resizeContents(5); + } + contents[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 { + contentsOffset = attributeOffset; + } + if (fieldBinding != null) { + final int enumConstantTypeNameIndex = constantPool.literalIndex(fieldBinding.type.signature()); + final int enumConstantNameIndex = constantPool.literalIndex(fieldBinding.name); + contents[contentsOffset++] = (byte) (enumConstantTypeNameIndex >> 8); + contents[contentsOffset++] = (byte) enumConstantTypeNameIndex; + contents[contentsOffset++] = (byte) (enumConstantNameIndex >> 8); + contents[contentsOffset++] = (byte) enumConstantNameIndex; + } + } else if (defaultValueBinding.isAnnotationType()) { + if (contentsOffset + 1 >= this.contents.length) { + resizeContents(1); + } + contents[contentsOffset++] = (byte) '@'; + generateAnnotation((Annotation) defaultValue, attributeOffset); + } else if (defaultValueBinding.isArrayType()) { + // array type + if (contentsOffset + 3 >= this.contents.length) { + resizeContents(3); + } + contents[contentsOffset++] = (byte) '['; + if (defaultValue instanceof ArrayInitializer) { + ArrayInitializer arrayInitializer = (ArrayInitializer) defaultValue; + int arrayLength = arrayInitializer.expressions != null ? arrayInitializer.expressions.length : 0; + contents[contentsOffset++] = (byte) (arrayLength >> 8); + contents[contentsOffset++] = (byte) arrayLength; + for (int i = 0; i < arrayLength; i++) { + generateElementValue(arrayInitializer.expressions[i], defaultValueBinding.leafComponentType(), attributeOffset); + } + } else { + contentsOffset = attributeOffset; + } + } else { + // class type + if (contentsOffset + 3 >= this.contents.length) { + resizeContents(3); + } + contents[contentsOffset++] = (byte) 'c'; + if (defaultValue instanceof ClassLiteralAccess) { + ClassLiteralAccess classLiteralAccess = (ClassLiteralAccess) defaultValue; + final int classInfoIndex = constantPool.literalIndex(classLiteralAccess.targetType.signature()); + contents[contentsOffset++] = (byte) (classInfoIndex >> 8); + contents[contentsOffset++] = (byte) classInfoIndex; + } else { + contentsOffset = attributeOffset; + } + } + } else { + contentsOffset = attributeOffset; + } + } + + public int generateMethodInfoAttribute(MethodBinding methodBinding) { + return generateMethodInfoAttribute(methodBinding, false); + } /** * INTERNAL USE-ONLY * That method generates the attributes of a code attribute. @@ -2224,7 +3075,7 @@ public class ClassFile * @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding * @return int */ - public int generateMethodInfoAttribute(MethodBinding methodBinding) { + public int generateMethodInfoAttribute(MethodBinding methodBinding, boolean createProblemMethod) { // leave two bytes for the attribute_number contentsOffset += 2; // now we can handle all the attribute for that method info: @@ -2258,7 +3109,7 @@ public class ClassFile contents[contentsOffset++] = (byte) (length >> 8); contents[contentsOffset++] = (byte) length; for (int i = 0; i < length; i++) { - int exceptionIndex = constantPool.literalIndex(thrownsExceptions[i]); + int exceptionIndex = constantPool.literalIndexForType(thrownsExceptions[i].constantPoolName()); contents[contentsOffset++] = (byte) (exceptionIndex >> 8); contents[contentsOffset++] = (byte) exceptionIndex; } @@ -2300,9 +3151,72 @@ public class ClassFile attributeNumber++; } + // add signature attribute + char[] genericSignature = methodBinding.genericSignature(); + if (genericSignature != null) { + // check that there is enough space to write all the bytes for the field info corresponding + // to the @fieldBinding + if (contentsOffset + 8 >= this.contents.length) { + resizeContents(8); + } + int signatureAttributeNameIndex = + constantPool.literalIndex(AttributeNamesConstants.SignatureName); + contents[contentsOffset++] = (byte) (signatureAttributeNameIndex >> 8); + contents[contentsOffset++] = (byte) signatureAttributeNameIndex; + // the length of a signature attribute is equals to 2 + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 0; + contents[contentsOffset++] = 2; + int signatureIndex = + constantPool.literalIndex(genericSignature); + contents[contentsOffset++] = (byte) (signatureIndex >> 8); + contents[contentsOffset++] = (byte) signatureIndex; + attributeNumber++; + } + if (this.targetJDK >= ClassFileConstants.JDK1_5 && !this.creatingProblemType && !createProblemMethod) { + AbstractMethodDeclaration methodDeclaration = methodBinding.sourceMethod(); + if (methodDeclaration != null) { + Annotation[] annotations = methodDeclaration.annotations; + if (annotations != null) { + attributeNumber += generateRuntimeAnnotations(annotations); + } + if ((methodBinding.tagBits & TagBits.HasParameterAnnotations) != 0) { + Argument[] arguments = methodDeclaration.arguments; + if (arguments != null) { + attributeNumber += generateRuntimeAnnotationsForParameters(arguments); + } + } + } + } return attributeNumber; } + public int generateMethodInfoAttribute(MethodBinding methodBinding, AnnotationMethodDeclaration declaration) { + int attributesNumber = generateMethodInfoAttribute(methodBinding); + int attributeOffset = contentsOffset; + if ((declaration.modifiers & AccAnnotationDefault) != 0) { + // add an annotation default attribute + int annotationDefaultNameIndex = + constantPool.literalIndex(AttributeNamesConstants.AnnotationDefaultName); + contents[contentsOffset++] = (byte) (annotationDefaultNameIndex >> 8); + contents[contentsOffset++] = (byte) annotationDefaultNameIndex; + int attributeLengthOffset = contentsOffset; + contentsOffset += 4; + + generateElementValue(declaration.defaultValue, declaration.binding.returnType, attributeOffset); + if (contentsOffset != attributeOffset) { + int attributeLength = contentsOffset - attributeLengthOffset - 4; + contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); + contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); + contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); + contents[attributeLengthOffset++] = (byte) attributeLength; + attributesNumber++; + } + } + return attributesNumber; + } + /** * INTERNAL USE-ONLY * That method generates the header of a method info: @@ -2368,11 +3282,11 @@ public class ClassFile } contents[contentsOffset++] = (byte) ((AccDefault | AccStatic) >> 8); contents[contentsOffset++] = (byte) (AccDefault | AccStatic); - int nameIndex = constantPool.literalIndex(QualifiedNamesConstants.Clinit); + int nameIndex = constantPool.literalIndex(ConstantPool.Clinit); contents[contentsOffset++] = (byte) (nameIndex >> 8); contents[contentsOffset++] = (byte) nameIndex; int descriptorIndex = - constantPool.literalIndex(QualifiedNamesConstants.ClinitSignature); + constantPool.literalIndex(ConstantPool.ClinitSignature); contents[contentsOffset++] = (byte) (descriptorIndex >> 8); contents[contentsOffset++] = (byte) descriptorIndex; // We know that we won't get more than 1 attribute: the code attribute @@ -2381,6 +3295,264 @@ public class ClassFile } /** + * 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) { + for (int i = 0, max = methodDeclarations.length; i < max; i++) { + MethodDeclaration methodDeclaration = methodDeclarations[i]; + MethodBinding methodBinding = methodDeclaration.binding; + String readableName = new String(methodBinding.readableName()); + IProblem[] problems = compilationResult.problems; + int problemsCount = compilationResult.problemCount; + for (int j = 0; j < problemsCount; j++) { + IProblem problem = problems[j]; + if (problem != null + && problem.getID() == IProblem.AbstractMethodMustBeImplemented + && problem.getMessage().indexOf(readableName) != -1) { + // we found a match + addMissingAbstractProblemMethod(methodDeclaration, methodBinding, problem, compilationResult); + } + } + } + } + } + + /** + * @param annotations + * @return the number of attributes created while dumping the annotations in the .class file + */ + private int generateRuntimeAnnotations(final Annotation[] annotations) { + int attributesNumber = 0; + final int length = annotations.length; + int visibleAnnotationsCounter = 0; + int invisibleAnnotationsCounter = 0; + + for (int i = 0; i < length; i++) { + Annotation annotation = annotations[i]; + if (isRuntimeInvisible(annotation)) { + invisibleAnnotationsCounter++; + } else if (isRuntimeVisible(annotation)) { + visibleAnnotationsCounter++; + } + } + + if (invisibleAnnotationsCounter != 0) { + int annotationAttributeOffset = contentsOffset; + if (contentsOffset + 10 >= contents.length) { + resizeContents(10); + } + int runtimeInvisibleAnnotationsAttributeNameIndex = + constantPool.literalIndex(AttributeNamesConstants.RuntimeInvisibleAnnotationsName); + contents[contentsOffset++] = (byte) (runtimeInvisibleAnnotationsAttributeNameIndex >> 8); + contents[contentsOffset++] = (byte) runtimeInvisibleAnnotationsAttributeNameIndex; + int attributeLengthOffset = contentsOffset; + contentsOffset += 4; // leave space for the attribute length + + int annotationsLengthOffset = contentsOffset; + contentsOffset += 2; // leave space for the annotations length + + contents[annotationsLengthOffset++] = (byte) (invisibleAnnotationsCounter >> 8); + contents[annotationsLengthOffset++] = (byte) invisibleAnnotationsCounter; + + loop: for (int i = 0; i < length; i++) { + if (invisibleAnnotationsCounter == 0) break loop; + Annotation annotation = annotations[i]; + if (isRuntimeInvisible(annotation)) { + generateAnnotation(annotation, annotationAttributeOffset); + invisibleAnnotationsCounter--; + if (this.contentsOffset == annotationAttributeOffset) { + break loop; + } + } + } + if (contentsOffset != annotationAttributeOffset) { + int attributeLength = contentsOffset - attributeLengthOffset - 4; + contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); + contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); + contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); + contents[attributeLengthOffset++] = (byte) attributeLength; + attributesNumber++; + } else { + contentsOffset = annotationAttributeOffset; + } + } + + if (visibleAnnotationsCounter != 0) { + int annotationAttributeOffset = contentsOffset; + if (contentsOffset + 10 >= contents.length) { + resizeContents(10); + } + int runtimeVisibleAnnotationsAttributeNameIndex = + constantPool.literalIndex(AttributeNamesConstants.RuntimeVisibleAnnotationsName); + contents[contentsOffset++] = (byte) (runtimeVisibleAnnotationsAttributeNameIndex >> 8); + contents[contentsOffset++] = (byte) runtimeVisibleAnnotationsAttributeNameIndex; + int attributeLengthOffset = contentsOffset; + contentsOffset += 4; // leave space for the attribute length + + int annotationsLengthOffset = contentsOffset; + contentsOffset += 2; // leave space for the annotations length + + contents[annotationsLengthOffset++] = (byte) (visibleAnnotationsCounter >> 8); + contents[annotationsLengthOffset++] = (byte) visibleAnnotationsCounter; + + loop: for (int i = 0; i < length; i++) { + if (visibleAnnotationsCounter == 0) break loop; + Annotation annotation = annotations[i]; + if (isRuntimeVisible(annotation)) { + visibleAnnotationsCounter--; + generateAnnotation(annotation, annotationAttributeOffset); + if (this.contentsOffset == annotationAttributeOffset) { + break loop; + } + } + } + if (contentsOffset != annotationAttributeOffset) { + int attributeLength = contentsOffset - attributeLengthOffset - 4; + contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); + contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); + contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); + contents[attributeLengthOffset++] = (byte) attributeLength; + attributesNumber++; + } else { + 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 = annotations[j]; + if (isRuntimeInvisible(annotation)) { + annotationsCounters[i][INVISIBLE_INDEX]++; + invisibleParametersAnnotationsCounter++; + } else if (isRuntimeVisible(annotation)) { + annotationsCounters[i][VISIBLE_INDEX]++; + visibleParametersAnnotationsCounter++; + } + } + } + } + int attributesNumber = 0; + int annotationAttributeOffset = contentsOffset; + if (invisibleParametersAnnotationsCounter != 0) { + if (contentsOffset + 7 >= contents.length) { + resizeContents(7); + } + int attributeNameIndex = + constantPool.literalIndex(AttributeNamesConstants.RuntimeInvisibleParameterAnnotationsName); + contents[contentsOffset++] = (byte) (attributeNameIndex >> 8); + contents[contentsOffset++] = (byte) attributeNameIndex; + int attributeLengthOffset = contentsOffset; + contentsOffset += 4; // leave space for the attribute length + + contents[contentsOffset++] = (byte) argumentsLength; + invisibleLoop: for (int i = 0; i < argumentsLength; i++) { + if (contentsOffset + 2 >= contents.length) { + resizeContents(2); + } + if (invisibleParametersAnnotationsCounter == 0) { + contents[contentsOffset++] = (byte) 0; + contents[contentsOffset++] = (byte) 0; + } else { + final int numberOfInvisibleAnnotations = annotationsCounters[i][INVISIBLE_INDEX]; + contents[contentsOffset++] = (byte) (numberOfInvisibleAnnotations >> 8); + contents[contentsOffset++] = (byte) numberOfInvisibleAnnotations; + if (numberOfInvisibleAnnotations != 0) { + Argument argument = arguments[i]; + Annotation[] annotations = argument.annotations; + for (int j = 0, max = annotations.length; j < max; j++) { + Annotation annotation = annotations[j]; + if (isRuntimeInvisible(annotation)) { + generateAnnotation(annotation, annotationAttributeOffset); + if (contentsOffset == annotationAttributeOffset) { + break invisibleLoop; + } + invisibleParametersAnnotationsCounter--; + } + } + } + } + } + if (contentsOffset != annotationAttributeOffset) { + int attributeLength = contentsOffset - attributeLengthOffset - 4; + contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); + contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); + contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); + contents[attributeLengthOffset++] = (byte) attributeLength; + attributesNumber++; + } else { + contentsOffset = annotationAttributeOffset; + } + } + if (visibleParametersAnnotationsCounter != 0) { + if (contentsOffset + 7 >= contents.length) { + resizeContents(7); + } + int attributeNameIndex = + constantPool.literalIndex(AttributeNamesConstants.RuntimeVisibleParameterAnnotationsName); + contents[contentsOffset++] = (byte) (attributeNameIndex >> 8); + contents[contentsOffset++] = (byte) attributeNameIndex; + int attributeLengthOffset = contentsOffset; + contentsOffset += 4; // leave space for the attribute length + + contents[contentsOffset++] = (byte) argumentsLength; + visibleLoop: for (int i = 0; i < argumentsLength; i++) { + if (contentsOffset + 2 >= contents.length) { + resizeContents(2); + } + if (visibleParametersAnnotationsCounter == 0) { + contents[contentsOffset++] = (byte) 0; + contents[contentsOffset++] = (byte) 0; + } else { + final int numberOfVisibleAnnotations = annotationsCounters[i][VISIBLE_INDEX]; + contents[contentsOffset++] = (byte) (numberOfVisibleAnnotations >> 8); + contents[contentsOffset++] = (byte) numberOfVisibleAnnotations; + if (numberOfVisibleAnnotations != 0) { + Argument argument = arguments[i]; + Annotation[] annotations = argument.annotations; + for (int j = 0, max = annotations.length; j < max; j++) { + Annotation annotation = annotations[j]; + if (isRuntimeVisible(annotation)) { + generateAnnotation(annotation, annotationAttributeOffset); + if (contentsOffset == annotationAttributeOffset) { + break visibleLoop; + } + visibleParametersAnnotationsCounter--; + } + } + } + } + } + if (contentsOffset != annotationAttributeOffset) { + int attributeLength = contentsOffset - attributeLengthOffset - 4; + contents[attributeLengthOffset++] = (byte) (attributeLength >> 24); + contents[attributeLengthOffset++] = (byte) (attributeLength >> 16); + contents[attributeLengthOffset++] = (byte) (attributeLength >> 8); + contents[attributeLengthOffset++] = (byte) attributeLength; + attributesNumber++; + } else { + contentsOffset = annotationAttributeOffset; + } + } + return attributesNumber; + } + /** * EXTERNAL API * Answer the actual bytes of the class file * @@ -2422,6 +3594,31 @@ public class ClassFile } } + + private boolean isRuntimeInvisible(Annotation annotation) { + final TypeBinding annotationBinding = annotation.resolvedType; + if (annotationBinding == null) { + return false; + } + long metaTagBits = annotationBinding.getAnnotationTagBits(); + if ((metaTagBits & TagBits.AnnotationRetentionMASK) == 0) + return true; // by default the retention is CLASS + + return (metaTagBits & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationClassRetention; + } + + private boolean isRuntimeVisible(Annotation annotation) { + final TypeBinding annotationBinding = annotation.resolvedType; + if (annotationBinding == null) { + return false; + } + long metaTagBits = annotationBinding.tagBits; + if ((metaTagBits & TagBits.AnnotationRetentionMASK) == 0) + return false; // by default the retention is CLASS + + return (metaTagBits & TagBits.AnnotationRetentionMASK) == TagBits.AnnotationRuntimeRetention; + } + /** * INTERNAL USE-ONLY * Returns the most enclosing classfile of the receiver. This is used know to store the constant pool name @@ -2520,39 +3717,6 @@ public class ClassFile /** * INTERNAL USE-ONLY - * Search the line number corresponding to a specific position - */ - public static final int searchLineNumber( - int[] startLineIndexes, - int position) { - // this code is completely useless, but it is the same implementation than - // org.eclipse.jdt.internal.compiler.problem.ProblemHandler.searchLineNumber(int[], int) - // if (startLineIndexes == null) - // return 1; - int length = startLineIndexes.length; - if (length == 0) - return 1; - int g = 0, d = length - 1; - int m = 0; - while (g <= d) { - m = (g + d) / 2; - if (position < startLineIndexes[m]) { - d = m - 1; - } else - if (position > startLineIndexes[m]) { - g = m + 1; - } else { - return m + 1; - } - } - if (position < startLineIndexes[m]) { - return m + 1; - } - return m + 2; - } - - /** - * INTERNAL USE-ONLY * This methods leaves the space for method counts recording. */ public void setForMethodInfos() { @@ -2560,62 +3724,4 @@ public class ClassFile methodCountOffset = contentsOffset; contentsOffset += 2; } - - /** - * INTERNAL USE-ONLY - * outputPath is formed like: - * c:\temp\ the last character is a file separator - * relativeFileName is formed like: - * java\lang\String.class - * @param generatePackagesStructure a flag to know if the packages structure has to be generated. - * @param outputPath the output directory - * @param relativeFileName java.lang.String - * @param contents byte[] - * - */ - public static void writeToDisk( - boolean generatePackagesStructure, - String outputPath, - String relativeFileName, - byte[] contents) - throws IOException { - - BufferedOutputStream output = null; - if (generatePackagesStructure) { - output = new BufferedOutputStream( - new FileOutputStream( - new File(buildAllDirectoriesInto(outputPath, relativeFileName)))); - } else { - String fileName = null; - char fileSeparatorChar = File.separatorChar; - String fileSeparator = File.separator; - // First we ensure that the outputPath exists - outputPath = outputPath.replace('/', fileSeparatorChar); - // To be able to pass the mkdirs() method we need to remove the extra file separator at the end of the outDir name - int indexOfPackageSeparator = relativeFileName.lastIndexOf(fileSeparatorChar); - if (indexOfPackageSeparator == -1) { - if (outputPath.endsWith(fileSeparator)) { - fileName = outputPath + relativeFileName; - } else { - fileName = outputPath + fileSeparator + relativeFileName; - } - } else { - int length = relativeFileName.length(); - if (outputPath.endsWith(fileSeparator)) { - fileName = outputPath + relativeFileName.substring(indexOfPackageSeparator + 1, length); - } else { - fileName = outputPath + fileSeparator + relativeFileName.substring(indexOfPackageSeparator + 1, length); - } - } - output = new BufferedOutputStream( - new FileOutputStream( - new File(fileName))); - } - try { - output.write(contents); - } finally { - output.flush(); - output.close(); - } - } } diff --git a/src/org/eclipse/jdt/internal/compiler/CompilationResult.java b/src/org/eclipse/jdt/internal/compiler/CompilationResult.java index 684df0b..d6b7475 100644 --- a/src/org/eclipse/jdt/internal/compiler/CompilationResult.java +++ b/src/org/eclipse/jdt/internal/compiler/CompilationResult.java @@ -35,6 +35,7 @@ import org.eclipse.jdt.core.compiler.*; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.env.*; import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; +import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import java.util.*; @@ -56,6 +57,7 @@ public class CompilationResult { public int unitIndex, totalUnitsKnown; public boolean hasBeenAccepted = false; public char[] fileName; + public boolean hasInconsistentToplevelHierarchies = false; // record the fact some toplevel types have inconsistent hierarchies public CompilationResult( char[] fileName, @@ -358,6 +360,10 @@ public class CompilationResult { */ public void record(char[] typeName, ClassFile classFile) { + SourceTypeBinding sourceType = classFile.referenceBinding; + if (!sourceType.isLocalType() && sourceType.isHierarchyInconsistent()) { + this.hasInconsistentToplevelHierarchies = true; + } compiledTypes.put(typeName, classFile); } diff --git a/src/org/eclipse/jdt/internal/compiler/Compiler.java b/src/org/eclipse/jdt/internal/compiler/Compiler.java index 92c85dc..ba20e99 100644 --- a/src/org/eclipse/jdt/internal/compiler/Compiler.java +++ b/src/org/eclipse/jdt/internal/compiler/Compiler.java @@ -180,7 +180,7 @@ public class Compiler implements ITypeRequestor, ProblemSeverities { /** * Add an additional binary type */ - public void accept(IBinaryType binaryType, PackageBinding packageBinding) { + public void accept(IBinaryType binaryType, PackageBinding packageBinding, AccessRestriction accessRestriction) { if (options.verbose) { System.out.println( Util.bind( @@ -190,14 +190,14 @@ public class Compiler implements ITypeRequestor, ProblemSeverities { // new Exception("TRACE BINARY").printStackTrace(System.out); // System.out.println(); } - lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding); + lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding, accessRestriction); } /** * Add an additional compilation unit into the loop * -> build compilation unit declarations, their bindings and record their results. */ - public void accept(ICompilationUnit sourceUnit) { + public void accept(ICompilationUnit sourceUnit, AccessRestriction accessRestriction) { // Switch the current policy and compilation result for this unit to the requested one. CompilationResult unitResult = new CompilationResult(sourceUnit, totalUnits, totalUnits, this.options.maxProblemsPerUnit); @@ -220,7 +220,7 @@ public class Compiler implements ITypeRequestor, ProblemSeverities { parsedUnit = parser.dietParse(sourceUnit, unitResult); } // initial type binding creation - lookupEnvironment.buildTypeBindings(parsedUnit); + lookupEnvironment.buildTypeBindings(parsedUnit, accessRestriction); this.addCompilationUnit(sourceUnit, parsedUnit); // binding resolution @@ -239,7 +239,7 @@ public class Compiler implements ITypeRequestor, ProblemSeverities { /** * Add additional source types */ - public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) { + public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding, AccessRestriction accessRestriction) { problemReporter.abortDueToInternalError( Util.bind( "abort.againstSourceModel" , //$NON-NLS-1$ @@ -295,7 +295,7 @@ public class Compiler implements ITypeRequestor, ProblemSeverities { parsedUnit = parser.dietParse(sourceUnits[i], unitResult); } // initial type binding creation - lookupEnvironment.buildTypeBindings(parsedUnit); + lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/); this.addCompilationUnit(sourceUnits[i], parsedUnit); //} catch (AbortCompilationUnit e) { // requestor.acceptResult(unitResult.tagAsAccepted()); @@ -340,11 +340,13 @@ public class Compiler implements ITypeRequestor, ProblemSeverities { unitsToProcess[i] = null; // release reference to processed unit declaration requestor.acceptResult(unit.compilationResult.tagAsAccepted()); if (options.verbose) - System.out.println(Util.bind("compilation.done", //$NON-NLS-1$ - new String[] { - String.valueOf(i + 1), - String.valueOf(totalUnits), - new String(unit.getFileName())})); + System.out.println( + Util.bind( + "compilation.done", //$NON-NLS-1$ + new String[] { + String.valueOf(i + 1), + String.valueOf(totalUnits), + new String(unit.getFileName())})); } } catch (AbortCompilation e) { this.handleInternalException(e, unit); @@ -546,7 +548,7 @@ public class Compiler implements ITypeRequestor, ProblemSeverities { unit = unitsToProcess[0]; } else { // initial type binding creation - lookupEnvironment.buildTypeBindings(unit); + lookupEnvironment.buildTypeBindings(unit, null /*no access restriction*/); // binding resolution lookupEnvironment.completeTypeBindings(); diff --git a/src/org/eclipse/jdt/internal/compiler/ConfigurableOption.java b/src/org/eclipse/jdt/internal/compiler/ConfigurableOption.java deleted file mode 100644 index 13e2858..0000000 --- a/src/org/eclipse/jdt/internal/compiler/ConfigurableOption.java +++ /dev/null @@ -1,227 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2000, 2004 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Common Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/cpl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.jdt.internal.compiler; - -/** - * Generic option description, which can be modified independently from the - * component it belongs to. - * - * @deprecated backport 1.0 internal functionality - */ - -import java.util.*; - -public class ConfigurableOption { - private String componentName; - private String optionName; - private int id; - - private String category; - private String name; - private String description; - private int currentValueIndex; - private int defaultValueIndex; - private String[] possibleValues; - - // special value for indicating that - // the is the actual value - public final static String[] NoDiscreteValue = {}; -/** - * INTERNAL USE ONLY - * - * Initialize an instance of this class according to a specific locale - * - * @param loc java.util.Locale - */ -public ConfigurableOption( - String componentName, - String optionName, - Locale loc, - int currentValueIndex) { - - this.componentName = componentName; - this.optionName = optionName; - this.currentValueIndex = currentValueIndex; - - ResourceBundle resource = null; - try { - String location = componentName.substring(0, componentName.lastIndexOf('.')); - resource = ResourceBundle.getBundle(location + ".options", loc); //$NON-NLS-1$ - } catch (MissingResourceException e) { - category = "Missing ressources entries for" + componentName + " options"; //$NON-NLS-1$ //$NON-NLS-2$ - name = "Missing ressources entries for"+ componentName + " options"; //$NON-NLS-1$ //$NON-NLS-2$ - description = "Missing ressources entries for" + componentName + " options"; //$NON-NLS-1$ //$NON-NLS-2$ - possibleValues = new String[0]; - id = -1; - } - if (resource == null) return; - try { - id = Integer.parseInt(resource.getString(optionName + ".number")); //$NON-NLS-1$ - } catch (MissingResourceException e) { - id = -1; - } catch (NumberFormatException e) { - id = -1; - } - try { - category = resource.getString(optionName + ".category"); //$NON-NLS-1$ - } catch (MissingResourceException e) { - category = "Missing ressources entries for" + componentName + " options"; //$NON-NLS-1$ //$NON-NLS-2$ - } - try { - name = resource.getString(optionName + ".name"); //$NON-NLS-1$ - } catch (MissingResourceException e) { - name = "Missing ressources entries for"+ componentName + " options"; //$NON-NLS-1$ //$NON-NLS-2$ - } - try { - StringTokenizer tokenizer = new StringTokenizer(resource.getString(optionName + ".possibleValues"), "|"); //$NON-NLS-1$ //$NON-NLS-2$ - int numberOfValues = Integer.parseInt(tokenizer.nextToken()); - if(numberOfValues == -1){ - possibleValues = NoDiscreteValue; - } else { - possibleValues = new String[numberOfValues]; - int index = 0; - while (tokenizer.hasMoreTokens()) { - possibleValues[index] = tokenizer.nextToken(); - index++; - } - } - } catch (MissingResourceException e) { - possibleValues = new String[0]; - } catch (NoSuchElementException e) { - possibleValues = new String[0]; - } catch (NumberFormatException e) { - possibleValues = new String[0]; - } - try { - description = resource.getString(optionName + ".description"); //$NON-NLS-1$ - } catch (MissingResourceException e) { - description = "Missing ressources entries for"+ componentName + " options"; //$NON-NLS-1$ //$NON-NLS-2$ - } -} -/** - * Return a String that represents the localized category of the receiver. - * @return java.lang.String - */ -public String getCategory() { - return category; -} -/** - * Return a String that identifies the component owner (typically the qualified - * type name of the class which it corresponds to). - * - * e.g. "org.eclipse.jdt.internal.compiler.api.Compiler" - * - * @return java.lang.String - */ -public String getComponentName() { - return componentName; -} -/** - * Answer the index (in possibleValues array) of the current setting for this - * particular option. - * - * In case the set of possibleValues is NoDiscreteValue, then this index is the - * actual value (e.g. max line lenght set to 80). - * - * @return int - */ -public int getCurrentValueIndex() { - return currentValueIndex; -} -/** - * Answer the index (in possibleValues array) of the default setting for this - * particular option. - * - * In case the set of possibleValues is NoDiscreteValue, then this index is the - * actual value (e.g. max line lenght set to 80). - * - * @return int - */ -public int getDefaultValueIndex() { - return defaultValueIndex; -} -/** - * Return an String that represents the localized description of the receiver. - * - * @return java.lang.String - */ -public String getDescription() { - return description; -} -/** - * Internal ID which allows the configurable component to identify this particular option. - * - * @return int - */ -public int getID() { - return id; -} -/** - * Return a String that represents the localized name of the receiver. - * @return java.lang.String - */ -public String getName() { - return name; -} -/** - * Return an array of String that represents the localized possible values of the receiver. - * @return java.lang.String[] - */ -public String[] getPossibleValues() { - return possibleValues; -} -/** - * Change the index (in possibleValues array) of the current setting for this - * particular option. - * - * In case the set of possibleValues is NoDiscreteValue, then this index is the - * actual value (e.g. max line lenght set to 80). - */ -public void setValueIndex(int newIndex) { - currentValueIndex = newIndex; -} -public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append("Configurable option for "); //$NON-NLS-1$ - buffer.append(this.componentName).append("\n"); //$NON-NLS-1$ - buffer.append("- category: ").append(this.category).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ - buffer.append("- name: ").append(this.name).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ - /* display current value */ - buffer.append("- current value: "); //$NON-NLS-1$ - if (possibleValues == NoDiscreteValue){ - buffer.append(this.currentValueIndex); - } else { - buffer.append(this.possibleValues[this.currentValueIndex]); - } - buffer.append("\n"); //$NON-NLS-1$ - - /* display possible values */ - if (possibleValues != NoDiscreteValue){ - buffer.append("- possible values: ["); //$NON-NLS-1$ - for (int i = 0, max = possibleValues.length; i < max; i++) { - if (i != 0) - buffer.append(", "); //$NON-NLS-1$ - buffer.append(possibleValues[i]); - } - buffer.append("]\n"); //$NON-NLS-1$ - buffer.append("- curr. val. index: ").append(currentValueIndex).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ - } - buffer.append("- description: ").append(description).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ - return buffer.toString(); -} - /** - * Gets the optionName. - * @return Returns a String - */ - public String getOptionName() { - return optionName; - } -} diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ASTNode.java b/src/org/eclipse/jdt/internal/compiler/ast/ASTNode.java index 74ba90b..1457547 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ASTNode.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ASTNode.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import org.eclipse.jdt.internal.compiler.env.AccessRestriction; import org.eclipse.jdt.internal.compiler.impl.*; import org.eclipse.jdt.internal.compiler.lookup.*; import org.eclipse.jdt.internal.compiler.ASTVisitor; @@ -26,14 +27,14 @@ public abstract class ASTNode implements BaseTypes, CompilerModifiers, TypeConst public final static int Bit2 = 0x2; // return type (operator) | name reference kind (name ref) | has local type (type, method, field decl) public final static int Bit3 = 0x4; // return type (operator) | name reference kind (name ref) | implicit this (this ref) public final static int Bit4 = 0x8; // return type (operator) | first assignment to local (local decl) | undocumented empty block (block, type and method decl) - public final static int Bit5 = 0x10; // value for return (expression) | has all method bodies (unit) + public final static int Bit5 = 0x10; // value for return (expression) | has all method bodies (unit) | supertype ref (type ref) public final static int Bit6 = 0x20; // depth (name ref, msg) | only value required (binary expression) | ignore need cast check (cast expression) public final static int Bit7 = 0x40; // depth (name ref, msg) | operator (operator) | need runtime checkcast (cast expression) public final static int Bit8 = 0x80; // depth (name ref, msg) | operator (operator) public final static int Bit9 = 0x100; // depth (name ref, msg) | operator (operator) | is local type (type decl) public final static int Bit10= 0x200; // depth (name ref, msg) | operator (operator) | is anonymous type (type decl) public final static int Bit11 = 0x400; // depth (name ref, msg) | operator (operator) | is member type (type decl) - public final static int Bit12 = 0x800; // depth (name ref, msg) | operator (operator) + public final static int Bit12 = 0x800; // depth (name ref, msg) | operator (operator) | has abstract methods (type decl) public final static int Bit13 = 0x1000; // depth (name ref, msg) public final static int Bit14 = 0x2000; // strictly assigned (reference lhs) public final static int Bit15 = 0x4000; // is unnecessary cast (expression) @@ -64,6 +65,22 @@ public abstract class ASTNode implements BaseTypes, CompilerModifiers, TypeConst public final static long Bit38L = 0x2000000000L; public final static long Bit39L = 0x4000000000L; public final static long Bit40L = 0x8000000000L; + public final static long Bit41L = 0x10000000000L; + public final static long Bit42L = 0x20000000000L; + public final static long Bit43L = 0x40000000000L; + public final static long Bit44L = 0x80000000000L; + public final static long Bit45L = 0x100000000000L; + public final static long Bit46L = 0x200000000000L; + public final static long Bit47L = 0x400000000000L; + public final static long Bit48L = 0x800000000000L; + public final static long Bit49L = 0x1000000000000L; + public final static long Bit50L = 0x2000000000000L; + public final static long Bit51L = 0x4000000000000L; + public final static long Bit52L = 0x8000000000000L; + public final static long Bit53L = 0x10000000000000L; + public final static long Bit54L = 0x20000000000000L; + public final static long Bit55L = 0x40000000000000L; + public final static long Bit56L = 0x80000000000000L; public int bits = IsReachableMASK; // reachable by default @@ -102,6 +119,7 @@ public abstract class ASTNode implements BaseTypes, CompilerModifiers, TypeConst public static final int IsAnonymousTypeMASK = Bit10; // used to test for anonymous public static final int AnonymousAndLocalMask = IsAnonymousTypeMASK | IsLocalTypeMASK; // used to set anonymous marker public static final int IsMemberTypeMASK = Bit11; // local member do not know it is local at parse time (need to look at binding) + public static final int HasAbstractMethods = Bit12; // used to promote abstract enums // for type, method and field declarations public static final int HasLocalTypeMASK = Bit2; // cannot conflict with AddAssertionMASK @@ -132,15 +150,80 @@ public abstract class ASTNode implements BaseTypes, CompilerModifiers, TypeConst // for if statement public static final int IsElseIfStatement = Bit30; + // for type reference + public static final int IsSuperType = Bit5; + + // for variable argument + public static final int IsVarArgs = Bit15; + public ASTNode() { super(); } - + private static boolean checkInvocationArgument(BlockScope scope, Expression argument, TypeBinding parameterType, TypeBinding argumentType) { + argument.computeConversion(scope, parameterType, argumentType); + + if (argumentType != NullBinding && parameterType.isWildcard() && ((WildcardBinding) parameterType).kind != Wildcard.SUPER) + return true; // unsafeWildcardInvocation + if (argumentType != parameterType && argumentType.isRawType()) + if (parameterType.isBoundParameterizedType() || parameterType.isGenericType()) + scope.problemReporter().unsafeRawConversion(argument, argumentType, parameterType); + return false; + } + public static void checkInvocationArguments(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding method, Expression[] arguments, TypeBinding[] argumentTypes, boolean argsContainCast, InvocationSite invocationSite) { + boolean unsafeWildcardInvocation = false; + TypeBinding[] params = method.parameters; + if (method.isVarargs()) { + // 4 possibilities exist for a call to the vararg method foo(int i, long ... value) : foo(1), foo(1, 2), foo(1, 2, 3, 4) & foo(1, new long[] {1, 2}) + int lastIndex = params.length - 1; + for (int i = 0; i < lastIndex; i++) + if (checkInvocationArgument(scope, arguments[i], params[i], argumentTypes[i])) + unsafeWildcardInvocation = true; + int argLength = arguments.length; + if (lastIndex < argLength) { // vararg argument was provided + TypeBinding parameterType = params[lastIndex]; + if (params.length != argLength || parameterType.dimensions() != argumentTypes[lastIndex].dimensions()) + parameterType = ((ArrayBinding) parameterType).elementsType(); // single element was provided for vararg parameter + for (int i = lastIndex; i < argLength; i++) + if (checkInvocationArgument(scope, arguments[i], parameterType, argumentTypes[i])) + unsafeWildcardInvocation = true; + } + + if (method.parameters.length == argumentTypes.length) { // 70056 + int varargIndex = method.parameters.length - 1; + ArrayBinding varargType = (ArrayBinding) method.parameters[varargIndex]; + TypeBinding lastArgType = argumentTypes[varargIndex]; + if (lastArgType == NullBinding) { + if (!(varargType.leafComponentType().isBaseType() && varargType.dimensions() == 1)) + scope.problemReporter().varargsArgumentNeedCast(method, lastArgType, invocationSite); + } else if (varargType.dimensions <= lastArgType.dimensions()) { + int dimensions = lastArgType.dimensions(); + if (lastArgType.leafComponentType().isBaseType()) + dimensions--; + if (varargType.dimensions < dimensions) + scope.problemReporter().varargsArgumentNeedCast(method, lastArgType, invocationSite); + else if (varargType.dimensions == dimensions && varargType.leafComponentType != lastArgType.leafComponentType()) + scope.problemReporter().varargsArgumentNeedCast(method, lastArgType, invocationSite); + } + } + } else { + for (int i = 0, argLength = arguments.length; i < argLength; i++) + if (checkInvocationArgument(scope, arguments[i], params[i], argumentTypes[i])) + unsafeWildcardInvocation = true; + } + if (argsContainCast) { + CastExpression.checkNeedForArgumentCasts(scope, receiver, receiverType, method, arguments, argumentTypes, invocationSite); + } + if (unsafeWildcardInvocation) { + scope.problemReporter().wildcardInvocation((ASTNode)invocationSite, receiverType, method, argumentTypes); + } else if (!receiverType.isUnboundWildcard() && method.declaringClass.isRawType() && method.hasSubstitutedParameters()) { + scope.problemReporter().unsafeRawInvocation((ASTNode)invocationSite, method); + } + } public ASTNode concreteStatement() { return this; } - + /* Answer true if the field use is considered deprecated. * An access in the same compilation unit is allowed. */ @@ -173,7 +256,7 @@ public abstract class ASTNode implements BaseTypes, CompilerModifiers, TypeConst if (method.isPrivate() && !scope.isDefinedInMethod(method)) { // ignore cases where method is used from within inside itself (e.g. direct recursions) - method.modifiers |= AccPrivateUsed; + method.original().modifiers |= AccPrivateUsed; } if (!method.isViewedAsDeprecated()) return false; @@ -210,9 +293,15 @@ public abstract class ASTNode implements BaseTypes, CompilerModifiers, TypeConst if (refType.isPrivate() && !scope.isDefinedInType(refType)) { // ignore cases where type is used from within inside itself - refType.modifiers |= AccPrivateUsed; + ((ReferenceBinding)refType.erasure()).modifiers |= AccPrivateUsed; + } + + if (refType.hasRestrictedAccess()) { + AccessRestriction restriction = scope.environment().getAccessRestriction(type); + if (restriction != null) { + scope.problemReporter().forbiddenReference(type, this, restriction.getMessageTemplate()); + } } - if (!refType.isViewedAsDeprecated()) return false; // inside same unit - no report @@ -225,6 +314,15 @@ public abstract class ASTNode implements BaseTypes, CompilerModifiers, TypeConst public abstract StringBuffer print(int indent, StringBuffer output); + public static StringBuffer printAnnotations(Annotation[] annotations, StringBuffer output) { + int length = annotations.length; + for (int i = 0; i < length; i++) { + annotations[i].print(0, output); + output.append(" "); //$NON-NLS-1$ + } + return output; + } + public static StringBuffer printIndent(int indent, StringBuffer output) { for (int i = indent; i > 0; i--) output.append(" "); //$NON-NLS-1$ @@ -255,6 +353,68 @@ public abstract class ASTNode implements BaseTypes, CompilerModifiers, TypeConst output.append("abstract "); //$NON-NLS-1$ return output; } + + /** + * Resolve annotations, and check duplicates, answers combined tagBits + * for recognized standard annotations + */ + public void resolveAnnotations(BlockScope scope, Annotation[] annotations, Binding recipient) { + if (recipient != null) { + switch (recipient.kind()) { + case Binding.PACKAGE : + // TODO (philippe) need support for package annotations + break; + case Binding.TYPE : + case Binding.GENERIC_TYPE : + case Binding.TYPE_PARAMETER : + ReferenceBinding type = (ReferenceBinding) recipient; + if ((type.tagBits & TagBits.AnnotationResolved) != 0) return; + type.tagBits |= TagBits.AnnotationResolved; + break; + case Binding.METHOD : + MethodBinding method = (MethodBinding) recipient; + if ((method.tagBits & TagBits.AnnotationResolved) != 0) return; + method.tagBits |= TagBits.AnnotationResolved; + break; + case Binding.FIELD : + FieldBinding field = (FieldBinding) recipient; + if ((field.tagBits & TagBits.AnnotationResolved) != 0) return; + field.tagBits |= TagBits.AnnotationResolved; + break; + case Binding.LOCAL : + LocalVariableBinding local = (LocalVariableBinding) recipient; + if ((local.tagBits & TagBits.AnnotationResolved) != 0) return; + local.tagBits |= TagBits.AnnotationResolved; + break; + } + } + if (annotations == null) + return; + int length = annotations.length; + TypeBinding[] annotationTypes = new TypeBinding[length]; + for (int i = 0; i < length; i++) { + Annotation annotation = annotations[i]; + annotation.recipient = recipient; + annotationTypes[i] = annotation.resolveType(scope); + } + // check duplicate annotations + for (int i = 0; i < length; i++) { + TypeBinding annotationType = annotationTypes[i]; + if (annotationType == null) continue; + boolean foundDuplicate = false; + for (int j = i+1; j < length; j++) { + if (annotationTypes[j] == annotationType) { + foundDuplicate = true; + annotationTypes[j] = null; // report it only once + scope.problemReporter().duplicateAnnotation(annotations[j]); + } + } + if (foundDuplicate) { + scope.problemReporter().duplicateAnnotation(annotations[i]); + } + } + } + public int sourceStart() { return this.sourceStart; } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java b/src/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java index c25253e..985a97d 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java @@ -32,6 +32,7 @@ public abstract class AbstractMethodDeclaration public int declarationSourceEnd; public int modifiers; public int modifiersSourceStart; + public Annotation[] annotations; public Argument[] arguments; public TypeReference[] thrownExceptions; public Statement[] statements; @@ -83,7 +84,11 @@ public abstract class AbstractMethodDeclaration int length = this.arguments.length; for (int i = 0; i < length; i++) { TypeBinding argType = this.binding == null ? null : this.binding.parameters[i]; - this.arguments[i].bind(this.scope, argType, used); + Argument argument = this.arguments[i]; + argument.bind(this.scope, argType, used); + if (argument.annotations != null) { + this.binding.tagBits |= TagBits.HasParameterAnnotations; + } } } } @@ -187,7 +192,7 @@ public abstract class AbstractMethodDeclaration } } - private void generateCode(ClassFile classFile) { + public void generateCode(ClassFile classFile) { classFile.generateMethodInfoHeader(this.binding); int methodAttributeOffset = classFile.contentsOffset; @@ -258,6 +263,11 @@ public abstract class AbstractMethodDeclaration return (this.modifiers & AccAbstract) != 0; } + public boolean isAnnotationMethod() { + + return false; + } + public boolean isClinit() { return false; @@ -278,6 +288,11 @@ public abstract class AbstractMethodDeclaration return false; } + public boolean isMethod() { + + return false; + } + public boolean isNative() { if (this.binding != null) @@ -305,6 +320,20 @@ public abstract class AbstractMethodDeclaration printIndent(tab, output); printModifiers(this.modifiers, output); + if (this.annotations != null) printAnnotations(this.annotations, output); + + TypeParameter[] typeParams = typeParameters(); + if (typeParams != null) { + output.append('<');//$NON-NLS-1$ + int max = typeParams.length - 1; + for (int j = 0; j < max; j++) { + typeParams[j].print(0, output); + output.append(", ");//$NON-NLS-1$ + } + typeParams[max].print(0, output); + output.append('>'); + } + printReturnType(0, output).append(this.selector).append('('); if (this.arguments != null) { for (int i = 0; i < this.arguments.length; i++) { @@ -356,6 +385,7 @@ public abstract class AbstractMethodDeclaration bindArguments(); bindThrownExceptions(); resolveJavadoc(); + resolveAnnotations(scope, this.annotations, this.binding); resolveStatements(); } catch (AbortMethod e) { // ========= abort on fatal error ============= this.ignoreFurtherInvestigation = true; @@ -395,4 +425,8 @@ public abstract class AbstractMethodDeclaration ClassScope classScope) { // default implementation: subclass will define it } + + public TypeParameter[] typeParameters() { + return null; + } } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/AbstractVariableDeclaration.java b/src/org/eclipse/jdt/internal/compiler/ast/AbstractVariableDeclaration.java index cce62fc..ec08039 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/AbstractVariableDeclaration.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/AbstractVariableDeclaration.java @@ -15,6 +15,7 @@ import org.eclipse.jdt.internal.compiler.flow.FlowInfo; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.InvocationSite; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; public abstract class AbstractVariableDeclaration extends Statement implements InvocationSite { public int declarationEnd; @@ -24,6 +25,7 @@ public abstract class AbstractVariableDeclaration extends Statement implements I public Expression initialization; public int modifiers; public int modifiersSourceStart; + public Annotation[] annotations; public char[] name; @@ -32,6 +34,27 @@ public abstract class AbstractVariableDeclaration extends Statement implements I public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { return flowInfo; } + + public static final int FIELD = 1; + public static final int INITIALIZER = 2; + public static final int ENUM_CONSTANT = 3; + public static final int LOCAL_VARIABLE = 4; + public static final int PARAMETER = 5; + public static final int TYPE_PARAMETER = 6; + + + /** + * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments() + */ + public TypeBinding[] genericTypeArguments() { + return null; + } + + /** + * Returns the constant kind of this variable declaration + */ + public abstract int getKind(); + /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#isSuperAccess() */ @@ -46,17 +69,29 @@ public abstract class AbstractVariableDeclaration extends Statement implements I return false; } - public StringBuffer printStatement(int indent, StringBuffer output) { printIndent(indent, output); printModifiers(this.modifiers, output); - type.print(0, output).append(' ').append(this.name); - if (initialization != null) { - output.append(" = "); //$NON-NLS-1$ - initialization.printExpression(indent, output); + if (this.annotations != null) printAnnotations(this.annotations, output); + + if (type != null) { + type.print(0, output).append(' '); + } + output.append(this.name); + switch(getKind()) { + case ENUM_CONSTANT: + if (initialization != null) { + initialization.printExpression(indent, output); + } + return output.append(','); + default: + if (initialization != null) { + output.append(" = "); //$NON-NLS-1$ + initialization.printExpression(indent, output); + } + return output.append(';'); } - return output.append(';'); } public void resolve(BlockScope scope) { diff --git a/src/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java b/src/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java index 2a47d4d..e631f12 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java @@ -15,15 +15,16 @@ import org.eclipse.jdt.internal.compiler.codegen.*; import org.eclipse.jdt.internal.compiler.flow.*; import org.eclipse.jdt.internal.compiler.lookup.*; -public class AllocationExpression - extends Expression - implements InvocationSite { +public class AllocationExpression extends Expression implements InvocationSite { public TypeReference type; public Expression[] arguments; - public MethodBinding binding; - - MethodBinding syntheticAccessor; + public MethodBinding binding; // exact binding resulting from lookup + protected MethodBinding codegenBinding; // actual binding used for code generation (if no synthetic accessor) + MethodBinding syntheticAccessor; // synthetic accessor for inner-emulation + public TypeReference[] typeArguments; + public TypeBinding[] genericTypeArguments; + public FieldDeclaration enumConstant; // for enum constant initializations public FlowInfo analyseCode( BlockScope currentScope, @@ -31,7 +32,7 @@ public class AllocationExpression FlowInfo flowInfo) { // check captured variables are initialized in current context (26134) - checkCapturedLocalInitializationIfNecessary(this.binding.declaringClass, currentScope, flowInfo); + checkCapturedLocalInitializationIfNecessary((ReferenceBinding)this.binding.declaringClass.erasure(), currentScope, flowInfo); // process arguments if (arguments != null) { @@ -74,7 +75,6 @@ public class AllocationExpression currentScope.problemReporter().uninitializedLocalVariable(targetLocal, this); } } - } } @@ -88,14 +88,20 @@ public class AllocationExpression boolean valueRequired) { int pc = codeStream.position; - ReferenceBinding allocatedType = binding.declaringClass; + ReferenceBinding allocatedType = this.codegenBinding.declaringClass; codeStream.new_(allocatedType); if (valueRequired) { codeStream.dup(); } // better highlight for allocation: display the type individually - codeStream.recordPositionsFrom(pc, type.sourceStart); + if (this.type != null) { // null for enum constant body + codeStream.recordPositionsFrom(pc, this.type.sourceStart); + } else { + // push enum constant name and ordinal + codeStream.ldc(String.valueOf(enumConstant.name)); + codeStream.generateInlinedValue(enumConstant.binding.id); + } // handling innerclass instance allocation - enclosing instance arguments if (allocatedType.isNestedType()) { @@ -106,11 +112,7 @@ public class AllocationExpression this); } // generate the arguments for constructor - if (arguments != null) { - for (int i = 0, count = arguments.length; i < count; i++) { - arguments[i].generateCode(currentScope, codeStream, true); - } - } + generateArguments(binding, arguments, currentScope, codeStream); // handling innerclass instance allocation - outer local arguments if (allocatedType.isNestedType()) { codeStream.generateSyntheticOuterArgumentValues( @@ -120,20 +122,27 @@ public class AllocationExpression } // invoke constructor if (syntheticAccessor == null) { - codeStream.invokespecial(binding); + codeStream.invokespecial(this.codegenBinding); } else { // synthetic accessor got some extra arguments appended to its signature, which need values for (int i = 0, - max = syntheticAccessor.parameters.length - binding.parameters.length; + max = syntheticAccessor.parameters.length - this.codegenBinding.parameters.length; i < max; i++) { codeStream.aconst_null(); } codeStream.invokespecial(syntheticAccessor); } + codeStream.generateImplicitConversion(this.implicitConversion); codeStream.recordPositionsFrom(pc, this.sourceStart); } - + /** + * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments() + */ + public TypeBinding[] genericTypeArguments() { + return this.genericTypeArguments; + } + public boolean isSuperAccess() { return false; @@ -154,18 +163,18 @@ public class AllocationExpression public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { if (!flowInfo.isReachable()) return; - ReferenceBinding allocatedType; + ReferenceBinding allocatedTypeErasure = (ReferenceBinding) binding.declaringClass.erasure(); // perform some emulation work in case there is some and we are inside a local type only - if ((allocatedType = binding.declaringClass).isNestedType() + if (allocatedTypeErasure.isNestedType() && currentScope.enclosingSourceType().isLocalType()) { - if (allocatedType.isLocalType()) { - ((LocalTypeBinding) allocatedType).addInnerEmulationDependent(currentScope, false); + if (allocatedTypeErasure.isLocalType()) { + ((LocalTypeBinding) allocatedTypeErasure).addInnerEmulationDependent(currentScope, false); // request cascade of accesses } else { // locally propagate, since we already now the desired shape for sure - currentScope.propagateInnerEmulation(allocatedType, false); + currentScope.propagateInnerEmulation(allocatedTypeErasure, false); // request cascade of accesses } } @@ -174,27 +183,42 @@ public class AllocationExpression public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { if (!flowInfo.isReachable()) return; - if (binding.isPrivate() - && (currentScope.enclosingSourceType() != binding.declaringClass)) { - - if (currentScope - .environment() - .options - .isPrivateConstructorAccessChangingVisibility) { - binding.tagForClearingPrivateModifier(); + + // if constructor from parameterized type got found, use the original constructor at codegen time + this.codegenBinding = this.binding.original(); + + if (this.codegenBinding.isPrivate() + && (currentScope.enclosingSourceType() != this.codegenBinding.declaringClass)) { + + if (currentScope.environment().options.isPrivateConstructorAccessChangingVisibility) { + this.codegenBinding.tagForClearingPrivateModifier(); // constructor will not be dumped as private, no emulation required thus } else { syntheticAccessor = - ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, isSuperAccess()); - currentScope.problemReporter().needToEmulateMethodAccess(binding, this); + ((SourceTypeBinding) this.codegenBinding.declaringClass).addSyntheticMethod(this.codegenBinding, isSuperAccess()); + currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this); } } } public StringBuffer printExpression(int indent, StringBuffer output) { - output.append("new "); //$NON-NLS-1$ - type.printExpression(0, output); + if (this.type != null) { // type null for enum constant initializations + output.append("new "); //$NON-NLS-1$ + } + if (typeArguments != null) { + output.append('<');//$NON-NLS-1$ + int max = typeArguments.length - 1; + for (int j = 0; j < max; j++) { + typeArguments[j].print(0, output); + output.append(", ");//$NON-NLS-1$ + } + typeArguments[max].print(0, output); + output.append('>'); + } + if (type != null) { // type null for enum constant initializations + type.printExpression(0, output); + } output.append('('); if (arguments != null) { for (int i = 0; i < arguments.length; i++) { @@ -209,9 +233,29 @@ public class AllocationExpression // Propagate the type checking to the arguments, and check if the constructor is defined. constant = NotAConstant; - this.resolvedType = type.resolveType(scope); + if (this.type == null) { + // initialization of an enum constant + this.resolvedType = scope.enclosingSourceType(); + } else { + this.resolvedType = this.type.resolveType(scope, true /* check bounds*/); + } // will check for null after args are resolved + // resolve type arguments (for generic constructor call) + if (this.typeArguments != null) { + int length = this.typeArguments.length; + boolean argHasError = false; // typeChecks all arguments + this.genericTypeArguments = new TypeBinding[length]; + for (int i = 0; i < length; i++) { + if ((this.genericTypeArguments[i] = this.typeArguments[i].resolveType(scope, true /* check bounds*/)) == null) { + argHasError = true; + } + } + if (argHasError) { + return null; + } + } + // buffering the arguments' types boolean argsContainCast = false; TypeBinding[] argumentTypes = NoParameters; @@ -236,13 +280,13 @@ public class AllocationExpression if (this.resolvedType == null) return null; - if (!this.resolvedType.canBeInstantiated()) { + // null type denotes fake allocation for enum constant inits + if (this.type != null && !this.resolvedType.canBeInstantiated()) { scope.problemReporter().cannotInstantiate(type, this.resolvedType); return this.resolvedType; } ReferenceBinding allocationType = (ReferenceBinding) this.resolvedType; - if (!(binding = scope.getConstructor(allocationType, argumentTypes, this)) - .isValidBinding()) { + if (!(binding = scope.getConstructor(allocationType, argumentTypes, this)).isValidBinding()) { if (binding.declaringClass == null) binding.declaringClass = allocationType; scope.problemReporter().invalidConstructor(this, binding); @@ -250,15 +294,9 @@ public class AllocationExpression } if (isMethodUseDeprecated(binding, scope)) scope.problemReporter().deprecatedMethod(binding, this); + if (this.arguments != null) + checkInvocationArguments(scope, null, allocationType, this.binding, this.arguments, argumentTypes, argsContainCast, this); - if (arguments != null) { - for (int i = 0; i < arguments.length; i++) { - arguments[i].implicitWidening(binding.parameters[i], argumentTypes[i]); - } - if (argsContainCast) { - CastExpression.checkNeedForArgumentCasts(scope, null, allocationType, binding, this.arguments, argumentTypes, this); - } - } return allocationType; } @@ -277,12 +315,17 @@ public class AllocationExpression public void traverse(ASTVisitor visitor, BlockScope scope) { if (visitor.visit(this, scope)) { - int argumentsLength; - type.traverse(visitor, scope); - if (arguments != null) { - argumentsLength = arguments.length; - for (int i = 0; i < argumentsLength; i++) - arguments[i].traverse(visitor, scope); + if (this.typeArguments != null) { + for (int i = 0, typeArgumentsLength = this.typeArguments.length; i < typeArgumentsLength; i++) { + this.typeArguments[i].traverse(visitor, scope); + } + } + if (this.type != null) { // enum constant scenario + this.type.traverse(visitor, scope); + } + if (this.arguments != null) { + for (int i = 0, argumentsLength = this.arguments.length; i < argumentsLength; i++) + this.arguments[i].traverse(visitor, scope); } } visitor.endVisit(this, scope); diff --git a/src/org/eclipse/jdt/internal/compiler/ast/Annotation.java b/src/org/eclipse/jdt/internal/compiler/ast/Annotation.java new file mode 100644 index 0000000..fb31f38 --- /dev/null +++ b/src/org/eclipse/jdt/internal/compiler/ast/Annotation.java @@ -0,0 +1,299 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.ast; + +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.lookup.*; + +/** + * Annotation + */ +public abstract class Annotation extends Expression { + + public TypeReference type; + public int declarationSourceEnd; + public Binding recipient; + + final static MemberValuePair[] NoValuePairs = new MemberValuePair[0]; + + public static long getRetentionPolicy(char[] policyName) { + if (policyName == null || policyName.length == 0) + return 0; + switch(policyName[0]) { + case 'C' : + if (CharOperation.equals(policyName, TypeConstants.UPPER_CLASS)) + return TagBits.AnnotationClassRetention; + break; + case 'S' : + if (CharOperation.equals(policyName, TypeConstants.UPPER_SOURCE)) + return TagBits.AnnotationSourceRetention; + break; + case 'R' : + if (CharOperation.equals(policyName, TypeConstants.UPPER_RUNTIME)) + return TagBits.AnnotationRuntimeRetention; + break; + } + return 0; // unknown + } + + public static long getTargetElementType(char[] elementName) { + if (elementName == null || elementName.length == 0) + return 0; + switch(elementName[0]) { + case 'A' : + if (CharOperation.equals(elementName, TypeConstants.UPPER_ANNOTATION_TYPE)) + return TagBits.AnnotationForAnnotationType; + break; + case 'C' : + if (CharOperation.equals(elementName, TypeConstants.UPPER_CONSTRUCTOR)) + return TagBits.AnnotationForConstructor; + break; + case 'F' : + if (CharOperation.equals(elementName, TypeConstants.UPPER_FIELD)) + return TagBits.AnnotationForField; + break; + case 'L' : + if (CharOperation.equals(elementName, TypeConstants.UPPER_LOCAL_VARIABLE)) + return TagBits.AnnotationForLocalVariable; + break; + case 'M' : + if (CharOperation.equals(elementName, TypeConstants.UPPER_METHOD)) + return TagBits.AnnotationForMethod; + break; + case 'P' : + if (CharOperation.equals(elementName, TypeConstants.UPPER_PARAMETER)) + return TagBits.AnnotationForParameter; + else if (CharOperation.equals(elementName, TypeConstants.UPPER_PACKAGE)) + return TagBits.AnnotationForPackage; + break; + case 'T' : + if (CharOperation.equals(elementName, TypeConstants.TYPE)) + return TagBits.AnnotationForType; + break; + } + return 0; // unknown + } + + /** + * Compute the bit pattern for recognized standard annotations the compiler may need to act upon + */ + private long detectStandardAnnotation(Scope scope, ReferenceBinding annotationType, MemberValuePair valueAttribute) { + long tagBits = 0; + switch (annotationType.id) { + // retention annotation + case TypeIds.T_JavaLangAnnotationRetention : + if (valueAttribute != null) { + Expression expr = valueAttribute.value; + if ((expr.bits & Binding.VARIABLE) == Binding.FIELD) { + FieldBinding field = ((Reference)expr).fieldBinding(); + if (field != null && field.declaringClass.id == T_JavaLangAnnotationRetentionPolicy) { + tagBits |= getRetentionPolicy(field.name); + } + } + } + break; + // target annotation + case TypeIds.T_JavaLangAnnotationTarget : + tagBits |= TagBits.AnnotationTarget; // target specified (could be empty) + if (valueAttribute != null) { + Expression expr = valueAttribute.value; + if (expr instanceof ArrayInitializer) { + ArrayInitializer initializer = (ArrayInitializer) expr; + final Expression[] expressions = initializer.expressions; + if (expressions != null) { + for (int i = 0, length = expressions.length; i < length; i++) { + Expression initExpr = expressions[i]; + if ((initExpr.bits & Binding.VARIABLE) == Binding.FIELD) { + FieldBinding field = ((Reference) initExpr).fieldBinding(); + if (field != null && field.declaringClass.id == T_JavaLangAnnotationElementType) { + long element = getTargetElementType(field.name); + if ((tagBits & element) != 0) { + scope.problemReporter().duplicateTargetInTargetAnnotation(annotationType, (NameReference)initExpr); + } else { + tagBits |= element; + } + } + } + } + } + } else if ((expr.bits & Binding.VARIABLE) == Binding.FIELD) { + FieldBinding field = ((Reference) expr).fieldBinding(); + if (field != null && field.declaringClass.id == T_JavaLangAnnotationElementType) { + tagBits |= getTargetElementType(field.name); + } + } + } + break; + // marker annotations + case TypeIds.T_JavaLangDeprecated : + tagBits |= TagBits.AnnotationDeprecated; + break; + case TypeIds.T_JavaLangAnnotationDocumented : + tagBits |= TagBits.AnnotationDocumented; + break; + case TypeIds.T_JavaLangAnnotationInherited : + tagBits |= TagBits.AnnotationInherited; + break; + case TypeIds.T_JavaLangOverride : + tagBits |= TagBits.AnnotationOverride; + break; + case TypeIds.T_JavaLangSuppressWarnings : + tagBits |= TagBits.AnnotationSuppressWarnings; + break; + } + return tagBits; + } + + public StringBuffer printExpression(int indent, StringBuffer output) { + output.append('@'); + this.type.printExpression(0, output); + return output; + } + + public abstract MemberValuePair[] memberValuePairs(); + + public TypeBinding resolveType(BlockScope scope) { + + this.constant = NotAConstant; + + TypeBinding typeBinding = this.type.resolveType(scope); + if (typeBinding == null) + return null; + this.resolvedType = typeBinding; + // ensure type refers to an annotation type + if (!typeBinding.isAnnotationType()) { + scope.problemReporter().typeMismatchError(typeBinding, scope.getJavaLangAnnotationAnnotation(), this.type); + return null; + } + + ReferenceBinding annotationType = (ReferenceBinding) this.resolvedType; + MethodBinding[] methods = annotationType.methods(); + // clone valuePairs to keep track of unused ones + MemberValuePair[] valuePairs = memberValuePairs(); + MemberValuePair valueAttribute = null; // remember the first 'value' pair + MemberValuePair[] usedValuePairs; + int pairsLength = valuePairs.length; + System.arraycopy(valuePairs, 0, usedValuePairs = new MemberValuePair[pairsLength], 0, pairsLength); + + nextMember: for (int i = 0, requiredLength = methods.length; i < requiredLength; i++) { + MethodBinding method = methods[i]; + char[] selector = method.selector; + boolean foundValue = false; + nextPair: for (int j = 0; j < pairsLength; j++) { + MemberValuePair valuePair = usedValuePairs[j]; + if (valuePair == null) continue nextPair; + char[] memberName = valuePair.name; + if (CharOperation.equals(memberName, selector)) { + if (valueAttribute == null && CharOperation.equals(memberName, TypeConstants.VALUE)) { + valueAttribute = valuePair; + } + valuePair.binding = method; + usedValuePairs[j] = null; // consumed + foundValue = true; + boolean foundDuplicate = false; + for (int k = j+1; k < pairsLength; k++) { + if (CharOperation.equals(usedValuePairs[k].name, selector)) { + foundDuplicate = true; + scope.problemReporter().duplicateAnnotationValue(annotationType, usedValuePairs[k]); + usedValuePairs[k].binding = method; + usedValuePairs[k] = null; + } + } + if (foundDuplicate) { + scope.problemReporter().duplicateAnnotationValue(annotationType, valuePair); + continue nextMember; + } + valuePair.resolveTypeExpecting(scope, method.returnType); + } + } + if (!foundValue && (method.modifiers & AccAnnotationDefault) == 0) { + scope.problemReporter().missingValueForAnnotationMember(this, method.selector); + } + } + // check unused pairs + for (int i = 0; i < pairsLength; i++) { + if (usedValuePairs[i] != null) { + scope.problemReporter().undefinedAnnotationValue(annotationType, usedValuePairs[i]); + } + } + // recognize standard annotations ? + long tagBits = detectStandardAnnotation(scope, annotationType, valueAttribute); + if (this.recipient != null) { + if (tagBits != 0) { + // tag bits onto recipient + switch (this.recipient.kind()) { + case Binding.PACKAGE : + // TODO (philippe) need support for package annotations + break; + case Binding.TYPE : + case Binding.GENERIC_TYPE : + case Binding.TYPE_PARAMETER : + ((ReferenceBinding)this.recipient).tagBits |= tagBits; + break; + case Binding.METHOD : + ((MethodBinding)this.recipient).tagBits |= tagBits; + break; + case Binding.FIELD : + ((FieldBinding)this.recipient).tagBits |= tagBits; + break; + case Binding.LOCAL : + ((LocalVariableBinding)this.recipient).tagBits |= tagBits; + break; + } + } + // check (meta)target compatibility + checkTargetCompatibility: { + long metaTagBits = annotationType.getAnnotationTagBits(); + if ((metaTagBits & TagBits.AnnotationTargetMASK) == 0) // does not specify any target restriction + break checkTargetCompatibility; + + switch (recipient.kind()) { + case Binding.PACKAGE : + if ((metaTagBits & TagBits.AnnotationForPackage) != 0) + break checkTargetCompatibility; + break; + case Binding.TYPE : + case Binding.GENERIC_TYPE : + if (((ReferenceBinding)this.recipient).isAnnotationType()) { + if ((metaTagBits & (TagBits.AnnotationForAnnotationType|TagBits.AnnotationForType)) != 0) + break checkTargetCompatibility; + } else if ((metaTagBits & TagBits.AnnotationForType) != 0) + break checkTargetCompatibility; + break; + case Binding.METHOD : + if (((MethodBinding)this.recipient).isConstructor()) { + if ((metaTagBits & TagBits.AnnotationForConstructor) != 0) + break checkTargetCompatibility; + } else if ((metaTagBits & TagBits.AnnotationForMethod) != 0) + break checkTargetCompatibility; + break; + case Binding.FIELD : + if ((metaTagBits & TagBits.AnnotationForField) != 0) + break checkTargetCompatibility; + break; + case Binding.LOCAL : + if (((LocalVariableBinding)this.recipient).isArgument) { + if ((metaTagBits & TagBits.AnnotationForParameter) != 0) + break checkTargetCompatibility; + } else if ((annotationType.tagBits & TagBits.AnnotationForLocalVariable) != 0) + break checkTargetCompatibility; + break; + } + scope.problemReporter().disallowedTargetForAnnotation(this); + } + } + return this.resolvedType; + } + + public abstract void traverse(ASTVisitor visitor, BlockScope scope); + public abstract void traverse(ASTVisitor visitor, CompilationUnitScope scope); +} diff --git a/src/org/eclipse/jdt/internal/compiler/ast/AnnotationMethodDeclaration.java b/src/org/eclipse/jdt/internal/compiler/ast/AnnotationMethodDeclaration.java new file mode 100644 index 0000000..c7ef29f --- /dev/null +++ b/src/org/eclipse/jdt/internal/compiler/ast/AnnotationMethodDeclaration.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.ast; + +import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.ClassFile; +import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.parser.Parser; + +public class AnnotationMethodDeclaration extends MethodDeclaration { + + public Expression defaultValue; + public int extendedDimensions; + + /** + * MethodDeclaration constructor comment. + */ + public AnnotationMethodDeclaration(CompilationResult compilationResult) { + super(compilationResult); + } + + public void generateCode(ClassFile classFile) { + classFile.generateMethodInfoHeader(this.binding); + int methodAttributeOffset = classFile.contentsOffset; + int attributeNumber = classFile.generateMethodInfoAttribute(this.binding, this); + classFile.completeMethodInfo(methodAttributeOffset, attributeNumber); + } + + public boolean isAnnotationMethod() { + + return true; + } + + public boolean isMethod() { + + return false; + } + + public void parseStatements(Parser parser, CompilationUnitDeclaration unit) { + // nothing to do + // annotation type member declaration don't have any body + } + + public StringBuffer print(int tab, StringBuffer output) { + + printIndent(tab, output); + printModifiers(this.modifiers, output); + if (this.annotations != null) printAnnotations(this.annotations, output); + + TypeParameter[] typeParams = typeParameters(); + if (typeParams != null) { + output.append('<');//$NON-NLS-1$ + int max = typeParams.length - 1; + for (int j = 0; j < max; j++) { + typeParams[j].print(0, output); + output.append(", ");//$NON-NLS-1$ + } + typeParams[max].print(0, output); + output.append('>'); + } + + printReturnType(0, output).append(this.selector).append('('); + if (this.arguments != null) { + for (int i = 0; i < this.arguments.length; i++) { + if (i > 0) output.append(", "); //$NON-NLS-1$ + this.arguments[i].print(0, output); + } + } + output.append(')'); + if (this.thrownExceptions != null) { + output.append(" throws "); //$NON-NLS-1$ + for (int i = 0; i < this.thrownExceptions.length; i++) { + if (i > 0) output.append(", "); //$NON-NLS-1$ + this.thrownExceptions[i].print(0, output); + } + } + + if (this.defaultValue != null) { + output.append(" default "); //$NON-NLS-1$ + this.defaultValue.print(0, output); + } + + printBody(tab + 1, output); + return output; + } + + public void resolveStatements() { + + super.resolveStatements(); + if (this.arguments != null) { + scope.problemReporter().annotationMembersCannotHaveParameters(this); + } + if (this.typeParameters != null) { + scope.problemReporter().annotationMembersCannotHaveTypeParameters(this); + } + if (this.extendedDimensions != 0) { + scope.problemReporter().illegalExtendedDimensions(this); + } + if (this.binding == null) return; + TypeBinding returnTypeBinding = this.binding.returnType; + if (returnTypeBinding != null) { + + // annotation methods can only return base types, String, Class, enum type, annotation types and arrays of these + checkAnnotationMethodType: { + TypeBinding leafReturnType = returnTypeBinding.leafComponentType(); + + switch (leafReturnType.erasure().id) { + case T_byte : + case T_short : + case T_char : + case T_int : + case T_long : + case T_float : + case T_double : + case T_boolean : + case T_JavaLangString : + case T_JavaLangClass : + if (returnTypeBinding.dimensions() <= 1) // only 1-dimensional array permitted + break checkAnnotationMethodType; + } + if (leafReturnType.isEnum()) { + if (returnTypeBinding.dimensions() <= 1) // only 1-dimensional array permitted + break checkAnnotationMethodType; + } + if (leafReturnType.isAnnotationType()) { + scope.classScope().detectAnnotationCycle(scope.enclosingSourceType(), leafReturnType, this.returnType); + if (returnTypeBinding.dimensions() <= 1) // only 1-dimensional array permitted + break checkAnnotationMethodType; + } + scope.problemReporter().invalidAnnotationMemberType(this); + } + if (this.defaultValue != null) { + MemberValuePair pair = new MemberValuePair(this.selector, this.sourceStart, this.sourceEnd, this.defaultValue); + pair.binding = this.binding; + pair.resolveTypeExpecting(scope, returnTypeBinding); + } + } + } + + public void traverse( + ASTVisitor visitor, + ClassScope classScope) { + + if (visitor.visit(this, classScope)) { + if (this.annotations != null) { + int annotationsLength = this.annotations.length; + for (int i = 0; i < annotationsLength; i++) + this.annotations[i].traverse(visitor, scope); + } + if (this.returnType != null) { + this.returnType.traverse(visitor, scope); + } + if (this.defaultValue != null) { + this.defaultValue.traverse(visitor, scope); + } + } + visitor.endVisit(this, classScope); + } +} diff --git a/src/org/eclipse/jdt/internal/compiler/ast/Argument.java b/src/org/eclipse/jdt/internal/compiler/ast/Argument.java index b6669ac..4a75e07 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/Argument.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/Argument.java @@ -18,7 +18,7 @@ public class Argument extends LocalDeclaration { // prefix for setter method (to recognize special hiding argument) private final static char[] SET = "set".toCharArray(); //$NON-NLS-1$ - + public Argument(char[] name, long posNom, TypeReference tr, int modifiers) { super(name, (int) (posNom >>> 32), (int) posNom); @@ -31,11 +31,11 @@ public class Argument extends LocalDeclaration { public void bind(MethodScope scope, TypeBinding typeBinding, boolean used) { if (this.type != null) - this.type.resolvedType = typeBinding; + this.type.resolvedType = typeBinding; // TODO (philippe) no longer necessary as when binding got resolved, it was recorded already (SourceTypeBinding#resolveTypesFor(MethodBinding)) // record the resolved type into the type reference int modifierFlag = this.modifiers; - Binding existingVariable = scope.getBinding(name, BindingIds.VARIABLE, this, false /*do not resolve hidden field*/); + Binding existingVariable = scope.getBinding(name, Binding.VARIABLE, this, false /*do not resolve hidden field*/); if (existingVariable != null && existingVariable.isValidBinding()){ if (existingVariable instanceof LocalVariableBinding && this.hiddenVariableDepth == 0) { scope.problemReporter().redefineArgument(this); @@ -58,17 +58,29 @@ public class Argument extends LocalDeclaration { scope.addLocalVariable( this.binding = new LocalVariableBinding(this, typeBinding, modifierFlag, true)); + resolveAnnotations(scope, this.annotations, this.binding); //true stand for argument instead of just local - if (typeBinding != null && isTypeUseDeprecated(typeBinding, scope)) - scope.problemReporter().deprecatedType(typeBinding, this.type); this.binding.declaration = this; this.binding.useFlag = used ? LocalVariableBinding.USED : LocalVariableBinding.UNUSED; } + /** + * @see org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration#getKind() + */ + public int getKind() { + return PARAMETER; + } + + public boolean isVarArgs() { + return (this.type.bits & IsVarArgs) != 0; + } + public StringBuffer print(int indent, StringBuffer output) { printIndent(indent, output); printModifiers(this.modifiers, output); + if (this.annotations != null) printAnnotations(this.annotations, output); + if (type == null) { output.append(" "); //$NON-NLS-1$ } else { @@ -88,11 +100,23 @@ public class Argument extends LocalDeclaration { // provide the scope with a side effect : insertion of a LOCAL // that represents the argument. The type must be from JavaThrowable - TypeBinding tb = type.resolveTypeExpecting(scope, scope.getJavaLangThrowable()); - if (tb == null) + TypeBinding exceptionType = this.type.resolveType(scope, true /* check bounds*/); + if (exceptionType == null) return null; + if (exceptionType.isGenericType() || exceptionType.isParameterizedType()) { + scope.problemReporter().invalidParameterizedExceptionType(exceptionType, this); return null; - - Binding existingVariable = scope.getBinding(name, BindingIds.VARIABLE, this, false /*do not resolve hidden field*/); + } + if (exceptionType.isTypeVariable()) { + scope.problemReporter().invalidTypeVariableAsException(exceptionType, this); + return null; + } + TypeBinding throwable = scope.getJavaLangThrowable(); + if (!exceptionType.isCompatibleWith(throwable)) { + scope.problemReporter().typeMismatchError(exceptionType, throwable, this); + return null; + } + + Binding existingVariable = scope.getBinding(name, Binding.VARIABLE, this, false /*do not resolve hidden field*/); if (existingVariable != null && existingVariable.isValidBinding()){ if (existingVariable instanceof LocalVariableBinding && this.hiddenVariableDepth == 0) { scope.problemReporter().redefineArgument(this); @@ -101,10 +125,12 @@ public class Argument extends LocalDeclaration { scope.problemReporter().localVariableHiding(this, existingVariable, false); } - binding = new LocalVariableBinding(this, tb, modifiers, false); // argument decl, but local var (where isArgument = false) + this.binding = new LocalVariableBinding(this, exceptionType, modifiers, false); // argument decl, but local var (where isArgument = false) + resolveAnnotations(scope, this.annotations, this.binding); + scope.addLocalVariable(binding); - binding.constant = NotAConstant; - return tb; + binding.setConstant(NotAConstant); + return exceptionType; } public void traverse(ASTVisitor visitor, BlockScope scope) { diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ArrayAllocationExpression.java b/src/org/eclipse/jdt/internal/compiler/ast/ArrayAllocationExpression.java index d9ab28d..d1862ff 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ArrayAllocationExpression.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ArrayAllocationExpression.java @@ -73,7 +73,7 @@ public class ArrayAllocationExpression extends Expression { // Generate a sequence of bytecodes corresponding to an array allocation if (this.resolvedType.dimensions() == 1) { // Mono-dimensional array - codeStream.newArray(currentScope, (ArrayBinding)this.resolvedType); + codeStream.newArray((ArrayBinding)this.resolvedType); } else { // Multi-dimensional array codeStream.multianewarray(this.resolvedType, nonNullDimensionsLength); @@ -113,7 +113,7 @@ public class ArrayAllocationExpression extends Expression { // only at the -end- like new int [4][][]. The parser allows new int[][4][] // so this must be checked here......(this comes from a reduction to LL1 grammar) - TypeBinding referenceType = type.resolveType(scope); + TypeBinding referenceType = type.resolveType(scope, true /* check bounds*/); // will check for null after dimensions are checked constant = Constant.NotAConstant; @@ -124,12 +124,13 @@ public class ArrayAllocationExpression extends Expression { // check the validity of the dimension syntax (and test for all null dimensions) int explicitDimIndex = -1; - for (int i = dimensions.length; --i >= 0;) { + loop: for (int i = dimensions.length; --i >= 0;) { if (dimensions[i] != null) { if (explicitDimIndex < 0) explicitDimIndex = i; - } else if (explicitDimIndex> 0) { + } else if (explicitDimIndex > 0) { // should not have an empty dimension before an non-empty one - scope.problemReporter().incorrectLocationForEmptyDimension(this, i); + scope.problemReporter().incorrectLocationForNonEmptyDimension(this, explicitDimIndex); + break loop; } } @@ -139,6 +140,10 @@ public class ArrayAllocationExpression extends Expression { if (explicitDimIndex < 0) { scope.problemReporter().mustDefineDimensionsOrInitializer(this); } + // allow new List[5] - only check for generic array when no initializer, since also checked inside initializer resolution + if (referenceType != null && !referenceType.isReifiable()) { + scope.problemReporter().illegalGenericArray(referenceType, this); + } } else if (explicitDimIndex >= 0) { scope.problemReporter().cannotDefineDimensionsAndInitializer(this); } @@ -148,7 +153,7 @@ public class ArrayAllocationExpression extends Expression { if (dimensions[i] != null) { TypeBinding dimensionType = dimensions[i].resolveTypeExpecting(scope, IntBinding); if (dimensionType != null) { - dimensions[i].implicitWidening(IntBinding, dimensionType); + dimensions[i].computeConversion(scope, IntBinding, dimensionType); } } } @@ -158,7 +163,7 @@ public class ArrayAllocationExpression extends Expression { if (dimensions.length > 255) { scope.problemReporter().tooManyDimensions(this); } - this.resolvedType = scope.createArray(referenceType, dimensions.length); + this.resolvedType = scope.createArrayType(referenceType, dimensions.length); // check the initializer if (initializer != null) { diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ArrayInitializer.java b/src/org/eclipse/jdt/internal/compiler/ast/ArrayInitializer.java index b717047..9310cf7 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ArrayInitializer.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ArrayInitializer.java @@ -47,7 +47,7 @@ public class ArrayInitializer extends Expression { int pc = codeStream.position; int expressionLength = (expressions == null) ? 0: expressions.length; codeStream.generateInlinedValue(expressionLength); - codeStream.newArray(currentScope, binding); + codeStream.newArray(binding); if (expressions != null) { // binding is an ArrayType, so I can just deal with the dimension int elementsTypeID = binding.dimensions > 1 ? -1 : binding.leafComponentType.id; @@ -101,7 +101,9 @@ public class ArrayInitializer extends Expression { } } } - if (!valueRequired) { + if (valueRequired) { + codeStream.generateImplicitConversion(this.implicitConversion); + } else { codeStream.pop(); } codeStream.recordPositionsFrom(pc, this.sourceStart); @@ -133,12 +135,19 @@ public class ArrayInitializer extends Expression { // this method is recursive... (the test on isArrayType is the stop case) - constant = NotAConstant; + this.constant = NotAConstant; + + // allow new List[5] + TypeBinding leafComponentType = expectedTb.leafComponentType(); + if (leafComponentType.isBoundParameterizedType() || leafComponentType.isGenericType() || leafComponentType.isTypeVariable()) { + scope.problemReporter().illegalGenericArray(leafComponentType, this); + } + if (expectedTb.isArrayType()) { binding = (ArrayBinding) expectedTb; if (expressions == null) return binding; - TypeBinding expectedElementsTb = binding.elementsType(scope); + TypeBinding expectedElementsTb = binding.elementsType(); if (expectedElementsTb.isBaseType()) { for (int i = 0, length = expressions.length; i < length; i++) { Expression expression = expressions[i]; @@ -150,12 +159,14 @@ public class ArrayInitializer extends Expression { return null; // Compile-time conversion required? - if (expression.isConstantValueOfTypeAssignableToType(expressionTb, expectedElementsTb)) { - expression.implicitWidening(expectedElementsTb, expressionTb); - } else if (BaseTypeBinding.isWidening(expectedElementsTb.id, expressionTb.id)) { - expression.implicitWidening(expectedElementsTb, expressionTb); + if (expectedElementsTb != expressionTb) // must call before computeConversion() and typeMismatchError() + scope.compilationUnitScope().recordTypeConversion(expectedElementsTb, expressionTb); + if (expression.isConstantValueOfTypeAssignableToType(expressionTb, expectedElementsTb) + || BaseTypeBinding.isWidening(expectedElementsTb.id, expressionTb.id) + || scope.isBoxingCompatibleWith(expressionTb, expectedElementsTb)) { + expression.computeConversion(scope, expectedElementsTb, expressionTb); } else { - scope.problemReporter().typeMismatchErrorActualTypeExpectedType(expression, expressionTb, expectedElementsTb); + scope.problemReporter().typeMismatchError(expressionTb, expectedElementsTb, expression); return null; } } @@ -189,8 +200,8 @@ public class ArrayInitializer extends Expression { } } if (leafElementType != null) { - TypeBinding probableTb = scope.createArray(leafElementType, dim); - scope.problemReporter().typeMismatchErrorActualTypeExpectedType(this, probableTb, expectedTb); + TypeBinding probableTb = scope.createArrayType(leafElementType, dim); + scope.problemReporter().typeMismatchError(probableTb, expectedTb, this); } return null; } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ArrayQualifiedTypeReference.java b/src/org/eclipse/jdt/internal/compiler/ast/ArrayQualifiedTypeReference.java index 701f3a6..6c6d1ad 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ArrayQualifiedTypeReference.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ArrayQualifiedTypeReference.java @@ -10,8 +10,10 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; public class ArrayQualifiedTypeReference extends QualifiedTypeReference { int dimensions; @@ -22,32 +24,57 @@ public class ArrayQualifiedTypeReference extends QualifiedTypeReference { dimensions = dim ; } - public ArrayQualifiedTypeReference(char[][] sources , TypeBinding tb, int dim, long[] poss) { - - super( sources , tb, poss); - dimensions = dim ; - } - public int dimensions() { return dimensions; } + + /** + * @return char[][] + */ + public char [][] getParameterizedTypeName(){ + int dim = this.dimensions; + char[] dimChars = new char[dim*2]; + for (int i = 0; i < dim; i++) { + int index = i*2; + dimChars[index] = '['; + dimChars[index+1] = ']'; + } + int length = this.tokens.length; + char[][] qParamName = new char[length][]; + System.arraycopy(this.tokens, 0, qParamName, 0, length-1); + qParamName[length-1] = CharOperation.concat(this.tokens[length-1], dimChars); + return qParamName; + } - public TypeBinding getTypeBinding(Scope scope) { + protected TypeBinding getTypeBinding(Scope scope) { if (this.resolvedType != null) return this.resolvedType; if (dimensions > 255) { scope.problemReporter().tooManyDimensions(this); } - return scope.createArray(scope.getType(tokens), dimensions); + try { + TypeBinding leafComponentType = scope.getType(this.tokens, this.tokens.length); + return scope.createArrayType(leafComponentType, dimensions); + } catch (AbortCompilation e) { + e.updateContext(this, scope.referenceCompilationUnit().compilationResult); + throw e; + } } public StringBuffer printExpression(int indent, StringBuffer output){ super.printExpression(indent, output); - for (int i = 0 ; i < dimensions ; i++) { - output.append("[]"); //$NON-NLS-1$ + if ((this.bits & IsVarArgs) != 0) { + for (int i= 0 ; i < dimensions - 1; i++) { + output.append("[]"); //$NON-NLS-1$ + } + output.append("..."); //$NON-NLS-1$ + } else { + for (int i= 0 ; i < dimensions; i++) { + output.append("[]"); //$NON-NLS-1$ + } } return output; } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java b/src/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java index 915b347..fc0bb69 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java @@ -51,10 +51,9 @@ public class ArrayReference extends Reference { FlowContext flowContext, FlowInfo flowInfo) { - return position.analyseCode( - currentScope, - flowContext, - receiver.analyseCode(currentScope, flowContext, flowInfo)); + flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo); + receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); + return position.analyseCode(currentScope, flowContext, flowInfo); } public void generateAssignment( @@ -123,21 +122,25 @@ public class ArrayReference extends Reference { codeStream.dup2(); codeStream.arrayAt(this.resolvedType.id); int operationTypeID; - if ((operationTypeID = implicitConversion >> 4) == T_String) { - codeStream.generateStringAppend(currentScope, null, expression); - } else { - // promote the array reference to the suitable operation type - codeStream.generateImplicitConversion(implicitConversion); - // generate the increment value (will by itself be promoted to the operation value) - if (expression == IntLiteral.One) { // prefix operation - codeStream.generateConstant(expression.constant, implicitConversion); - } else { - expression.generateCode(currentScope, codeStream, true); - } - // perform the operation - codeStream.sendOperator(operator, operationTypeID); - // cast the value back to the array reference type - codeStream.generateImplicitConversion(assignmentImplicitConversion); + switch(operationTypeID = (implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) { + case T_JavaLangString : + case T_JavaLangObject : + case T_undefined : + codeStream.generateStringConcatenationAppend(currentScope, null, expression); + break; + default : + // promote the array reference to the suitable operation type + codeStream.generateImplicitConversion(implicitConversion); + // generate the increment value (will by itself be promoted to the operation value) + if (expression == IntLiteral.One) { // prefix operation + codeStream.generateConstant(expression.constant, implicitConversion); + } else { + expression.generateCode(currentScope, codeStream, true); + } + // perform the operation + codeStream.sendOperator(operator, operationTypeID); + // cast the value back to the array reference type + codeStream.generateImplicitConversion(assignmentImplicitConversion); } codeStream.arrayAtPut(this.resolvedType.id, valueRequired); } @@ -164,10 +167,11 @@ public class ArrayReference extends Reference { codeStream.dup_x2(); } } + codeStream.generateImplicitConversion(implicitConversion); codeStream.generateConstant( postIncrement.expression.constant, implicitConversion); - codeStream.sendOperator(postIncrement.operator, this.resolvedType.id); + codeStream.sendOperator(postIncrement.operator, this.implicitConversion & COMPILE_TYPE_MASK); codeStream.generateImplicitConversion( postIncrement.assignmentImplicitConversion); codeStream.arrayAtPut(this.resolvedType.id, false); @@ -188,15 +192,16 @@ public class ArrayReference extends Reference { } TypeBinding arrayType = receiver.resolveType(scope); if (arrayType != null) { + receiver.computeConversion(scope, arrayType, arrayType); if (arrayType.isArrayType()) { - this.resolvedType = ((ArrayBinding) arrayType).elementsType(scope); + this.resolvedType = ((ArrayBinding) arrayType).elementsType(); } else { scope.problemReporter().referenceMustBeArrayTypeAt(arrayType, this); } } TypeBinding positionType = position.resolveTypeExpecting(scope, IntBinding); if (positionType != null) { - position.implicitWidening(IntBinding, positionType); + position.computeConversion(scope, IntBinding, positionType); } return this.resolvedType; } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ArrayTypeReference.java b/src/org/eclipse/jdt/internal/compiler/ast/ArrayTypeReference.java index eec8865..66d9947 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ArrayTypeReference.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ArrayTypeReference.java @@ -10,11 +10,16 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; -import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; +import org.eclipse.jdt.internal.compiler.lookup.Scope; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; public class ArrayTypeReference extends SingleTypeReference { public int dimensions; + public int originalSourceEnd; /** * ArrayTypeReference constructor comment. @@ -25,12 +30,7 @@ public class ArrayTypeReference extends SingleTypeReference { public ArrayTypeReference(char[] source, int dimensions, long pos) { super(source, pos); - this.dimensions = dimensions ; - } - - public ArrayTypeReference(char[] source, TypeBinding tb, int dimensions, long pos) { - - super(source, tb, pos); + this.originalSourceEnd = this.sourceEnd; this.dimensions = dimensions ; } @@ -38,22 +38,42 @@ public class ArrayTypeReference extends SingleTypeReference { return dimensions; } - - public TypeBinding getTypeBinding(Scope scope) { + /** + * @return char[][] + */ + public char [][] getParameterizedTypeName(){ + int dim = this.dimensions; + char[] dimChars = new char[dim*2]; + for (int i = 0; i < dim; i++) { + int index = i*2; + dimChars[index] = '['; + dimChars[index+1] = ']'; + } + return new char[][]{ CharOperation.concat(token, dimChars) }; + } + protected TypeBinding getTypeBinding(Scope scope) { if (this.resolvedType != null) return this.resolvedType; if (dimensions > 255) { scope.problemReporter().tooManyDimensions(this); } - return scope.createArray(scope.getType(token), dimensions); + TypeBinding leafComponentType = scope.getType(token); + return scope.createArrayType(leafComponentType, dimensions); } public StringBuffer printExpression(int indent, StringBuffer output){ - super.printExpression(indent, output) ; - for (int i= 0 ; i < dimensions ; i++) { - output.append("[]"); //$NON-NLS-1$ + super.printExpression(indent, output); + if ((this.bits & IsVarArgs) != 0) { + for (int i= 0 ; i < dimensions - 1; i++) { + output.append("[]"); //$NON-NLS-1$ + } + output.append("..."); //$NON-NLS-1$ + } else { + for (int i= 0 ; i < dimensions; i++) { + output.append("[]"); //$NON-NLS-1$ + } } return output; } @@ -63,4 +83,10 @@ public class ArrayTypeReference extends SingleTypeReference { visitor.visit(this, scope); visitor.endVisit(this, scope); } + + public void traverse(ASTVisitor visitor, ClassScope scope) { + + visitor.visit(this, scope); + visitor.endVisit(this, scope); + } } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java b/src/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java index cf3093a..dedee1f 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.*; import org.eclipse.jdt.internal.compiler.flow.*; import org.eclipse.jdt.internal.compiler.impl.Constant; @@ -128,7 +129,7 @@ public class AssertStatement extends Statement { case T_void : scope.problemReporter().illegalVoidExpression(exceptionArgument); default: - id = T_Object; + id = T_JavaLangObject; case T_boolean : case T_byte : case T_char : @@ -137,7 +138,7 @@ public class AssertStatement extends Statement { case T_float : case T_int : case T_long : - case T_String : + case T_JavaLangString : exceptionArgument.implicitConversion = (id << 4) + id; } } @@ -168,7 +169,7 @@ public class AssertStatement extends Statement { outerMostClass = (SourceTypeBinding) enclosing; } - this.assertionSyntheticFieldBinding = outerMostClass.addSyntheticField(this, currentScope); + this.assertionSyntheticFieldBinding = outerMostClass.addSyntheticFieldForAssert(currentScope); // find and enable assertion support TypeDeclaration typeDeclaration = outerMostClass.scope.referenceType(); @@ -176,7 +177,7 @@ public class AssertStatement extends Statement { for (int i = 0, max = methods.length; i < max; i++) { AbstractMethodDeclaration method = methods[i]; if (method.isClinit()) { - ((Clinit) method).setAssertionSupport(assertionSyntheticFieldBinding); + ((Clinit) method).setAssertionSupport(assertionSyntheticFieldBinding, currentScope.environment().options.sourceLevel < ClassFileConstants.JDK1_5); break; } } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/Assignment.java b/src/org/eclipse/jdt/internal/compiler/ast/Assignment.java index 5b86ade..e43c286 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/Assignment.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/Assignment.java @@ -42,9 +42,25 @@ public class Assignment extends Expression { // a field reference, a blank final field reference, a field of an enclosing instance or // just a local variable. - return ((Reference) lhs) + LocalVariableBinding local = this.lhs.localVariableBinding(); + int nullStatus = this.expression.nullStatus(flowInfo); + if (local != null && nullStatus == FlowInfo.NULL) { + flowContext.recordUsingNullReference(currentScope, local, this.lhs, FlowInfo.NON_NULL, flowInfo); + } + flowInfo = ((Reference) lhs) .analyseAssignment(currentScope, flowContext, flowInfo, this, false) .unconditionalInits(); + if (local != null) { + switch(nullStatus) { + case FlowInfo.NULL : + flowInfo.markAsDefinitelyNull(local); + break; + case FlowInfo.NON_NULL : + flowInfo.markAsDefinitelyNonNull(local); + break; + } + } + return flowInfo; } void checkAssignmentEffect(BlockScope scope) { @@ -56,6 +72,19 @@ public class Assignment extends Expression { } } + void checkAssignment(BlockScope scope, TypeBinding lhsType, TypeBinding rhsType) { + + FieldBinding leftField = getLastField(this.lhs); + if (leftField != null && rhsType != NullBinding && lhsType.isWildcard() && ((WildcardBinding)lhsType).kind != Wildcard.SUPER) { + scope.problemReporter().wildcardAssignment(lhsType, rhsType, this.expression); + } else if (leftField != null && leftField.declaringClass != null /*length pseudo field*/&& leftField.declaringClass.isRawType() + && (rhsType.isParameterizedType() || rhsType.isGenericType())) { + scope.problemReporter().unsafeRawFieldAssignment(leftField, rhsType, this.lhs); + } else if (rhsType.needsUncheckedConversion(lhsType)) { + scope.problemReporter().unsafeRawConversion(this.expression, rhsType, lhsType); + } + } + public void generateCode( BlockScope currentScope, CodeStream codeStream, @@ -89,6 +118,28 @@ public class Assignment extends Expression { } return null; } + FieldBinding getLastField(Expression someExpression) { + if (someExpression instanceof SingleNameReference) { + if ((someExpression.bits & RestrictiveFlagMASK) == Binding.FIELD) { + return (FieldBinding) ((SingleNameReference)someExpression).binding; + } + } else if (someExpression instanceof FieldReference) { + return ((FieldReference)someExpression).binding; + } else if (someExpression instanceof QualifiedNameReference) { + QualifiedNameReference qName = (QualifiedNameReference) someExpression; + if (qName.otherBindings == null && ((someExpression.bits & RestrictiveFlagMASK) == Binding.FIELD)) { + return (FieldBinding)qName.binding; + } else { + return qName.otherBindings[qName.otherBindings.length - 1]; + } + } + return null; + } + + public int nullStatus(FlowInfo flowInfo) { + return this.expression.nullStatus(flowInfo); + } + public StringBuffer print(int indent, StringBuffer output) { //no () when used as a statement @@ -122,26 +173,30 @@ public class Assignment extends Expression { scope.problemReporter().expressionShouldBeAVariable(this.lhs); return null; } - this.resolvedType = lhs.resolveType(scope); // expressionType contains the assignment type (lhs Type) + TypeBinding lhsType = this.resolvedType = lhs.resolveType(scope); + expression.setExpectedType(lhsType); // needed in case of generic method invocation TypeBinding rhsType = expression.resolveType(scope); - if (this.resolvedType == null || rhsType == null) { + if (lhsType == null || rhsType == null) { return null; } checkAssignmentEffect(scope); - + // Compile-time conversion of base-types : implicit narrowing integer into byte/short/character // may require to widen the rhs expression at runtime - if ((expression.isConstantValueOfTypeAssignableToType(rhsType, this.resolvedType) - || (this.resolvedType.isBaseType() && BaseTypeBinding.isWidening(this.resolvedType.id, rhsType.id))) - || rhsType.isCompatibleWith(this.resolvedType)) { - expression.implicitWidening(this.resolvedType, rhsType); + if (lhsType != rhsType) // must call before computeConversion() and typeMismatchError() + scope.compilationUnitScope().recordTypeConversion(lhsType, rhsType); + if ((expression.isConstantValueOfTypeAssignableToType(rhsType, lhsType) + || (lhsType.isBaseType() && BaseTypeBinding.isWidening(lhsType.id, rhsType.id))) + || rhsType.isCompatibleWith(lhsType)) { + expression.computeConversion(scope, lhsType, rhsType); + checkAssignment(scope, lhsType, rhsType); return this.resolvedType; - } - scope.problemReporter().typeMismatchErrorActualTypeExpectedType( - expression, - rhsType, - this.resolvedType); - return this.resolvedType; + } else if (scope.isBoxingCompatibleWith(rhsType, lhsType)) { + expression.computeConversion(scope, lhsType, rhsType); + return this.resolvedType; + } + scope.problemReporter().typeMismatchError(rhsType, lhsType, expression); + return lhsType; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.ast.Expression#resolveTypeExpecting(org.eclipse.jdt.internal.compiler.lookup.BlockScope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding) @@ -151,13 +206,16 @@ public class Assignment extends Expression { TypeBinding expectedType) { TypeBinding type = super.resolveTypeExpecting(scope, expectedType); + if (type == null) return null; + TypeBinding lhsType = this.resolvedType; + TypeBinding rhsType = this.expression.resolvedType; // signal possible accidental boolean assignment (instead of using '==' operator) if (expectedType == BooleanBinding - && this.lhs.resolvedType == BooleanBinding + && lhsType == BooleanBinding && (this.lhs.bits & IsStrictlyAssignedMASK) != 0) { scope.problemReporter().possibleAccidentalBooleanAssignment(this); } - + checkAssignment(scope, lhsType, rhsType); return type; } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java b/src/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java index b5804d0..d5a0b3e 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java @@ -96,8 +96,8 @@ public class BinaryExpression extends OperatorExpression { switch ((bits & OperatorMASK) >> OperatorSHIFT) { case PLUS : switch (bits & ReturnTypeIDMASK) { - case T_String : - codeStream.generateStringAppend(currentScope, left, right); + case T_JavaLangString : + codeStream.generateStringConcatenationAppend(currentScope, left, right); if (!valueRequired) codeStream.pop(); break; @@ -699,7 +699,7 @@ public class BinaryExpression extends OperatorExpression { Label falseLabel, boolean valueRequired) { - int promotedTypeID = left.implicitConversion >> 4; + int promotedTypeID = (left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4; // both sides got promoted in the same way if (promotedTypeID == T_int) { // 0 > x @@ -813,7 +813,7 @@ public class BinaryExpression extends OperatorExpression { Label falseLabel, boolean valueRequired) { - int promotedTypeID = left.implicitConversion >> 4; + int promotedTypeID = (left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4; // both sides got promoted in the same way if (promotedTypeID == T_int) { // 0 >= x @@ -927,7 +927,7 @@ public class BinaryExpression extends OperatorExpression { Label falseLabel, boolean valueRequired) { - int promotedTypeID = left.implicitConversion >> 4; + int promotedTypeID = (left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4; // both sides got promoted in the same way if (promotedTypeID == T_int) { // 0 < x @@ -1037,7 +1037,7 @@ public class BinaryExpression extends OperatorExpression { Label falseLabel, boolean valueRequired) { - int promotedTypeID = left.implicitConversion >> 4; + int promotedTypeID = (left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4; // both sides got promoted in the same way if (promotedTypeID == T_int) { // 0 <= x @@ -1152,7 +1152,7 @@ public class BinaryExpression extends OperatorExpression { boolean valueRequired) { Constant condConst; - if ((left.implicitConversion & 0xF) == T_boolean) { + if ((left.implicitConversion & COMPILE_TYPE_MASK) == T_boolean) { if ((condConst = left.optimizedBooleanConstant()) != NotAConstant) { if (condConst.booleanValue() == true) { // & x @@ -1290,7 +1290,7 @@ public class BinaryExpression extends OperatorExpression { boolean valueRequired) { Constant condConst; - if ((left.implicitConversion & 0xF) == T_boolean) { + if ((left.implicitConversion & COMPILE_TYPE_MASK) == T_boolean) { if ((condConst = left.optimizedBooleanConstant()) != NotAConstant) { if (condConst.booleanValue() == true) { // | x @@ -1426,7 +1426,7 @@ public class BinaryExpression extends OperatorExpression { boolean valueRequired) { Constant condConst; - if ((left.implicitConversion & 0xF) == T_boolean) { + if ((left.implicitConversion & COMPILE_TYPE_MASK) == T_boolean) { if ((condConst = left.optimizedBooleanConstant()) != NotAConstant) { if (condConst.booleanValue() == true) { // ^ x @@ -1525,7 +1525,7 @@ public class BinaryExpression extends OperatorExpression { codeStream.updateLastRecordedEndPC(codeStream.position); } - public void generateOptimizedStringBuffer( + public void generateOptimizedStringConcatenation( BlockScope blockScope, CodeStream codeStream, int typeID) { @@ -1536,30 +1536,30 @@ public class BinaryExpression extends OperatorExpression { */ if ((((bits & OperatorMASK) >> OperatorSHIFT) == PLUS) - && ((bits & ReturnTypeIDMASK) == T_String)) { + && ((bits & ReturnTypeIDMASK) == T_JavaLangString)) { if (constant != NotAConstant) { codeStream.generateConstant(constant, implicitConversion); - codeStream.invokeStringBufferAppendForType(implicitConversion & 0xF); + codeStream.invokeStringConcatenationAppendForType(implicitConversion & COMPILE_TYPE_MASK); } else { int pc = codeStream.position; - left.generateOptimizedStringBuffer( + left.generateOptimizedStringConcatenation( blockScope, codeStream, - left.implicitConversion & 0xF); + left.implicitConversion & COMPILE_TYPE_MASK); codeStream.recordPositionsFrom(pc, left.sourceStart); pc = codeStream.position; - right.generateOptimizedStringBuffer( + right.generateOptimizedStringConcatenation( blockScope, codeStream, - right.implicitConversion & 0xF); + right.implicitConversion & COMPILE_TYPE_MASK); codeStream.recordPositionsFrom(pc, right.sourceStart); } } else { - super.generateOptimizedStringBuffer(blockScope, codeStream, typeID); + super.generateOptimizedStringConcatenation(blockScope, codeStream, typeID); } } - public void generateOptimizedStringBufferCreation( + public void generateOptimizedStringConcatenationCreation( BlockScope blockScope, CodeStream codeStream, int typeID) { @@ -1570,29 +1570,29 @@ public class BinaryExpression extends OperatorExpression { */ if ((((bits & OperatorMASK) >> OperatorSHIFT) == PLUS) - && ((bits & ReturnTypeIDMASK) == T_String)) { + && ((bits & ReturnTypeIDMASK) == T_JavaLangString)) { if (constant != NotAConstant) { - codeStream.newStringBuffer(); // new: java.lang.StringBuffer + codeStream.newStringContatenation(); // new: java.lang.StringBuffer codeStream.dup(); codeStream.ldc(constant.stringValue()); - codeStream.invokeStringBufferStringConstructor(); + codeStream.invokeStringConcatenationStringConstructor(); // invokespecial: java.lang.StringBuffer.(Ljava.lang.String;)V } else { int pc = codeStream.position; - left.generateOptimizedStringBufferCreation( + left.generateOptimizedStringConcatenationCreation( blockScope, codeStream, - left.implicitConversion & 0xF); + left.implicitConversion & COMPILE_TYPE_MASK); codeStream.recordPositionsFrom(pc, left.sourceStart); pc = codeStream.position; - right.generateOptimizedStringBuffer( + right.generateOptimizedStringConcatenation( blockScope, codeStream, - right.implicitConversion & 0xF); + right.implicitConversion & COMPILE_TYPE_MASK); codeStream.recordPositionsFrom(pc, right.sourceStart); } } else { - super.generateOptimizedStringBufferCreation(blockScope, codeStream, typeID); + super.generateOptimizedStringConcatenationCreation(blockScope, codeStream, typeID); } } @@ -1670,14 +1670,27 @@ public class BinaryExpression extends OperatorExpression { constant = Constant.NotAConstant; return null; } - int leftTypeId = leftType.id; - int rightTypeId = rightType.id; - if (leftTypeId > 15 - || rightTypeId > 15) { // must convert String + Object || Object + String - if (leftTypeId == T_String) { - rightTypeId = T_Object; - } else if (rightTypeId == T_String) { - leftTypeId = T_Object; + + int leftTypeID = leftType.id; + int rightTypeID = rightType.id; + + // autoboxing support + LookupEnvironment env = scope.environment(); + boolean use15specifics = env.options.sourceLevel >= JDK1_5; + if (use15specifics) { + if (!leftType.isBaseType() && rightTypeID != T_JavaLangString && rightTypeID != T_null) { + leftTypeID = env.computeBoxingType(leftType).id; + } + if (!rightType.isBaseType() && leftTypeID != T_JavaLangString && leftTypeID != T_null) { + rightTypeID = env.computeBoxingType(rightType).id; + } + } + if (leftTypeID > 15 + || rightTypeID > 15) { // must convert String + Object || Object + String + if (leftTypeID == T_JavaLangString) { + rightTypeID = T_JavaLangObject; + } else if (rightTypeID == T_JavaLangString) { + leftTypeID = T_JavaLangObject; } else { constant = Constant.NotAConstant; scope.problemReporter().invalidOperator(this, leftType, rightType); @@ -1685,14 +1698,17 @@ public class BinaryExpression extends OperatorExpression { } } if (((bits & OperatorMASK) >> OperatorSHIFT) == PLUS) { - if (leftTypeId == T_String - && rightType.isArrayType() - && ((ArrayBinding) rightType).elementsType(scope) == CharBinding) { - scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(right); - } else if (rightTypeId == T_String - && leftType.isArrayType() - && ((ArrayBinding) leftType).elementsType(scope) == CharBinding) { - scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(left); + if (leftTypeID == T_JavaLangString) { + this.left.computeConversion(scope, leftType, leftType); + if (rightType.isArrayType() && ((ArrayBinding) rightType).elementsType() == CharBinding) { + scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(right); + } + } + if (rightTypeID == T_JavaLangString) { + this.right.computeConversion(scope, rightType, rightType); + if (leftType.isArrayType() && ((ArrayBinding) leftType).elementsType() == CharBinding) { + scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(left); + } } } @@ -1704,10 +1720,10 @@ public class BinaryExpression extends OperatorExpression { // Don't test for result = 0. If it is zero, some more work is done. // On the one hand when it is not zero (correct code) we avoid doing the test int operator = (bits & OperatorMASK) >> OperatorSHIFT; - int operatorSignature = OperatorSignatures[operator][(leftTypeId << 4) + rightTypeId]; - left.implicitConversion = operatorSignature >>> 12; - right.implicitConversion = (operatorSignature >>> 4) & 0x000FF; + int operatorSignature = OperatorSignatures[operator][(leftTypeID << 4) + rightTypeID]; + left.computeConversion( scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F), leftType); + right.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 8) & 0x0000F), rightType); bits |= operatorSignature & 0xF; switch (operatorSignature & 0xF) { // record the current ReturnTypeID // only switch on possible result type..... @@ -1732,7 +1748,7 @@ public class BinaryExpression extends OperatorExpression { case T_long : this.resolvedType = LongBinding; break; - case T_String : + case T_JavaLangString : this.resolvedType = scope.getJavaLangString(); break; default : //error........ @@ -1743,10 +1759,10 @@ public class BinaryExpression extends OperatorExpression { // check need for operand cast if (leftIsCast || rightIsCast) { - CastExpression.checkNeedForArgumentCasts(scope, operator, operatorSignature, left, leftTypeId, leftIsCast, right, rightTypeId, rightIsCast); + CastExpression.checkNeedForArgumentCasts(scope, operator, operatorSignature, left, leftTypeID, leftIsCast, right, rightTypeID, rightIsCast); } // compute the constant when valid - computeConstant(scope, leftTypeId, rightTypeId); + computeConstant(scope, leftTypeID, rightTypeID); return this.resolvedType; } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java b/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java index 56380f7..ea3a9ae 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java @@ -20,6 +20,8 @@ public class CaseStatement extends Statement { public Expression constantExpression; public CaseLabel targetLabel; + boolean isEnumConstant; + public CaseStatement(Expression constantExpression, int sourceEnd, int sourceStart) { this.constantExpression = constantExpression; this.sourceEnd = sourceEnd; @@ -32,7 +34,7 @@ public class CaseStatement extends Statement { FlowInfo flowInfo) { if (constantExpression != null) { - if (constantExpression.constant == NotAConstant) { + if (!this.isEnumConstant && constantExpression.constant == NotAConstant) { currentScope.problemReporter().caseExpressionMustBeConstant(constantExpression); } this.constantExpression.analyseCode(currentScope, flowContext, flowInfo); @@ -73,9 +75,13 @@ public class CaseStatement extends Statement { // no-op : should use resolveCase(...) instead. } + /** + * Returns the constant intValue or ordinal for enum constants. If constant is NotAConstant, then answers Float.MIN_VALUE + * @see org.eclipse.jdt.internal.compiler.ast.Statement#resolveCase(org.eclipse.jdt.internal.compiler.lookup.BlockScope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.ast.SwitchStatement) + */ public Constant resolveCase( BlockScope scope, - TypeBinding switchType, + TypeBinding switchExpressionType, SwitchStatement switchStatement) { scope.switchCase = this; // record entering in a switch case block @@ -87,21 +93,36 @@ public class CaseStatement extends Statement { // on error the last default will be the selected one ... switchStatement.defaultCase = this; - return null; + return NotAConstant; } // add into the collection of cases of the associated switch statement switchStatement.cases[switchStatement.caseCount++] = this; + // tag constant name with enum type for privileged access to its members + if (switchExpressionType.isEnum() && (constantExpression instanceof SingleNameReference)) { + ((SingleNameReference) constantExpression).setActualReceiverType((ReferenceBinding)switchExpressionType); + } TypeBinding caseType = constantExpression.resolveType(scope); - if (caseType == null || switchType == null) return null; - if (constantExpression.isConstantValueOfTypeAssignableToType(caseType, switchType)) - return constantExpression.constant; - if (caseType.isCompatibleWith(switchType)) + if (caseType == null || switchExpressionType == null) return NotAConstant; + if (constantExpression.isConstantValueOfTypeAssignableToType(caseType, switchExpressionType) + || caseType.isCompatibleWith(switchExpressionType)) { + if (caseType.isEnum()) { + this.isEnumConstant = true; + if (constantExpression instanceof NameReference + && (constantExpression.bits & RestrictiveFlagMASK) == Binding.FIELD) { + if (constantExpression instanceof QualifiedNameReference) { + scope.problemReporter().cannotUseQualifiedEnumConstantInCaseLabel((QualifiedNameReference)constantExpression); + } + return Constant.fromValue(((NameReference)constantExpression).fieldBinding().id); // ordinal value + } + } else { + return constantExpression.constant; + } + } else if (scope.isBoxingCompatibleWith(switchExpressionType, caseType)) { + constantExpression.computeConversion(scope, caseType, switchExpressionType); return constantExpression.constant; - scope.problemReporter().typeMismatchErrorActualTypeExpectedType( - constantExpression, - caseType, - switchType); - return null; + } + scope.problemReporter().typeMismatchError(caseType, switchExpressionType, constantExpression); + return NotAConstant; } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/CastExpression.java b/src/org/eclipse/jdt/internal/compiler/ast/CastExpression.java index ecfe767..79e4922 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/CastExpression.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/CastExpression.java @@ -11,7 +11,6 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; -import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.impl.*; import org.eclipse.jdt.internal.compiler.codegen.*; @@ -23,6 +22,7 @@ public class CastExpression extends Expression { public Expression expression; public Expression type; + public TypeBinding expectedType; // when assignment conversion to a given expected type: String s = (String) t; //expression.implicitConversion holds the cast for baseType casting public CastExpression(Expression expression, Expression type) { @@ -54,172 +54,6 @@ public class CastExpression extends Expression { } /** - * Returns false if the cast is unnecessary - */ - public final boolean checkCastTypesCompatibility( - BlockScope scope, - TypeBinding castType, - TypeBinding expressionType) { - - // see specifications 5.5 - // handle errors and process constant when needed - - // if either one of the type is null ==> - // some error has been already reported some where ==> - // we then do not report an obvious-cascade-error. - - if (castType == null || expressionType == null) return true; - - // identity conversion cannot be performed upfront, due to side-effects - // like constant propagation - - if (castType.isBaseType()) { - if (expressionType.isBaseType()) { - if (expressionType == castType) { - expression.implicitWidening(castType, expressionType); - constant = expression.constant; //use the same constant - return false; - } - boolean necessary = false; - if (expressionType.isCompatibleWith(castType) - || (necessary = BaseTypeBinding.isNarrowing(castType.id, expressionType.id))) { - expression.implicitConversion = (castType.id << 4) + expressionType.id; - if (expression.constant != Constant.NotAConstant) { - constant = expression.constant.castTo(expression.implicitConversion); - } - return necessary; - - } - } - scope.problemReporter().typeCastError(this, castType, expressionType); - return true; - } - - //-----------cast to something which is NOT a base type-------------------------- - if (expressionType == NullBinding) { - // if (castType.isArrayType()){ // 26903 - need checkcast when casting null to array type - // needRuntimeCheckcast = true; - // } - return false; //null is compatible with every thing - } - if (expressionType.isBaseType()) { - scope.problemReporter().typeCastError(this, castType, expressionType); - return true; - } - - if (expressionType.isArrayType()) { - if (castType == expressionType) return false; // identity conversion - - if (castType.isArrayType()) { - //------- (castType.isArray) expressionType.isArray ----------- - TypeBinding exprElementType = ((ArrayBinding) expressionType).elementsType(scope); - if (exprElementType.isBaseType()) { - // <---stop the recursion------- - if (((ArrayBinding) castType).elementsType(scope) == exprElementType) { - this.bits |= NeedRuntimeCheckCastMASK; - } else { - scope.problemReporter().typeCastError(this, castType, expressionType); - } - return true; - } - // recursively on the elements... - return checkCastTypesCompatibility( - scope, - ((ArrayBinding) castType).elementsType(scope), - exprElementType); - } else if ( - castType.isClass()) { - //------(castType.isClass) expressionType.isArray --------------- - if (castType.id == T_Object) { - return false; - } - } else { //------- (castType.isInterface) expressionType.isArray ----------- - if (castType.id == T_JavaLangCloneable || castType.id == T_JavaIoSerializable) { - this.bits |= NeedRuntimeCheckCastMASK; - return true; - } - } - scope.problemReporter().typeCastError(this, castType, expressionType); - return true; - } - - if (expressionType.isClass()) { - if (castType.isArrayType()) { - // ---- (castType.isArray) expressionType.isClass ------- - if (expressionType.id == T_Object) { // potential runtime error - this.bits |= NeedRuntimeCheckCastMASK; - return true; - } - } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isClass ------ - if (expressionType.isCompatibleWith(castType)){ // no runtime error - if (castType.id == T_String) constant = expression.constant; // (String) cst is still a constant - return false; - } - if (castType.isCompatibleWith(expressionType)) { - // potential runtime error - this.bits |= NeedRuntimeCheckCastMASK; - return true; - } - } else { // ----- (castType.isInterface) expressionType.isClass ------- - if (expressionType.isCompatibleWith(castType)) - return false; - if (!((ReferenceBinding) expressionType).isFinal()) { - // a subclass may implement the interface ==> no check at compile time - this.bits |= NeedRuntimeCheckCastMASK; - return true; - } - // no subclass for expressionType, thus compile-time check is valid - } - scope.problemReporter().typeCastError(this, castType, expressionType); - return true; - } - - // if (expressionType.isInterface()) { cannot be anything else - if (castType.isArrayType()) { - // ----- (castType.isArray) expressionType.isInterface ------ - if (expressionType.id == T_JavaLangCloneable - || expressionType.id == T_JavaIoSerializable) {// potential runtime error - this.bits |= NeedRuntimeCheckCastMASK; - } else { - scope.problemReporter().typeCastError(this, castType, expressionType); - } - return true; - } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isInterface -------- - if (castType.id == T_Object) { // no runtime error - return false; - } - if (((ReferenceBinding) castType).isFinal()) { - // no subclass for castType, thus compile-time check is valid - if (!castType.isCompatibleWith(expressionType)) { - // potential runtime error - scope.problemReporter().typeCastError(this, castType, expressionType); - return true; - } - } - } else { // ----- (castType.isInterface) expressionType.isInterface ------- - if (expressionType.isCompatibleWith(castType)) { - return false; - } - if (!castType.isCompatibleWith(expressionType)) { - MethodBinding[] castTypeMethods = ((ReferenceBinding) castType).methods(); - MethodBinding[] expressionTypeMethods = - ((ReferenceBinding) expressionType).methods(); - int exprMethodsLength = expressionTypeMethods.length; - for (int i = 0, castMethodsLength = castTypeMethods.length; i < castMethodsLength; i++) - for (int j = 0; j < exprMethodsLength; j++) { - if ((castTypeMethods[i].returnType != expressionTypeMethods[j].returnType) - && (CharOperation.equals(castTypeMethods[i].selector, expressionTypeMethods[j].selector)) - && castTypeMethods[i].areParametersEqual(expressionTypeMethods[j])) { - scope.problemReporter().typeCastError(this, castType, expressionType); - } - } - } - } - this.bits |= NeedRuntimeCheckCastMASK; - return true; - } - - /** * Casting an enclosing instance will considered as useful if removing it would actually bind to a different type */ public static void checkNeedForEnclosingInstanceCast(BlockScope scope, Expression enclosingInstance, TypeBinding enclosingInstanceType, TypeBinding memberType) { @@ -363,10 +197,10 @@ public class CastExpression extends Expression { } if (leftIsCast || rightIsCast) { if (alternateLeftTypeId > 15 || alternateRightTypeId > 15) { // must convert String + Object || Object + String - if (alternateLeftTypeId == T_String) { - alternateRightTypeId = T_Object; - } else if (alternateRightTypeId == T_String) { - alternateLeftTypeId = T_Object; + if (alternateLeftTypeId == T_JavaLangString) { + alternateRightTypeId = T_JavaLangObject; + } else if (alternateRightTypeId == T_JavaLangString) { + alternateLeftTypeId = T_JavaLangObject; } else { return; // invalid operator } @@ -377,8 +211,8 @@ public class CastExpression extends Expression { // <<16 <<12 <<8 <<4 <<0 final int CompareMASK = (0xF<<16) + (0xF<<8) + 0xF; // mask hiding compile-time types if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result - if (leftIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)left, TypeBinding.wellKnownType(scope, left.implicitConversion >> 4)); - if (rightIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)right, TypeBinding.wellKnownType(scope, right.implicitConversion >> 4)); + if (leftIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)left, TypeBinding.wellKnownType(scope, (left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4)); + if (rightIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)right, TypeBinding.wellKnownType(scope, (right.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4)); } } } @@ -386,6 +220,7 @@ public class CastExpression extends Expression { private static void checkAlternateBinding(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] originalArgumentTypes, TypeBinding[] alternateArgumentTypes, final InvocationSite invocationSite) { InvocationSite fakeInvocationSite = new InvocationSite(){ + public TypeBinding[] genericTypeArguments() { return null; } public boolean isSuperAccess(){ return invocationSite.isSuperAccess(); } public boolean isTypeAccess() { return invocationSite.isTypeAccess(); } public void setActualReceiverType(ReferenceBinding actualReceiverType) { /* ignore */} @@ -410,6 +245,33 @@ public class CastExpression extends Expression { } } } + + public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) { + if (match == castType) { + if (!isNarrowing) tagAsUnnecessaryCast(scope, castType); + return true; + } + if (castType.isBoundParameterizedType() || castType.isGenericType()) { + if (match.isProvablyDistinctFrom(isNarrowing ? expressionType : castType, 0)) { + reportIllegalCast(scope, castType, expressionType); + return false; + } + if (isNarrowing ? !expressionType.isEquivalentTo(match) : !match.isEquivalentTo(castType)) { + scope.problemReporter().unsafeCast(this); + return true; + } + if ((castType.tagBits & TagBits.HasDirectWildcard) == 0) { + if ((!match.isParameterizedType() && !match.isGenericType()) + || expressionType.isRawType()) { + scope.problemReporter().unsafeCast(this); + return true; + } + } + } + if (!isNarrowing) tagAsUnnecessaryCast(scope, castType); + return true; + } + /** * Cast expression code generation * @@ -421,7 +283,7 @@ public class CastExpression extends Expression { BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { - + int pc = codeStream.position; boolean needRuntimeCheckcast = (this.bits & NeedRuntimeCheckCastMASK) != 0; if (constant != NotAConstant) { @@ -429,8 +291,11 @@ public class CastExpression extends Expression { codeStream.generateConstant(constant, implicitConversion); if (needRuntimeCheckcast) { codeStream.checkcast(this.resolvedType); - if (!valueRequired) + if (valueRequired) { + codeStream.generateImplicitConversion(this.implicitConversion); + } else { codeStream.pop(); + } } } codeStream.recordPositionsFrom(pc, this.sourceStart); @@ -442,8 +307,11 @@ public class CastExpression extends Expression { valueRequired || needRuntimeCheckcast); if (needRuntimeCheckcast) { codeStream.checkcast(this.resolvedType); - if (!valueRequired) + if (valueRequired) { + codeStream.generateImplicitConversion(implicitConversion); + } else { codeStream.pop(); + } } else { if (valueRequired) codeStream.generateImplicitConversion(implicitConversion); @@ -459,6 +327,17 @@ public class CastExpression extends Expression { return current; } + /** + * @see org.eclipse.jdt.internal.compiler.ast.Expression#localVariableBinding() + */ + public LocalVariableBinding localVariableBinding() { + return this.expression.localVariableBinding(); + } + + public int nullStatus(FlowInfo flowInfo) { + return this.expression.nullStatus(flowInfo); + } + public StringBuffer printExpression(int indent, StringBuffer output) { output.append('('); @@ -466,6 +345,10 @@ public class CastExpression extends Expression { return expression.printExpression(0, output); } + public void reportIllegalCast(Scope scope, TypeBinding castType, TypeBinding expressionType) { + scope.problemReporter().typeCastError(this, castType, expressionType); + } + public TypeBinding resolveType(BlockScope scope) { // compute a new constant if the cast is effective @@ -480,25 +363,67 @@ public class CastExpression extends Expression { && ((type.bits & ASTNode.ParenthesizedMASK) >> ASTNode.ParenthesizedSHIFT) == 0) { // no extra parenthesis around type: ((A))exp this.resolvedType = type.resolveType(scope); + expression.setExpectedType(this.resolvedType); // needed in case of generic method invocation TypeBinding expressionType = expression.resolveType(scope); if (this.resolvedType != null && expressionType != null) { - boolean necessary = checkCastTypesCompatibility(scope, this.resolvedType, expressionType); - if (!necessary && this.expression.resolvedType != null) { // cannot do better if expression is not bound - this.bits |= UnnecessaryCastMask; + checkCastTypesCompatibility(scope, this.resolvedType, expressionType, this.expression); + this.expression.computeConversion(scope, this.resolvedType, expressionType); + if ((this.bits & UnnecessaryCastMask) != 0) { if ((this.bits & IgnoreNeedForCastCheckMASK) == 0) { - scope.problemReporter().unnecessaryCast(this); + if (!usedForGenericMethodReturnTypeInference()) // used for generic type inference ? + scope.problemReporter().unnecessaryCast(this); } } } return this.resolvedType; - } else { // expression as a cast !!!!!!!! + } else { // expression as a cast TypeBinding expressionType = expression.resolveType(scope); if (expressionType == null) return null; scope.problemReporter().invalidTypeReference(type); return null; } } + + /** + * @see org.eclipse.jdt.internal.compiler.ast.Expression#setExpectedType(org.eclipse.jdt.internal.compiler.lookup.TypeBinding) + */ + public void setExpectedType(TypeBinding expectedType) { + this.expectedType = expectedType; + } + /** + * Determines whether apparent unnecessary cast wasn't actually used to + * perform return type inference of generic method invocation. + */ + private boolean usedForGenericMethodReturnTypeInference() { + if (this.expression instanceof MessageSend) { + MethodBinding method = ((MessageSend)this.expression).binding; + if (method instanceof ParameterizedGenericMethodBinding + && ((ParameterizedGenericMethodBinding)method).inferredReturnType) { + if (this.expectedType == null) + return true; + if (this.resolvedType != this.expectedType) + return true; + } + } + return false; + } + + /** + * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsNeedCheckCast() + */ + public void tagAsNeedCheckCast() { + this.bits |= NeedRuntimeCheckCastMASK; + } + + /** + * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsUnnecessaryCast(Scope, TypeBinding) + */ + public void tagAsUnnecessaryCast(Scope scope, TypeBinding castType) { + if (this.expression.resolvedType == null) return; // cannot do better if expression is not bound + this.bits |= UnnecessaryCastMask; + } + public void traverse( ASTVisitor visitor, BlockScope blockScope) { diff --git a/src/org/eclipse/jdt/internal/compiler/ast/CharLiteral.java b/src/org/eclipse/jdt/internal/compiler/ast/CharLiteral.java index c2d90b0..8aad9ab 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/CharLiteral.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/CharLiteral.java @@ -84,11 +84,9 @@ private void computeValue() { */ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { int pc = codeStream.position; - if (valueRequired) - if ((implicitConversion >> 4) == T_char) - codeStream.generateInlinedValue(value); - else - codeStream.generateConstant(constant, implicitConversion); + if (valueRequired) { + codeStream.generateConstant(constant, implicitConversion); + } codeStream.recordPositionsFrom(pc, this.sourceStart); } public TypeBinding literalType(BlockScope scope) { diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ClassLiteralAccess.java b/src/org/eclipse/jdt/internal/compiler/ast/ClassLiteralAccess.java index 4a4136e..4e35315 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ClassLiteralAccess.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ClassLiteralAccess.java @@ -11,6 +11,7 @@ package org.eclipse.jdt.internal.compiler.ast; import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.*; import org.eclipse.jdt.internal.compiler.flow.*; import org.eclipse.jdt.internal.compiler.lookup.*; @@ -35,10 +36,11 @@ public class ClassLiteralAccess extends Expression { // if reachable, request the addition of a synthetic field for caching the class descriptor SourceTypeBinding sourceType = currentScope.outerMostMethodScope().enclosingSourceType(); - if (!(sourceType.isInterface() - // no field generated in interface case (would'nt verify) see 1FHHEZL - || sourceType.isBaseType())) { - syntheticField = sourceType.addSyntheticField(targetType, currentScope); + if ((!(sourceType.isInterface() + // no field generated in interface case (would'nt verify) see 1FHHEZL + || sourceType.isBaseType())) + && currentScope.environment().options.sourceLevel <= ClassFileConstants.JDK1_5) { + syntheticField = sourceType.addSyntheticFieldForClassLiteral(targetType, currentScope); } return flowInfo; } @@ -57,8 +59,10 @@ public class ClassLiteralAccess extends Expression { int pc = codeStream.position; // in interface case, no caching occurs, since cannot make a cache field for interface - if (valueRequired) + if (valueRequired) { codeStream.generateClassLiteralAccessForType(type.resolvedType, syntheticField); + codeStream.generateImplicitConversion(this.implicitConversion); + } codeStream.recordPositionsFrom(pc, this.sourceStart); } @@ -70,16 +74,24 @@ public class ClassLiteralAccess extends Expression { public TypeBinding resolveType(BlockScope scope) { constant = NotAConstant; - if ((targetType = type.resolveType(scope)) == null) + if ((targetType = type.resolveType(scope, true /* check bounds*/)) == null) return null; if (targetType.isArrayType() && ((ArrayBinding) targetType).leafComponentType == VoidBinding) { scope.problemReporter().cannotAllocateVoidArray(this); return null; + } else if (targetType.isTypeVariable()) { + scope.problemReporter().illegalClassLiteralForTypeVariable((TypeVariableBinding)targetType, this); } - - return this.resolvedType = scope.getJavaLangClass(); + ReferenceBinding classType = scope.getJavaLangClass(); + if (classType.isGenericType()) { + // Integer.class --> Class, perform boxing of base types (int.class --> Class) + this.resolvedType = scope.createParameterizedType(classType, new TypeBinding[]{ scope.boxing(targetType) }, null/*not a member*/); + } else { + this.resolvedType = classType; + } + return this.resolvedType; } public void traverse( diff --git a/src/org/eclipse/jdt/internal/compiler/ast/Clinit.java b/src/org/eclipse/jdt/internal/compiler/ast/Clinit.java index 2bbac9c..22dbe6d 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/Clinit.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/Clinit.java @@ -13,6 +13,7 @@ package org.eclipse.jdt.internal.compiler.ast; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.*; import org.eclipse.jdt.internal.compiler.codegen.*; +import org.eclipse.jdt.internal.compiler.env.IGenericType; import org.eclipse.jdt.internal.compiler.flow.*; import org.eclipse.jdt.internal.compiler.lookup.*; import org.eclipse.jdt.internal.compiler.parser.*; @@ -20,15 +21,13 @@ import org.eclipse.jdt.internal.compiler.problem.*; public class Clinit extends AbstractMethodDeclaration { - public final static char[] ConstantPoolName = "".toCharArray(); //$NON-NLS-1$ - private FieldBinding assertionSyntheticFieldBinding = null; private FieldBinding classLiteralSyntheticField = null; public Clinit(CompilationResult compilationResult) { super(compilationResult); modifiers = 0; - selector = ConstantPoolName; + selector = TypeConstants.CLINIT; } public void analyseCode( @@ -60,7 +59,7 @@ public class Clinit extends AbstractMethodDeclaration { && (!flowInfo.isDefinitelyAssigned(fields[i]))) { scope.problemReporter().uninitializedBlankFinalField( field, - scope.referenceType().declarationOf(field)); + scope.referenceType().declarationOf(field.original())); // can complain against the field decl, since only one } } @@ -150,7 +149,7 @@ public class Clinit extends AbstractMethodDeclaration { if (this.assertionSyntheticFieldBinding != null) { // generate code related to the activation of assertion for this class codeStream.generateClassLiteralAccessForType( - classScope.enclosingSourceType(), + classScope.enclosingSourceType(), classLiteralSyntheticField); codeStream.invokeJavaLangClassDesiredAssertionStatus(); Label falseLabel = new Label(codeStream); @@ -163,15 +162,63 @@ public class Clinit extends AbstractMethodDeclaration { jumpLabel.place(); codeStream.putstatic(this.assertionSyntheticFieldBinding); } - // generate initializers - if (declaringType.fields != null) { - for (int i = 0, max = declaringType.fields.length; i < max; i++) { - FieldDeclaration fieldDecl; - if ((fieldDecl = declaringType.fields[i]).isStatic()) { - fieldDecl.generateCode(staticInitializerScope, codeStream); + // generate static fields/initializers/enum constants + final FieldDeclaration[] fieldDeclarations = declaringType.fields; + if (declaringType.kind() == IGenericType.ENUM_DECL) { + int enumCount = 0; + int notEnumConstants = 0; + if (fieldDeclarations != null) { + for (int i = 0, max = fieldDeclarations.length; i < max; i++) { + FieldDeclaration fieldDecl = fieldDeclarations[i]; + if (fieldDecl.isStatic()) { + if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) { + fieldDecl.generateCode(staticInitializerScope, codeStream); + enumCount++; + } else { + notEnumConstants++; + } + } + } + } + // enum need to initialize $VALUES synthetic cache of enum constants + if (enumCount > 0) { + if (fieldDeclarations != null) { + // $VALUES := new [] + codeStream.generateInlinedValue(enumCount); + codeStream.anewarray(declaringType.binding); + for (int i = 0, max = fieldDeclarations.length; i < max; i++) { + FieldDeclaration fieldDecl = fieldDeclarations[i]; + // $VALUES[i] = + if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) { + codeStream.dup(); + codeStream.generateInlinedValue(fieldDecl.binding.id); + codeStream.getstatic(fieldDecl.binding); + codeStream.aastore(); + } + } + codeStream.putstatic(declaringType.enumValuesSyntheticfield); + } + } + if (notEnumConstants != 0) { + // if fields that are not enum constants need to be generated (static initializer/static field) + for (int i = 0, max = fieldDeclarations.length; i < max; i++) { + FieldDeclaration fieldDecl = fieldDeclarations[i]; + if (fieldDecl.isStatic() && fieldDecl.getKind() != AbstractVariableDeclaration.ENUM_CONSTANT) { + fieldDecl.generateCode(staticInitializerScope, codeStream); + } + } + } + } else { + if (fieldDeclarations != null) { + for (int i = 0, max = fieldDeclarations.length; i < max; i++) { + FieldDeclaration fieldDecl = fieldDeclarations[i]; + if (fieldDecl.isStatic()) { + fieldDecl.generateCode(staticInitializerScope, codeStream); + } } } } + if (codeStream.position == 0) { // do not need to output a Clinit if no bytecodes // so we reset the offset inside the byte array contents. @@ -231,16 +278,17 @@ public class Clinit extends AbstractMethodDeclaration { visitor.endVisit(this, classScope); } - // 1.4 feature - public void setAssertionSupport(FieldBinding assertionSyntheticFieldBinding) { + public void setAssertionSupport(FieldBinding assertionSyntheticFieldBinding, boolean needClassLiteralField) { this.assertionSyntheticFieldBinding = assertionSyntheticFieldBinding; // we need to add the field right now, because the field infos are generated before the methods SourceTypeBinding sourceType = this.scope.outerMostMethodScope().enclosingSourceType(); - this.classLiteralSyntheticField = - sourceType.addSyntheticField(sourceType, scope); + if (needClassLiteralField) { + this.classLiteralSyntheticField = + sourceType.addSyntheticFieldForClassLiteral(sourceType, scope); + } } } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java b/src/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java index d736614..df96e49 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java @@ -19,6 +19,8 @@ import org.eclipse.jdt.internal.compiler.problem.*; public class CompilationUnitDeclaration extends ASTNode implements ProblemSeverities, ReferenceContext { + + private static final char[] PACKAGE_INFO_FILE_NAME = "package-info.java".toCharArray(); //$NON-NLS-1$ public ImportReference currentPackage; public ImportReference[] imports; @@ -266,7 +268,12 @@ public class CompilationUnitDeclaration } public void resolve() { - + if (this.currentPackage != null) { + if (this.currentPackage.annotations != null + && !CharOperation.endsWith(getFileName(), PACKAGE_INFO_FILE_NAME)) { + scope.problemReporter().invalidFileNameForPackageAnnotations(this.currentPackage.annotations[0]); + } + } try { if (types != null) { for (int i = 0, count = types.length; i < count; i++) { diff --git a/src/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java b/src/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java index 47745f1..fda4ef3 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java @@ -54,6 +54,10 @@ public class CompoundAssignment extends Assignment implements OperatorIds { codeStream.recordPositionsFrom(pc, this.sourceStart); } + public int nullStatus(FlowInfo flowInfo) { + return FlowInfo.NON_NULL; + } + public String operatorToString() { switch (operator) { case PLUS : @@ -94,23 +98,41 @@ public class CompoundAssignment extends Assignment implements OperatorIds { scope.problemReporter().expressionShouldBeAVariable(this.lhs); return null; } - TypeBinding lhsType = lhs.resolveType(scope); - TypeBinding expressionType = expression.resolveType(scope); - if (lhsType == null || expressionType == null) + TypeBinding originalLhsType = lhs.resolveType(scope); + TypeBinding originalExpressionType = expression.resolveType(scope); + if (originalLhsType == null || originalExpressionType == null) return null; - int lhsId = lhsType.id; - int expressionId = expressionType.id; + // autoboxing support + LookupEnvironment env = scope.environment(); + TypeBinding lhsType = originalLhsType, expressionType = originalExpressionType; + boolean use15specifics = scope.environment().options.sourceLevel >= JDK1_5; + boolean unboxedLhs = false; + if (use15specifics) { + if (!lhsType.isBaseType() && expressionType.id != T_JavaLangString && expressionType.id != T_null) { + TypeBinding unboxedType = env.computeBoxingType(lhsType); + if (unboxedType != lhsType) { + lhsType = unboxedType; + unboxedLhs = true; + } + } + if (!expressionType.isBaseType() && lhsType.id != T_JavaLangString && lhsType.id != T_null) { + expressionType = env.computeBoxingType(expressionType); + } + } + if (restrainUsageToNumericTypes() && !lhsType.isNumericType()) { scope.problemReporter().operatorOnlyValidOnNumericType(this, lhsType, expressionType); return null; } - if (lhsId > 15 || expressionId > 15) { - if (lhsId != T_String) { // String += Thread is valid whereas Thread += String is not + int lhsID = lhsType.id; + int expressionID = expressionType.id; + if (lhsID > 15 || expressionID > 15) { + if (lhsID != T_JavaLangString) { // String += Thread is valid whereas Thread += String is not scope.problemReporter().invalidOperator(this, lhsType, expressionType); return null; } - expressionId = T_Object; // use the Object has tag table + expressionID = T_JavaLangObject; // use the Object has tag table } // the code is an int @@ -119,28 +141,28 @@ public class CompoundAssignment extends Assignment implements OperatorIds { // <<16 <<12 <<8 <<4 <<0 // the conversion is stored INTO the reference (info needed for the code gen) - int result = OperatorExpression.OperatorSignatures[operator][ (lhsId << 4) + expressionId]; + int result = OperatorExpression.OperatorSignatures[operator][ (lhsID << 4) + expressionID]; if (result == T_undefined) { scope.problemReporter().invalidOperator(this, lhsType, expressionType); return null; } if (operator == PLUS){ - if(lhsId == T_JavaLangObject) { + if(lhsID == T_JavaLangObject) { // += is illegal (39248) scope.problemReporter().invalidOperator(this, lhsType, expressionType); return null; } else { // += is illegal - if ((lhsType.isNumericType() || lhsId == T_boolean) && !expressionType.isNumericType()){ + if ((lhsType.isNumericType() || lhsID == T_boolean) && !expressionType.isNumericType()){ scope.problemReporter().invalidOperator(this, lhsType, expressionType); return null; } } } - lhs.implicitConversion = result >>> 12; - expression.implicitConversion = (result >>> 4) & 0x000FF; - assignmentImplicitConversion = (lhsId << 4) + (result & 0x0000F); - return this.resolvedType = lhsType; + this.lhs.implicitConversion = (unboxedLhs ? UNBOXING : 0) | (result >>> 12); + this.expression.computeConversion(scope, TypeBinding.wellKnownType(scope, (result >>> 8) & 0x0000F), originalExpressionType); + this.assignmentImplicitConversion = (unboxedLhs ? BOXING : 0) | (lhsID << 4) | (result & 0x0000F); + return this.resolvedType = originalLhsType; } public boolean restrainUsageToNumericTypes(){ diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java b/src/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java index cf86373..2572232 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java @@ -12,6 +12,7 @@ package org.eclipse.jdt.internal.compiler.ast; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.impl.*; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.*; import org.eclipse.jdt.internal.compiler.flow.*; import org.eclipse.jdt.internal.compiler.lookup.*; @@ -200,7 +201,7 @@ public class ConditionalExpression extends OperatorExpression { boolean valueRequired) { if ((constant != Constant.NotAConstant) && (constant.typeID() == T_boolean) // constant - || (valueIfTrue.implicitConversion >> 4) != T_boolean) { // non boolean values + || ((valueIfTrue.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) != T_boolean) { // non boolean values super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired); return; } @@ -246,9 +247,7 @@ public class ConditionalExpression extends OperatorExpression { if (needFalsePart) { internalFalseLabel.place(); if (falseInitStateIndex != -1) { - codeStream.removeNotDefinitelyAssignedVariables( - currentScope, - falseInitStateIndex); + codeStream.removeNotDefinitelyAssignedVariables(currentScope, falseInitStateIndex); codeStream.addDefinitelyAssignedVariables(currentScope, falseInitStateIndex); } valueIfFalse.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired); @@ -258,9 +257,7 @@ public class ConditionalExpression extends OperatorExpression { } // May loose some local variable initializations : affecting the local variable attributes if (mergedInitStateIndex != -1) { - codeStream.removeNotDefinitelyAssignedVariables( - currentScope, - mergedInitStateIndex); + codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex); } // no implicit conversion for boolean values codeStream.updateLastRecordedEndPC(codeStream.position); @@ -281,17 +278,30 @@ public class ConditionalExpression extends OperatorExpression { public TypeBinding resolveType(BlockScope scope) { // specs p.368 constant = NotAConstant; + LookupEnvironment env = scope.environment(); + boolean use15specifics = env.options.sourceLevel >= ClassFileConstants.JDK1_5; TypeBinding conditionType = condition.resolveTypeExpecting(scope, BooleanBinding); if (valueIfTrue instanceof CastExpression) valueIfTrue.bits |= IgnoreNeedForCastCheckMASK; // will check later on - TypeBinding valueIfTrueType = valueIfTrue.resolveType(scope); + TypeBinding originalValueIfTrueType = valueIfTrue.resolveType(scope); if (valueIfFalse instanceof CastExpression) valueIfFalse.bits |= IgnoreNeedForCastCheckMASK; // will check later on - TypeBinding valueIfFalseType = valueIfFalse.resolveType(scope); + TypeBinding originalValueIfFalseType = valueIfFalse.resolveType(scope); - if (conditionType == null || valueIfTrueType == null || valueIfFalseType == null) + if (conditionType == null || originalValueIfTrueType == null || originalValueIfFalseType == null) return null; + TypeBinding valueIfTrueType = originalValueIfTrueType; + TypeBinding valueIfFalseType = originalValueIfFalseType; + if (use15specifics) { + if (valueIfTrueType != NullBinding && valueIfTrueType.isBaseType()) { + if (!valueIfFalseType.isBaseType()) { + valueIfFalseType = env.computeBoxingType(valueIfFalseType); + } + } else if (valueIfFalseType != NullBinding && valueIfFalseType.isBaseType()) { + valueIfTrueType = env.computeBoxingType(valueIfTrueType); + } + } // Propagate the constant value from the valueIfTrue and valueIFFalse expression if it is possible Constant condConstant, trueConstant, falseConstant; if ((condConstant = condition.constant) != NotAConstant @@ -302,8 +312,8 @@ public class ConditionalExpression extends OperatorExpression { constant = condConstant.booleanValue() ? trueConstant : falseConstant; } if (valueIfTrueType == valueIfFalseType) { // harmed the implicit conversion - valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType); - valueIfFalse.implicitConversion = valueIfTrue.implicitConversion; + valueIfTrue.computeConversion(scope, valueIfTrueType, originalValueIfTrueType); + valueIfFalse.computeConversion(scope, valueIfFalseType, originalValueIfFalseType); if (valueIfTrueType == BooleanBinding) { this.optimizedIfTrueConstant = valueIfTrue.optimizedBooleanConstant(); this.optimizedIfFalseConstant = valueIfFalse.optimizedBooleanConstant(); @@ -326,16 +336,16 @@ public class ConditionalExpression extends OperatorExpression { // (Short x Byte) or (Byte x Short)" if ((valueIfTrueType == ByteBinding && valueIfFalseType == ShortBinding) || (valueIfTrueType == ShortBinding && valueIfFalseType == ByteBinding)) { - valueIfTrue.implicitWidening(ShortBinding, valueIfTrueType); - valueIfFalse.implicitWidening(ShortBinding, valueIfFalseType); + valueIfTrue.computeConversion(scope, ShortBinding, originalValueIfTrueType); + valueIfFalse.computeConversion(scope, ShortBinding, originalValueIfFalseType); return this.resolvedType = ShortBinding; } // x constant(Int) ---> and reciprocally if ((valueIfTrueType == ByteBinding || valueIfTrueType == ShortBinding || valueIfTrueType == CharBinding) && (valueIfFalseType == IntBinding && valueIfFalse.isConstantValueOfTypeAssignableToType(valueIfFalseType, valueIfTrueType))) { - valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType); - valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType); + valueIfTrue.computeConversion(scope, valueIfTrueType, originalValueIfTrueType); + valueIfFalse.computeConversion(scope, valueIfTrueType, originalValueIfFalseType); return this.resolvedType = valueIfTrueType; } if ((valueIfFalseType == ByteBinding @@ -343,35 +353,35 @@ public class ConditionalExpression extends OperatorExpression { || valueIfFalseType == CharBinding) && (valueIfTrueType == IntBinding && valueIfTrue.isConstantValueOfTypeAssignableToType(valueIfTrueType, valueIfFalseType))) { - valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType); - valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType); + valueIfTrue.computeConversion(scope, valueIfFalseType, originalValueIfTrueType); + valueIfFalse.computeConversion(scope, valueIfFalseType, originalValueIfFalseType); return this.resolvedType = valueIfFalseType; } // Manual binary numeric promotion // int if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_int) && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_int)) { - valueIfTrue.implicitWidening(IntBinding, valueIfTrueType); - valueIfFalse.implicitWidening(IntBinding, valueIfFalseType); + valueIfTrue.computeConversion(scope, IntBinding, originalValueIfTrueType); + valueIfFalse.computeConversion(scope, IntBinding, originalValueIfFalseType); return this.resolvedType = IntBinding; } // long if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_long) && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_long)) { - valueIfTrue.implicitWidening(LongBinding, valueIfTrueType); - valueIfFalse.implicitWidening(LongBinding, valueIfFalseType); + valueIfTrue.computeConversion(scope, LongBinding, originalValueIfTrueType); + valueIfFalse.computeConversion(scope, LongBinding, originalValueIfFalseType); return this.resolvedType = LongBinding; } // float if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_float) && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_float)) { - valueIfTrue.implicitWidening(FloatBinding, valueIfTrueType); - valueIfFalse.implicitWidening(FloatBinding, valueIfFalseType); + valueIfTrue.computeConversion(scope, FloatBinding, originalValueIfTrueType); + valueIfFalse.computeConversion(scope, FloatBinding, originalValueIfFalseType); return this.resolvedType = FloatBinding; } // double - valueIfTrue.implicitWidening(DoubleBinding, valueIfTrueType); - valueIfFalse.implicitWidening(DoubleBinding, valueIfFalseType); + valueIfTrue.computeConversion(scope, DoubleBinding, originalValueIfTrueType); + valueIfFalse.computeConversion(scope, DoubleBinding, originalValueIfFalseType); return this.resolvedType = DoubleBinding; } // Type references (null null is already tested) @@ -384,15 +394,24 @@ public class ConditionalExpression extends OperatorExpression { return null; } if (valueIfFalseType.isCompatibleWith(valueIfTrueType)) { - valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType); - valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType); + valueIfTrue.computeConversion(scope, valueIfTrueType, originalValueIfTrueType); + valueIfFalse.computeConversion(scope, valueIfTrueType, originalValueIfFalseType); return this.resolvedType = valueIfTrueType; } if (valueIfTrueType.isCompatibleWith(valueIfFalseType)) { - valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType); - valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType); + valueIfTrue.computeConversion(scope, valueIfFalseType, originalValueIfTrueType); + valueIfFalse.computeConversion(scope, valueIfFalseType, originalValueIfFalseType); return this.resolvedType = valueIfFalseType; } + // 1.5 addition: allow most common supertype + if (use15specifics) { + TypeBinding commonType = scope.lowerUpperBound(new TypeBinding[] { valueIfTrueType, valueIfFalseType }); + if (commonType != null) { + valueIfTrue.computeConversion(scope, commonType, valueIfTrueType); + valueIfFalse.computeConversion(scope, commonType, valueIfFalseType); + return this.resolvedType = commonType; + } + } scope.problemReporter().conditionalArgumentsIncompatibleTypes( this, valueIfTrueType, diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java b/src/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java index 7170bbf..892b91b 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java @@ -24,8 +24,9 @@ import org.eclipse.jdt.internal.compiler.problem.*; public class ConstructorDeclaration extends AbstractMethodDeclaration { public ExplicitConstructorCall constructorCall; - public final static char[] ConstantPoolName = "".toCharArray(); //$NON-NLS-1$ + public boolean isDefaultConstructor = false; + public TypeParameter[] typeParameters; public ConstructorDeclaration(CompilationResult compilationResult){ super(compilationResult); @@ -76,6 +77,13 @@ public class ConstructorDeclaration extends AbstractMethodDeclaration { } } + // tag parameters as being set + if (this.arguments != null) { + for (int i = 0, count = this.arguments.length; i < count; i++) { + flowInfo.markAsDefinitelyAssigned(this.arguments[i].binding); + } + } + // propagate to constructor call if (constructorCall != null) { // if calling 'this(...)', then flag all non-static fields as definitely @@ -212,7 +220,7 @@ public class ConstructorDeclaration extends AbstractMethodDeclaration { classFile.generateMethodInfoHeader(binding); int methodAttributeOffset = classFile.contentsOffset; - int attributeNumber = classFile.generateMethodInfoAttribute(binding); + int attributeNumber = classFile.generateMethodInfoAttribute(this.binding); if ((!binding.isNative()) && (!binding.isAbstract())) { TypeDeclaration declaringType = classScope.referenceContext; @@ -224,18 +232,19 @@ public class ConstructorDeclaration extends AbstractMethodDeclaration { // initialize local positions - including initializer scope. ReferenceBinding declaringClass = binding.declaringClass; - int argSlotSize = 1; // this==aload0 - + int enumOffset = declaringClass.isEnum() ? 2 : 0; // String name, int ordinal + int argSlotSize = 1 + enumOffset; // this==aload0 + if (declaringClass.isNestedType()){ NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass; this.scope.extraSyntheticArguments = nestedType.syntheticOuterLocalVariables(); scope.computeLocalVariablePositions(// consider synthetic arguments if any - nestedType.enclosingInstancesSlotSize + 1, + nestedType.enclosingInstancesSlotSize + 1 + enumOffset, codeStream); argSlotSize += nestedType.enclosingInstancesSlotSize; argSlotSize += nestedType.outerLocalVariablesSlotSize; } else { - scope.computeLocalVariablePositions(1, codeStream); + scope.computeLocalVariablePositions(1 + enumOffset, codeStream); } if (arguments != null) { @@ -321,13 +330,10 @@ public class ConstructorDeclaration extends AbstractMethodDeclaration { return true; } - /** + /* * Returns true if the constructor is directly involved in a cycle. * Given most constructors aren't, we only allocate the visited list * lazily. - * - * @param visited - * @return */ public boolean isRecursive(ArrayList visited) { @@ -340,7 +346,7 @@ public class ConstructorDeclaration extends AbstractMethodDeclaration { } ConstructorDeclaration targetConstructor = - ((ConstructorDeclaration)this.scope.referenceType().declarationOf(constructorCall.binding)); + ((ConstructorDeclaration)this.scope.referenceType().declarationOf(constructorCall.binding.original())); if (this == targetConstructor) return true; // direct case if (visited == null) { // lazy allocation @@ -359,10 +365,10 @@ public class ConstructorDeclaration extends AbstractMethodDeclaration { //fill up the constructor body with its statements if (ignoreFurtherInvestigation) return; - if (isDefaultConstructor){ - constructorCall = SuperReference.implicitSuperConstructorCall(); - constructorCall.sourceStart = sourceStart; - constructorCall.sourceEnd = sourceEnd; + if (isDefaultConstructor && this.constructorCall == null){ + this.constructorCall = SuperReference.implicitSuperConstructorCall(); + this.constructorCall.sourceStart = this.sourceStart; + this.constructorCall.sourceEnd = this.sourceEnd; return; } parser.parse(this, unit); @@ -406,11 +412,14 @@ public class ConstructorDeclaration extends AbstractMethodDeclaration { scope.problemReporter().missingReturnType(this); } + if (this.binding != null && this.binding.declaringClass.isAnnotationType()) { + scope.problemReporter().annotationTypeDeclarationCannotHaveConstructor(this); + } // if null ==> an error has occurs at parsing time .... if (this.constructorCall != null) { // e.g. using super() in java.lang.Object if (this.binding != null - && this.binding.declaringClass.id == T_Object + && this.binding.declaringClass.id == T_JavaLangObject && this.constructorCall.accessMode != ExplicitConstructorCall.This) { if (this.constructorCall.accessMode == ExplicitConstructorCall.Super) { scope.problemReporter().cannotUseSuperInJavaLangObject(this.constructorCall); @@ -430,7 +439,19 @@ public class ConstructorDeclaration extends AbstractMethodDeclaration { ASTVisitor visitor, ClassScope classScope) { + if (visitor.visit(this, classScope)) { + if (this.annotations != null) { + int annotationsLength = this.annotations.length; + for (int i = 0; i < annotationsLength; i++) + this.annotations[i].traverse(visitor, scope); + } + if (this.typeParameters != null) { + int typeParametersLength = this.typeParameters.length; + for (int i = 0; i < typeParametersLength; i++) { + this.typeParameters[i].traverse(visitor, scope); + } + } if (arguments != null) { int argumentLength = arguments.length; for (int i = 0; i < argumentLength; i++) @@ -451,4 +472,7 @@ public class ConstructorDeclaration extends AbstractMethodDeclaration { } visitor.endVisit(this, classScope); } + public TypeParameter[] typeParameters() { + return this.typeParameters; + } } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/DoStatement.java b/src/org/eclipse/jdt/internal/compiler/ast/DoStatement.java index 0dc995d..a12254a 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/DoStatement.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/DoStatement.java @@ -59,11 +59,12 @@ public class DoStatement extends Statement { int previousMode = flowInfo.reachMode(); + FlowInfo actionInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations(); if ((action != null) && !action.isEmptyBlock()) { - flowInfo = action.analyseCode(currentScope, loopingContext, flowInfo); + actionInfo = action.analyseCode(currentScope, loopingContext, actionInfo); // code generation can be optimized when no need to continue in the loop - if (!flowInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) { + if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) { continueLabel = null; } } @@ -72,24 +73,24 @@ public class DoStatement extends Statement { * do { if (true) break; else blank = 0; } while(false); * blank = 1; // may be initialized already */ - flowInfo.setReachMode(previousMode); + actionInfo.setReachMode(previousMode); - flowInfo = + actionInfo = condition.analyseCode( currentScope, loopingContext, (action == null - ? flowInfo - : (flowInfo.mergedWith(loopingContext.initsOnContinue)))); + ? actionInfo + : (actionInfo.mergedWith(loopingContext.initsOnContinue)))); if (!isConditionOptimizedFalse && continueLabel != null) { - loopingContext.complainOnFinalAssignmentsInLoop(currentScope, flowInfo); + loopingContext.complainOnDeferredChecks(currentScope, actionInfo); } // end of loop FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( loopingContext.initsOnBreak, isConditionOptimizedTrue, - flowInfo.initsWhenFalse(), + actionInfo.initsWhenFalse().addInitializationsFrom(flowInfo), // recover null inits from before condition analysis false, // never consider opt false case for DO loop, since break can always occur (47776) !isConditionTrue /*do{}while(true); unreachable(); */); mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); @@ -155,7 +156,7 @@ public class DoStatement extends Statement { public void resolve(BlockScope scope) { TypeBinding type = condition.resolveTypeExpecting(scope, BooleanBinding); - condition.implicitWidening(type, type); + condition.computeConversion(scope, type, type); if (action != null) action.resolve(scope); } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/DoubleLiteral.java b/src/org/eclipse/jdt/internal/compiler/ast/DoubleLiteral.java index 139c813..1c8a8c1 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/DoubleLiteral.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/DoubleLiteral.java @@ -14,58 +14,100 @@ import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.impl.*; import org.eclipse.jdt.internal.compiler.codegen.*; import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.util.FloatUtil; public class DoubleLiteral extends NumberLiteral { double value; -public DoubleLiteral(char[] token, int s, int e) { - super(token, s,e); -} -public void computeConstant() { - - //the source is correctly formated so the exception should never occurs + public DoubleLiteral(char[] token, int s, int e) { + super(token, s, e); + } + public void computeConstant() { + Double computedValue; + try { + computedValue = Double.valueOf(String.valueOf(source)); + } catch (NumberFormatException e) { + // hex floating point literal + // being rejected by 1.4 libraries where Double.valueOf(...) doesn't handle hex decimal floats + try { + double v = FloatUtil.valueOfHexDoubleLiteral(source); + if (v == Double.POSITIVE_INFINITY) { + // error: the number is too large to represent + return; + } + if (Double.isNaN(v)) { + // error: the number is too small to represent + return; + } + value = v; + constant = Constant.fromValue(v); + } catch (NumberFormatException e1) { + // if the computation of the constant fails + } + return; + } - Double computedValue; - try { computedValue = Double.valueOf(String.valueOf(source));} - catch(NumberFormatException e){return ;} //how can it happen ???? - - if (computedValue.doubleValue() > Double.MAX_VALUE) return ; //may be Infinity - if (computedValue.doubleValue() < Double.MIN_VALUE) - { //only a true 0 can be made of zeros - //2.00000000000000000e-324 is illegal .... - label : - for (int i=0;i Double.MAX_VALUE) { + // error: the number is too large to represent + return; + } + if (doubleValue < Double.MIN_VALUE) { + // see 1F6IGUU + // a true 0 only has '0' and '.' in mantissa + // 1.0e-5000d is non-zero, but underflows to 0 + boolean isHexaDecimal = false; + label : for (int i = 0; i < source.length; i++) { //it is welled formated so just test against '0' and potential . D d + switch (source[i]) { case '0' : case '.' : + break; + case 'x' : + case 'X' : + isHexaDecimal = true; + break; + case 'e' : + case 'E' : + case 'f' : + case 'F' : case 'd' : - case 'D' : break ; - case 'e' : - case 'E' : break label ; //exposant are valid....! - default : return;}}} //error - - constant = Constant.fromValue(value = computedValue.doubleValue());} -/** - * Code generation for the double literak - * - * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope - * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream - * @param valueRequired boolean - */ -public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { - int pc = codeStream.position; - if (valueRequired) - if ((implicitConversion >> 4) == T_double) - codeStream.generateInlinedValue(value); - else + case 'D' : + if (isHexaDecimal) { + return; + } + // starting the exponent - mantissa is all zero + // no exponent - mantissa is all zero + break label; + case 'p' : + case 'P' : + break label; + default : + // error: the number is too small to represent + return; + } + } + } + value = doubleValue; + constant = Constant.fromValue(value); + } + /** + * Code generation for the double literak + * + * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope + * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream + * @param valueRequired boolean + */ + public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { + int pc = codeStream.position; + if (valueRequired) { codeStream.generateConstant(constant, implicitConversion); - codeStream.recordPositionsFrom(pc, this.sourceStart); -} -public TypeBinding literalType(BlockScope scope) { - return DoubleBinding; -} -public void traverse(ASTVisitor visitor, BlockScope blockScope) { - visitor.visit(this, blockScope); - visitor.endVisit(this, blockScope); -} + } + codeStream.recordPositionsFrom(pc, this.sourceStart); + } + public TypeBinding literalType(BlockScope scope) { + return DoubleBinding; + } + public void traverse(ASTVisitor visitor, BlockScope blockScope) { + visitor.visit(this, blockScope); + visitor.endVisit(this, blockScope); + } } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java b/src/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java index 0caeb81..4b2fec9 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; -import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.impl.*; import org.eclipse.jdt.internal.compiler.codegen.*; @@ -19,386 +18,313 @@ import org.eclipse.jdt.internal.compiler.lookup.*; public class EqualExpression extends BinaryExpression { -public EqualExpression(Expression left, Expression right,int operator) { - super(left,right,operator); -} -public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { - if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) { - if (left.constant.booleanValue()) { // true == anything - // this is equivalent to the right argument inits - return right.analyseCode(currentScope, flowContext, flowInfo); - } else { // false == anything - // this is equivalent to the right argument inits negated - return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); - } - } - if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) { - if (right.constant.booleanValue()) { // anything == true - // this is equivalent to the right argument inits - return left.analyseCode(currentScope, flowContext, flowInfo); - } else { // anything == false - // this is equivalent to the right argument inits negated - return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); - } - } - return right.analyseCode( - currentScope, flowContext, - left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits(); - } else { //NOT_EQUAL : - if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) { - if (!left.constant.booleanValue()) { // false != anything - // this is equivalent to the right argument inits - return right.analyseCode(currentScope, flowContext, flowInfo); - } else { // true != anything - // this is equivalent to the right argument inits negated - return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); - } - } - if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) { - if (!right.constant.booleanValue()) { // anything != false - // this is equivalent to the right argument inits - return left.analyseCode(currentScope, flowContext, flowInfo); - } else { // anything != true - // this is equivalent to the right argument inits negated - return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); - } - } - return right.analyseCode( - currentScope, flowContext, - left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).asNegatedCondition().unconditionalInits(); + public EqualExpression(Expression left, Expression right,int operator) { + super(left,right,operator); } -} -public final boolean areTypesCastCompatible(BlockScope scope, TypeBinding castType, TypeBinding expressionType) { - //see specifications 5.5 - //A more complete version of this method is provided on - //CastExpression (it deals with constant and need runtime checkcast) - - if (castType == expressionType) return true; - - //========ARRAY=============== - if (expressionType.isArrayType()) { - if (castType.isArrayType()) { //------- (castTb.isArray) expressionTb.isArray ----------- - TypeBinding expressionEltType = ((ArrayBinding) expressionType).elementsType(scope); - if (expressionEltType.isBaseType()) - // <---stop the recursion------- - return ((ArrayBinding) castType).elementsType(scope) == expressionEltType; - //recursivly on the elts... - return areTypesCastCompatible(scope, ((ArrayBinding) castType).elementsType(scope), expressionEltType); + public void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) { + + LocalVariableBinding local = this.left.localVariableBinding(); + if (local != null) { + checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, right.nullStatus(flowInfo), this.left); } - if (castType.isBaseType()) { - return false; + local = this.right.localVariableBinding(); + if (local != null) { + checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, left.nullStatus(flowInfo), this.right); } - if (castType.isClass()) { //------(castTb.isClass) expressionTb.isArray --------------- - if (castType.id == T_Object) - return true; - return false; - } - if (castType.isInterface()) { //------- (castTb.isInterface) expressionTb.isArray ----------- - if (castType.id == T_JavaLangCloneable || castType.id == T_JavaIoSerializable) { - return true; - } - return false; - } - - return false; } - - //------------(castType) null-------------- - if (expressionType == NullBinding) { - return !castType.isBaseType(); - } - - //========BASETYPE============== - if (expressionType.isBaseType()) { - return false; + private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, LocalVariableBinding local, int nullStatus, Expression reference) { + switch (nullStatus) { + case FlowInfo.NULL : + flowContext.recordUsingNullReference(scope, local, reference, FlowInfo.NULL, flowInfo); + if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { + initsWhenTrue.markAsDefinitelyNull(local); // from thereon it is set + initsWhenFalse.markAsDefinitelyNonNull(local); // from thereon it is set + } else { + initsWhenTrue.markAsDefinitelyNonNull(local); // from thereon it is set + initsWhenFalse.markAsDefinitelyNull(local); // from thereon it is set + } + break; + case FlowInfo.NON_NULL : + flowContext.recordUsingNullReference(scope, local, reference, FlowInfo.NON_NULL, flowInfo); + if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { + initsWhenTrue.markAsDefinitelyNonNull(local); // from thereon it is set + } + break; + } } - - - //========REFERENCE TYPE=================== - - if (expressionType.isClass()) { - if (castType.isArrayType()) { // ---- (castTb.isArray) expressionTb.isClass ------- - if (expressionType.id == T_Object) - return true; - } - if (castType.isBaseType()) { - return false; - } - if (castType.isClass()) { // ----- (castTb.isClass) expressionTb.isClass ------ - if (expressionType.isCompatibleWith(castType)) - return true; - else { - if (castType.isCompatibleWith(expressionType)) { - return true; + + public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { + if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { + if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) { + if (left.constant.booleanValue()) { // true == anything + // this is equivalent to the right argument inits + return right.analyseCode(currentScope, flowContext, flowInfo); + } else { // false == anything + // this is equivalent to the right argument inits negated + return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } - return false; - } - } - if (castType.isInterface()) { // ----- (castTb.isInterface) expressionTb.isClass ------- - if (expressionType.isCompatibleWith(castType)) - return true; - if (!((ReferenceBinding) expressionType).isFinal()) { - return true; } - //no subclass for expressionTb, thus compile-time check is valid - } - - return false; - } - if (expressionType.isInterface()) { - if (castType.isArrayType()) { // ----- (castTb.isArray) expressionTb.isInterface ------ - if (expressionType.id == T_JavaLangCloneable || expressionType.id == T_JavaIoSerializable) - //potential runtime error - { - return true; + if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) { + if (right.constant.booleanValue()) { // anything == true + // this is equivalent to the right argument inits + return left.analyseCode(currentScope, flowContext, flowInfo); + } else { // anything == false + // this is equivalent to the right argument inits negated + return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); + } } - return false; - } - if (castType.isBaseType()) { - return false; - } - if (castType.isClass()) { // ----- (castTb.isClass) expressionTb.isInterface -------- - if (castType.id == T_Object) - return true; - if (((ReferenceBinding) castType).isFinal()) { //no subclass for castTb, thus compile-time check is valid - if (castType.isCompatibleWith(expressionType)) { - return true; + return right.analyseCode( + currentScope, flowContext, + left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits(); + } else { //NOT_EQUAL : + if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) { + if (!left.constant.booleanValue()) { // false != anything + // this is equivalent to the right argument inits + return right.analyseCode(currentScope, flowContext, flowInfo); + } else { // true != anything + // this is equivalent to the right argument inits negated + return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } - return false; } - return true; - } - if (castType.isInterface()) { // ----- (castTb.isInterface) expressionTb.isInterface ------- - if (expressionType.isCompatibleWith(castType)) - return true; - if (!castType.isCompatibleWith(expressionType)) { - MethodBinding[] castTbMethods = ((ReferenceBinding) castType).methods(); - int castTbMethodsLength = castTbMethods.length; - MethodBinding[] expressionTbMethods = ((ReferenceBinding) expressionType).methods(); - int expressionTbMethodsLength = expressionTbMethods.length; - for (int i = 0; i < castTbMethodsLength; i++) { - for (int j = 0; j < expressionTbMethodsLength; j++) { - if (CharOperation.equals(castTbMethods[i].selector, expressionTbMethods[j].selector)) { - if (castTbMethods[i].returnType != expressionTbMethods[j].returnType) { - if (castTbMethods[i].areParametersEqual(expressionTbMethods[j])) { - return false; - } - } - } - } + if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) { + if (!right.constant.booleanValue()) { // anything != false + // this is equivalent to the right argument inits + return left.analyseCode(currentScope, flowContext, flowInfo); + } else { // anything != true + // this is equivalent to the right argument inits negated + return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } } - return true; + return right.analyseCode( + currentScope, flowContext, + left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).asNegatedCondition().unconditionalInits(); } - return false; - } - return false; -} -public final void computeConstant(TypeBinding leftType, TypeBinding rightType) { - if ((this.left.constant != NotAConstant) && (this.right.constant != NotAConstant)) { - this.constant = - Constant.computeConstantOperationEQUAL_EQUAL( - left.constant, - leftType.id, - EQUAL_EQUAL, - right.constant, - rightType.id); - if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL) - constant = Constant.fromValue(!constant.booleanValue()); - } else { - this.constant = NotAConstant; - // no optimization for null == null - } -} -/** - * Normal == or != code generation. - * - * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope - * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream - * @param valueRequired boolean - */ -public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { - - if (constant != NotAConstant) { - int pc = codeStream.position; - if (valueRequired) - codeStream.generateConstant(constant, implicitConversion); - codeStream.recordPositionsFrom(pc, this.sourceStart); - return; } - Label falseLabel; - bits |= OnlyValueRequiredMASK; - generateOptimizedBoolean( - currentScope, - codeStream, - null, - falseLabel = new Label(codeStream), - valueRequired); - if (falseLabel.hasForwardReferences()) { - if (valueRequired){ - // comparison is TRUE - codeStream.iconst_1(); - if ((bits & ValueForReturnMASK) != 0){ - codeStream.ireturn(); - // comparison is FALSE - falseLabel.place(); - codeStream.iconst_0(); - } else { - Label endLabel = new Label(codeStream); - codeStream.goto_(endLabel); - codeStream.decrStackSize(1); - // comparison is FALSE - falseLabel.place(); - codeStream.iconst_0(); - endLabel.place(); - } + + public final void computeConstant(TypeBinding leftType, TypeBinding rightType) { + if ((this.left.constant != NotAConstant) && (this.right.constant != NotAConstant)) { + this.constant = + Constant.computeConstantOperationEQUAL_EQUAL( + left.constant, + leftType.id, + right.constant, + rightType.id); + if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL) + constant = Constant.fromValue(!constant.booleanValue()); } else { - falseLabel.place(); - } - } -} -/** - * Boolean operator code generation - * Optimized operations are: == and != - */ -public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) { - - if (constant != Constant.NotAConstant) { - super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired); - return; + this.constant = NotAConstant; + // no optimization for null == null + } } - if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { - if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) { - generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired); - } else { - generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired); + /** + * Normal == or != code generation. + * + * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope + * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream + * @param valueRequired boolean + */ + public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { + + if (constant != NotAConstant) { + int pc = codeStream.position; + if (valueRequired) + codeStream.generateConstant(constant, implicitConversion); + codeStream.recordPositionsFrom(pc, this.sourceStart); + return; } - } else { - if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) { - generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired); - } else { - generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired); + Label falseLabel; + bits |= OnlyValueRequiredMASK; + generateOptimizedBoolean( + currentScope, + codeStream, + null, + falseLabel = new Label(codeStream), + valueRequired); + if (falseLabel.hasForwardReferences()) { + if (valueRequired){ + // comparison is TRUE + codeStream.iconst_1(); + if ((bits & ValueForReturnMASK) != 0){ + codeStream.ireturn(); + // comparison is FALSE + falseLabel.place(); + codeStream.iconst_0(); + } else { + Label endLabel = new Label(codeStream); + codeStream.goto_(endLabel); + codeStream.decrStackSize(1); + // comparison is FALSE + falseLabel.place(); + codeStream.iconst_0(); + endLabel.place(); + } + codeStream.generateImplicitConversion(implicitConversion); + } else { + falseLabel.place(); + } } } -} -/** - * Boolean generation for == with boolean operands - * - * Note this code does not optimize conditional constants !!!! - */ -public void generateOptimizedBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) { - - // optimized cases: true == x, false == x - if (left.constant != NotAConstant) { - boolean inline = left.constant.booleanValue(); - right.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired); - return; - } // optimized cases: x == true, x == false - if (right.constant != NotAConstant) { - boolean inline = right.constant.booleanValue(); - left.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired); - return; - } - // default case - left.generateCode(currentScope, codeStream, valueRequired); - right.generateCode(currentScope, codeStream, valueRequired); - if (valueRequired) { - if (falseLabel == null) { - if (trueLabel != null) { - // implicit falling through the FALSE case - codeStream.if_icmpeq(trueLabel); + /** + * Boolean operator code generation + * Optimized operations are: == and != + */ + public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) { + + if (constant != Constant.NotAConstant) { + super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired); + return; + } + if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { + if ((left.implicitConversion & COMPILE_TYPE_MASK) /*compile-time*/ == T_boolean) { + generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired); + } else { + generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired); } } else { - // implicit falling through the TRUE case - if (trueLabel == null) { - codeStream.if_icmpne(falseLabel); + if ((left.implicitConversion & COMPILE_TYPE_MASK) /*compile-time*/ == T_boolean) { + generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired); } else { - // no implicit fall through TRUE/FALSE --> should never occur + generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired); } } } - // reposition the endPC - codeStream.updateLastRecordedEndPC(codeStream.position); -} -/** - * Boolean generation for == with non-boolean operands - * - */ -public void generateOptimizedNonBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) { - - int pc = codeStream.position; - Constant inline; - if ((inline = right.constant) != NotAConstant) { - // optimized case: x == 0 - if (((left.implicitConversion >> 4) == T_int) && (inline.intValue() == 0)) { - left.generateCode(currentScope, codeStream, valueRequired); - if (valueRequired) { - if (falseLabel == null) { - if (trueLabel != null) { - // implicit falling through the FALSE case - codeStream.ifeq(trueLabel); - } + /** + * Boolean generation for == with boolean operands + * + * Note this code does not optimize conditional constants !!!! + */ + public void generateOptimizedBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) { + + // optimized cases: true == x, false == x + if (left.constant != NotAConstant) { + boolean inline = left.constant.booleanValue(); + right.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired); + return; + } // optimized cases: x == true, x == false + if (right.constant != NotAConstant) { + boolean inline = right.constant.booleanValue(); + left.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired); + return; + } + // default case + left.generateCode(currentScope, codeStream, valueRequired); + right.generateCode(currentScope, codeStream, valueRequired); + if (valueRequired) { + if (falseLabel == null) { + if (trueLabel != null) { + // implicit falling through the FALSE case + codeStream.if_icmpeq(trueLabel); + } + } else { + // implicit falling through the TRUE case + if (trueLabel == null) { + codeStream.if_icmpne(falseLabel); } else { - // implicit falling through the TRUE case - if (trueLabel == null) { - codeStream.ifne(falseLabel); - } else { - // no implicit fall through TRUE/FALSE --> should never occur - } + // no implicit fall through TRUE/FALSE --> should never occur } } - codeStream.recordPositionsFrom(pc, this.sourceStart); - return; } + // reposition the endPC + codeStream.updateLastRecordedEndPC(codeStream.position); } - if ((inline = left.constant) != NotAConstant) { - // optimized case: 0 == x - if (((left.implicitConversion >> 4) == T_int) - && (inline.intValue() == 0)) { - right.generateCode(currentScope, codeStream, valueRequired); - if (valueRequired) { - if (falseLabel == null) { - if (trueLabel != null) { - // implicit falling through the FALSE case - codeStream.ifeq(trueLabel); + /** + * Boolean generation for == with non-boolean operands + * + */ + public void generateOptimizedNonBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) { + + int pc = codeStream.position; + Constant inline; + if ((inline = right.constant) != NotAConstant) { + // optimized case: x == 0 + if ((((left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) == T_int) && (inline.intValue() == 0)) { + left.generateCode(currentScope, codeStream, valueRequired); + if (valueRequired) { + if (falseLabel == null) { + if (trueLabel != null) { + // implicit falling through the FALSE case + codeStream.ifeq(trueLabel); + } + } else { + // implicit falling through the TRUE case + if (trueLabel == null) { + codeStream.ifne(falseLabel); + } else { + // no implicit fall through TRUE/FALSE --> should never occur + } } - } else { - // implicit falling through the TRUE case - if (trueLabel == null) { - codeStream.ifne(falseLabel); + } + codeStream.recordPositionsFrom(pc, this.sourceStart); + return; + } + } + if ((inline = left.constant) != NotAConstant) { + // optimized case: 0 == x + if ((((left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) == T_int) + && (inline.intValue() == 0)) { + right.generateCode(currentScope, codeStream, valueRequired); + if (valueRequired) { + if (falseLabel == null) { + if (trueLabel != null) { + // implicit falling through the FALSE case + codeStream.ifeq(trueLabel); + } } else { - // no implicit fall through TRUE/FALSE --> should never occur + // implicit falling through the TRUE case + if (trueLabel == null) { + codeStream.ifne(falseLabel); + } else { + // no implicit fall through TRUE/FALSE --> should never occur + } } } + codeStream.recordPositionsFrom(pc, this.sourceStart); + return; } - codeStream.recordPositionsFrom(pc, this.sourceStart); - return; } - } - // null cases - // optimized case: x == null - if (right instanceof NullLiteral) { - if (left instanceof NullLiteral) { - // null == null - if (valueRequired) { - if ((bits & OnlyValueRequiredMASK) != 0) { - if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { - codeStream.iconst_1(); + // null cases + // optimized case: x == null + if (right instanceof NullLiteral) { + if (left instanceof NullLiteral) { + // null == null + if (valueRequired) { + if ((bits & OnlyValueRequiredMASK) != 0) { + if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { + codeStream.iconst_1(); + } else { + codeStream.iconst_0(); + } } else { - codeStream.iconst_0(); + if (falseLabel == null) { + // implicit falling through the FALSE case + if (trueLabel != null) { + codeStream.goto_(trueLabel); + } + } + } + } + } else { + left.generateCode(currentScope, codeStream, valueRequired); + if (valueRequired) { + if (falseLabel == null) { + if (trueLabel != null) { + // implicit falling through the FALSE case + codeStream.ifnull(trueLabel); } } else { - if (falseLabel == null) { - // implicit falling through the FALSE case - if (trueLabel != null) { - codeStream.goto_(trueLabel); - } + // implicit falling through the TRUE case + if (trueLabel == null) { + codeStream.ifnonnull(falseLabel); + } else { + // no implicit fall through TRUE/FALSE --> should never occur } + } } } - } else { - left.generateCode(currentScope, codeStream, valueRequired); + codeStream.recordPositionsFrom(pc, this.sourceStart); + return; + } else if (left instanceof NullLiteral) { // optimized case: null == x + right.generateCode(currentScope, codeStream, valueRequired); if (valueRequired) { if (falseLabel == null) { if (trueLabel != null) { @@ -414,165 +340,162 @@ public void generateOptimizedNonBooleanEqual(BlockScope currentScope, CodeStream } } } + codeStream.recordPositionsFrom(pc, this.sourceStart); + return; } - codeStream.recordPositionsFrom(pc, this.sourceStart); - return; - } else if (left instanceof NullLiteral) { // optimized case: null == x + + // default case + left.generateCode(currentScope, codeStream, valueRequired); right.generateCode(currentScope, codeStream, valueRequired); if (valueRequired) { if (falseLabel == null) { if (trueLabel != null) { // implicit falling through the FALSE case - codeStream.ifnull(trueLabel); + switch ((left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) { // operand runtime type + case T_int : + codeStream.if_icmpeq(trueLabel); + break; + case T_float : + codeStream.fcmpl(); + codeStream.ifeq(trueLabel); + break; + case T_long : + codeStream.lcmp(); + codeStream.ifeq(trueLabel); + break; + case T_double : + codeStream.dcmpl(); + codeStream.ifeq(trueLabel); + break; + default : + codeStream.if_acmpeq(trueLabel); + } } } else { // implicit falling through the TRUE case if (trueLabel == null) { - codeStream.ifnonnull(falseLabel); + switch ((left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) { // operand runtime type + case T_int : + codeStream.if_icmpne(falseLabel); + break; + case T_float : + codeStream.fcmpl(); + codeStream.ifne(falseLabel); + break; + case T_long : + codeStream.lcmp(); + codeStream.ifne(falseLabel); + break; + case T_double : + codeStream.dcmpl(); + codeStream.ifne(falseLabel); + break; + default : + codeStream.if_acmpne(falseLabel); + } } else { // no implicit fall through TRUE/FALSE --> should never occur } } } codeStream.recordPositionsFrom(pc, this.sourceStart); - return; } - - // default case - left.generateCode(currentScope, codeStream, valueRequired); - right.generateCode(currentScope, codeStream, valueRequired); - if (valueRequired) { - if (falseLabel == null) { - if (trueLabel != null) { - // implicit falling through the FALSE case - switch (left.implicitConversion >> 4) { // operand runtime type - case T_int : - codeStream.if_icmpeq(trueLabel); - break; - case T_float : - codeStream.fcmpl(); - codeStream.ifeq(trueLabel); - break; - case T_long : - codeStream.lcmp(); - codeStream.ifeq(trueLabel); - break; - case T_double : - codeStream.dcmpl(); - codeStream.ifeq(trueLabel); - break; - default : - codeStream.if_acmpeq(trueLabel); - } - } - } else { - // implicit falling through the TRUE case - if (trueLabel == null) { - switch (left.implicitConversion >> 4) { // operand runtime type - case T_int : - codeStream.if_icmpne(falseLabel); - break; - case T_float : - codeStream.fcmpl(); - codeStream.ifne(falseLabel); - break; - case T_long : - codeStream.lcmp(); - codeStream.ifne(falseLabel); - break; - case T_double : - codeStream.dcmpl(); - codeStream.ifne(falseLabel); - break; - default : - codeStream.if_acmpne(falseLabel); - } - } else { - // no implicit fall through TRUE/FALSE --> should never occur - } - } - } - codeStream.recordPositionsFrom(pc, this.sourceStart); -} -public boolean isCompactableOperation() { - return false; -} -public TypeBinding resolveType(BlockScope scope) { - - boolean leftIsCast, rightIsCast; - if ((leftIsCast = left instanceof CastExpression) == true) left.bits |= IgnoreNeedForCastCheckMASK; // will check later on - TypeBinding leftType = left.resolveType(scope); - - if ((rightIsCast = right instanceof CastExpression) == true) right.bits |= IgnoreNeedForCastCheckMASK; // will check later on - TypeBinding rightType = right.resolveType(scope); - - // always return BooleanBinding - if (leftType == null || rightType == null){ - constant = NotAConstant; - return null; + public boolean isCompactableOperation() { + return false; } - - // both base type - if (leftType.isBaseType() && rightType.isBaseType()) { - // the code is an int - // (cast) left == (cast) right --> result - // 0000 0000 0000 0000 0000 - // <<16 <<12 <<8 <<4 <<0 - int operatorSignature = OperatorSignatures[EQUAL_EQUAL][ (leftType.id << 4) + rightType.id]; - left.implicitConversion = operatorSignature >>> 12; - right.implicitConversion = (operatorSignature >>> 4) & 0x000FF; - bits |= operatorSignature & 0xF; - if ((operatorSignature & 0x0000F) == T_undefined) { - constant = Constant.NotAConstant; - scope.problemReporter().invalidOperator(this, leftType, rightType); + public TypeBinding resolveType(BlockScope scope) { + + boolean leftIsCast, rightIsCast; + if ((leftIsCast = left instanceof CastExpression) == true) left.bits |= IgnoreNeedForCastCheckMASK; // will check later on + TypeBinding originalLeftType = left.resolveType(scope); + + if ((rightIsCast = right instanceof CastExpression) == true) right.bits |= IgnoreNeedForCastCheckMASK; // will check later on + TypeBinding originalRightType = right.resolveType(scope); + + // always return BooleanBinding + if (originalLeftType == null || originalRightType == null){ + constant = NotAConstant; return null; } - // check need for operand cast - if (leftIsCast || rightIsCast) { - CastExpression.checkNeedForArgumentCasts(scope, EQUAL_EQUAL, operatorSignature, left, leftType.id, leftIsCast, right, rightType.id, rightIsCast); + + // autoboxing support + LookupEnvironment env = scope.environment(); + boolean use15specifics = env.options.sourceLevel >= JDK1_5; + TypeBinding leftType = originalLeftType, rightType = originalRightType; + if (use15specifics) { + if (leftType != NullBinding && leftType.isBaseType()) { + if (!rightType.isBaseType()) { + rightType = env.computeBoxingType(rightType); + } + } else { + if (rightType != NullBinding && rightType.isBaseType()) { + leftType = env.computeBoxingType(leftType); + } + } } - computeConstant(leftType, rightType); - return this.resolvedType = BooleanBinding; - } - - // Object references - // spec 15.20.3 - if (areTypesCastCompatible(scope, rightType, leftType) || areTypesCastCompatible(scope, leftType, rightType)) { - // (special case for String) - if ((rightType.id == T_String) && (leftType.id == T_String)) { + // both base type + if (leftType.isBaseType() && rightType.isBaseType()) { + int leftTypeID = leftType.id; + int rightTypeID = rightType.id; + + // the code is an int + // (cast) left == (cast) right --> result + // 0000 0000 0000 0000 0000 + // <<16 <<12 <<8 <<4 <<0 + int operatorSignature = OperatorSignatures[EQUAL_EQUAL][ (leftTypeID << 4) + rightTypeID]; + left.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F), originalLeftType); + right.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 8) & 0x0000F), originalRightType); + bits |= operatorSignature & 0xF; + if ((operatorSignature & 0x0000F) == T_undefined) { + constant = Constant.NotAConstant; + scope.problemReporter().invalidOperator(this, leftType, rightType); + return null; + } + // check need for operand cast + if (leftIsCast || rightIsCast) { + CastExpression.checkNeedForArgumentCasts(scope, EQUAL_EQUAL, operatorSignature, left, leftType.id, leftIsCast, right, rightType.id, rightIsCast); + } computeConstant(leftType, rightType); - } else { - constant = NotAConstant; - } - if (rightType.id == T_String) { - right.implicitConversion = String2String; - } - if (leftType.id == T_String) { - left.implicitConversion = String2String; + return this.resolvedType = BooleanBinding; } - // check need for operand cast - boolean unnecessaryLeftCast = (left.bits & UnnecessaryCastMask) != 0; - boolean unnecessaryRightCast = (right.bits & UnnecessaryCastMask) != 0; - if (unnecessaryLeftCast || unnecessaryRightCast) { - TypeBinding alternateLeftType = unnecessaryLeftCast ? ((CastExpression)left).expression.resolvedType : leftType; - TypeBinding alternateRightType = unnecessaryRightCast ? ((CastExpression)right).expression.resolvedType : rightType; - if (areTypesCastCompatible(scope, alternateLeftType, alternateRightType) - || areTypesCastCompatible(scope, alternateRightType, alternateLeftType)) { - if (unnecessaryLeftCast) scope.problemReporter().unnecessaryCast((CastExpression)left); - if (unnecessaryRightCast) scope.problemReporter().unnecessaryCast((CastExpression)right); + + // Object references + // spec 15.20.3 + if (this.checkCastTypesCompatibility(scope, leftType, rightType, null) + || this.checkCastTypesCompatibility(scope, rightType, leftType, null)) { + + // (special case for String) + if ((rightType.id == T_JavaLangString) && (leftType.id == T_JavaLangString)) { + computeConstant(leftType, rightType); + } else { + constant = NotAConstant; } + TypeBinding objectType = scope.getJavaLangObject(); + left.computeConversion(scope, objectType, leftType); + right.computeConversion(scope, objectType, rightType); + // check need for operand cast + boolean unnecessaryLeftCast = (left.bits & UnnecessaryCastMask) != 0; + boolean unnecessaryRightCast = (right.bits & UnnecessaryCastMask) != 0; + if (unnecessaryLeftCast || unnecessaryRightCast) { + TypeBinding alternateLeftType = unnecessaryLeftCast ? ((CastExpression)left).expression.resolvedType : leftType; + TypeBinding alternateRightType = unnecessaryRightCast ? ((CastExpression)right).expression.resolvedType : rightType; + if (this.checkCastTypesCompatibility(scope, alternateLeftType, alternateRightType, null) + || this.checkCastTypesCompatibility(scope, alternateRightType, alternateLeftType, null)) { + if (unnecessaryLeftCast) scope.problemReporter().unnecessaryCast((CastExpression)left); + if (unnecessaryRightCast) scope.problemReporter().unnecessaryCast((CastExpression)right); + } + } + return this.resolvedType = BooleanBinding; } - return this.resolvedType = BooleanBinding; + constant = NotAConstant; + scope.problemReporter().notCompatibleTypesError(this, leftType, rightType); + return null; } - constant = NotAConstant; - scope.problemReporter().notCompatibleTypesError(this, leftType, rightType); - return null; -} -public void traverse(ASTVisitor visitor, BlockScope scope) { - if (visitor.visit(this, scope)) { - left.traverse(visitor, scope); - right.traverse(visitor, scope); + public void traverse(ASTVisitor visitor, BlockScope scope) { + if (visitor.visit(this, scope)) { + left.traverse(visitor, scope); + right.traverse(visitor, scope); + } + visitor.endVisit(this, scope); } - visitor.endVisit(this, scope); -} } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java b/src/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java index ec709ff..a0b814e 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java @@ -15,24 +15,26 @@ import org.eclipse.jdt.internal.compiler.codegen.*; import org.eclipse.jdt.internal.compiler.flow.*; import org.eclipse.jdt.internal.compiler.lookup.*; -public class ExplicitConstructorCall - extends Statement - implements InvocationSite { +public class ExplicitConstructorCall extends Statement implements InvocationSite { public Expression[] arguments; public Expression qualification; - public MethodBinding binding; - + public MethodBinding binding; // exact binding resulting from lookup + protected MethodBinding codegenBinding; // actual binding used for code generation (if no synthetic accessor) + MethodBinding syntheticAccessor; // synthetic accessor for inner-emulation public int accessMode; - + public TypeReference[] typeArguments; + public TypeBinding[] genericTypeArguments; + public final static int ImplicitSuper = 1; public final static int Super = 2; public final static int This = 3; public VariableBinding[][] implicitArguments; boolean discardEnclosingInstance; - - MethodBinding syntheticAccessor; + + // TODO Remove once DOMParser is activated + public int typeArgumentsSourceStart; public ExplicitConstructorCall(int accessMode) { this.accessMode = accessMode; @@ -101,8 +103,14 @@ public class ExplicitConstructorCall int pc = codeStream.position; codeStream.aload_0(); + ReferenceBinding targetType = this.codegenBinding.declaringClass; + + // special name&ordinal argument generation for enum constructors + if (targetType.erasure().id == T_JavaLangEnum || targetType.isEnum()) { + codeStream.aload_1(); // pass along name param as name arg + codeStream.iload_2(); // pass along ordinal param as ordinal arg + } // handling innerclass constructor invocation - ReferenceBinding targetType = binding.declaringClass; // handling innerclass instance allocation - enclosing instance arguments if (targetType.isNestedType()) { codeStream.generateSyntheticEnclosingInstanceValues( @@ -111,12 +119,9 @@ public class ExplicitConstructorCall discardEnclosingInstance ? null : qualification, this); } - // regular code gen - if (arguments != null) { - for (int i = 0, max = arguments.length; i < max; i++) { - arguments[i].generateCode(currentScope, codeStream, true); - } - } + // generate arguments + generateArguments(binding, arguments, currentScope, codeStream); + // handling innerclass instance allocation - outer local arguments if (targetType.isNestedType()) { codeStream.generateSyntheticOuterArgumentValues( @@ -127,21 +132,26 @@ public class ExplicitConstructorCall if (syntheticAccessor != null) { // synthetic accessor got some extra arguments appended to its signature, which need values for (int i = 0, - max = syntheticAccessor.parameters.length - binding.parameters.length; + max = syntheticAccessor.parameters.length - this.codegenBinding.parameters.length; i < max; i++) { codeStream.aconst_null(); } codeStream.invokespecial(syntheticAccessor); } else { - codeStream.invokespecial(binding); + codeStream.invokespecial(this.codegenBinding); } codeStream.recordPositionsFrom(pc, this.sourceStart); } finally { ((MethodScope) currentScope).isConstructorCall = false; } } - + /** + * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments() + */ + public TypeBinding[] genericTypeArguments() { + return this.genericTypeArguments; + } public boolean isImplicitSuper() { //return true if I'm of these compiler added statement super(); @@ -166,18 +176,18 @@ public class ExplicitConstructorCall * exact need. */ void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { - ReferenceBinding superType; + ReferenceBinding superTypeErasure = (ReferenceBinding) binding.declaringClass.erasure(); if (!flowInfo.isReachable()) return; // perform some emulation work in case there is some and we are inside a local type only - if ((superType = binding.declaringClass).isNestedType() + if (superTypeErasure.isNestedType() && currentScope.enclosingSourceType().isLocalType()) { - if (superType.isLocalType()) { - ((LocalTypeBinding) superType).addInnerEmulationDependent(currentScope, qualification != null); + if (superTypeErasure.isLocalType()) { + ((LocalTypeBinding) superTypeErasure).addInnerEmulationDependent(currentScope, qualification != null); } else { // locally propagate, since we already now the desired shape for sure - currentScope.propagateInnerEmulation(superType, qualification != null); + currentScope.propagateInnerEmulation(superTypeErasure, qualification != null); } } } @@ -185,19 +195,19 @@ public class ExplicitConstructorCall public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { if (!flowInfo.isReachable()) return; + // if constructor from parameterized type got found, use the original constructor at codegen time + this.codegenBinding = this.binding.original(); + // perform some emulation work in case there is some and we are inside a local type only - if (binding.isPrivate() && (accessMode != This)) { + if (binding.isPrivate() && accessMode != This) { - if (currentScope - .environment() - .options - .isPrivateConstructorAccessChangingVisibility) { - binding.tagForClearingPrivateModifier(); + if (currentScope.environment().options.isPrivateConstructorAccessChangingVisibility) { + this.codegenBinding.tagForClearingPrivateModifier(); // constructor will not be dumped as private, no emulation required thus } else { syntheticAccessor = - ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, isSuperAccess()); - currentScope.problemReporter().needToEmulateMethodAccess(binding, this); + ((SourceTypeBinding) this.codegenBinding.declaringClass).addSyntheticMethod(this.codegenBinding, isSuperAccess()); + currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this); } } } @@ -206,6 +216,16 @@ public class ExplicitConstructorCall printIndent(indent, output); if (qualification != null) qualification.printExpression(0, output).append('.'); + if (typeArguments != null) { + output.append('<');//$NON-NLS-1$ + int max = typeArguments.length - 1; + for (int j = 0; j < max; j++) { + typeArguments[j].print(0, output); + output.append(", ");//$NON-NLS-1$ + } + typeArguments[max].print(0, output); + output.append('>'); + } if (accessMode == This) { output.append("this("); //$NON-NLS-1$ } else { @@ -243,7 +263,10 @@ public class ExplicitConstructorCall if (receiverType == null) { return; } - + // prevent (explicit) super constructor invocation from within enum + if (this.accessMode == Super && receiverType.erasure().id == T_JavaLangEnum) { + scope.problemReporter().cannotInvokeSuperConstructorInEnum(this, methodScope.referenceMethod().binding); + } // qualification should be from the type of the enclosingType if (qualification != null) { if (accessMode != Super) { @@ -259,10 +282,24 @@ public class ExplicitConstructorCall discardEnclosingInstance = true; } else { TypeBinding qTb = qualification.resolveTypeExpecting(scope, enclosingType); - qualification.implicitWidening(qTb, qTb); + qualification.computeConversion(scope, qTb, qTb); } } - + // resolve type arguments (for generic constructor call) + if (this.typeArguments != null) { + int length = this.typeArguments.length; + boolean argHasError = false; // typeChecks all arguments + this.genericTypeArguments = new TypeBinding[length]; + for (int i = 0; i < length; i++) { + if ((this.genericTypeArguments[i] = this.typeArguments[i].resolveType(scope, true /* check bounds*/)) == null) { + argHasError = true; + } + } + if (argHasError) { + return; + } + } + // arguments buffering for the method lookup TypeBinding[] argumentTypes = NoParameters; boolean argsContainCast = false; @@ -283,24 +320,17 @@ public class ExplicitConstructorCall if (argHasError) { return; } + } else if (receiverType.erasure().id == T_JavaLangEnum) { + // TODO (philippe) get rid of once well-known binding is available + argumentTypes = new TypeBinding[] { scope.getJavaLangString(), BaseTypes.IntBinding }; } if ((binding = scope.getConstructor(receiverType, argumentTypes, this)).isValidBinding()) { if (isMethodUseDeprecated(binding, scope)) scope.problemReporter().deprecatedMethod(binding, this); - - // see for user-implicit widening conversion - if (arguments != null) { - int length = arguments.length; - TypeBinding[] paramTypes = binding.parameters; - for (int i = 0; i < length; i++) { - arguments[i].implicitWidening(paramTypes[i], argumentTypes[i]); - } - if (argsContainCast) { - CastExpression.checkNeedForArgumentCasts(scope, null, receiverType, binding, this.arguments, argumentTypes, this); - } - } + if (this.arguments != null) + checkInvocationArguments(scope, null, receiverType, binding, this.arguments, argumentTypes, argsContainCast, this); if (binding.isPrivate()) { - binding.modifiers |= AccPrivateUsed; + binding.original().modifiers |= AccPrivateUsed; } } else { if (binding.declaringClass == null) @@ -330,6 +360,11 @@ public class ExplicitConstructorCall if (this.qualification != null) { this.qualification.traverse(visitor, scope); } + if (this.typeArguments != null) { + for (int i = 0, typeArgumentsLength = this.typeArguments.length; i < typeArgumentsLength; i++) { + this.typeArguments[i].traverse(visitor, scope); + } + } if (this.arguments != null) { for (int i = 0, argumentLength = this.arguments.length; i < argumentLength; i++) this.arguments[i].traverse(visitor, scope); diff --git a/src/org/eclipse/jdt/internal/compiler/ast/Expression.java b/src/org/eclipse/jdt/internal/compiler/ast/Expression.java index ae3b502..df2538d 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/Expression.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/Expression.java @@ -10,6 +10,10 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import java.util.ArrayList; + +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.impl.*; import org.eclipse.jdt.internal.compiler.codegen.*; import org.eclipse.jdt.internal.compiler.flow.*; @@ -19,41 +23,6 @@ import org.eclipse.jdt.internal.compiler.util.Util; public abstract class Expression extends Statement { - //Some expression may not be used - from a java semantic point - //of view only - as statements. Other may. In order to avoid the creation - //of wrappers around expression in order to tune them as expression - //Expression is a subclass of Statement. See the message isValidJavaStatement() - - public int implicitConversion; - public TypeBinding resolvedType; - - public Constant constant; - - public Expression() { - super(); - } - - public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - - return flowInfo; - } - - public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) { - - return analyseCode(currentScope, flowContext, flowInfo); - } - - /** - * Constant usable for bytecode pattern optimizations, but cannot be inlined - * since it is not strictly equivalent to the definition of constant expressions. - * In particular, some side-effects may be required to occur (only the end value - * is known). - * @return Constant known to be of boolean type - */ - public Constant optimizedBooleanConstant() { - return this.constant; - } - public static final boolean isConstantValueRepresentable( Constant constant, int constantTypeID, @@ -208,7 +177,345 @@ public abstract class Expression extends Statement { return false; //boolean } } + + public Constant constant; + + //Some expression may not be used - from a java semantic point + //of view only - as statements. Other may. In order to avoid the creation + //of wrappers around expression in order to tune them as expression + //Expression is a subclass of Statement. See the message isValidJavaStatement() + + public int implicitConversion; + public TypeBinding resolvedType; + + public Expression() { + super(); + } + + public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { + + return flowInfo; + } + + public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) { + + return analyseCode(currentScope, flowContext, flowInfo); + } + + /** + * Returns false if cast is not legal. + */ + public final boolean checkCastTypesCompatibility( + Scope scope, + TypeBinding castType, + TypeBinding expressionType, + Expression expression) { + + // see specifications 5.5 + // handle errors and process constant when needed + + // if either one of the type is null ==> + // some error has been already reported some where ==> + // we then do not report an obvious-cascade-error. + + if (castType == null || expressionType == null) return true; + + // identity conversion cannot be performed upfront, due to side-effects + // like constant propagation + LookupEnvironment env = scope.environment(); + boolean use15specifics = env.options.sourceLevel >= JDK1_5; + if (castType.isBaseType()) { + if (expressionType.isBaseType()) { + if (expressionType == castType) { + if (expression != null) { + this.constant = expression.constant; //use the same constant + } + tagAsUnnecessaryCast(scope, castType); + return true; + } + boolean necessary = false; + if (expressionType.isCompatibleWith(castType) + || (necessary = BaseTypeBinding.isNarrowing(castType.id, expressionType.id))) { + if (expression != null) { + expression.implicitConversion = (castType.id << 4) + expressionType.id; + if (expression.constant != Constant.NotAConstant) { + constant = expression.constant.castTo(expression.implicitConversion); + } + } + if (!necessary) tagAsUnnecessaryCast(scope, castType); + return true; + + } + } else if (use15specifics) { // unboxing - only exact match is allowed + if (env.computeBoxingType(expressionType) == castType) { + // TODO (philippe) could tagAsUnnecessaryCast(scope, castType); + return true; + } + } + reportIllegalCast(scope, castType, expressionType); + return false; + } else if (use15specifics && expressionType.isBaseType()) { // boxing - only exact match is allowed + if (env.computeBoxingType(castType) == expressionType) { + // TODO (philippe) could tagAsUnnecessaryCast(scope, castType); + return true; + } + } + + //-----------cast to something which is NOT a base type-------------------------- + if (expressionType == NullBinding) { + tagAsUnnecessaryCast(scope, castType); + return true; //null is compatible with every thing + } + if (expressionType.isBaseType()) { + reportIllegalCast(scope, castType, expressionType); + return false; + } + + if (expressionType.isArrayType()) { + if (castType == expressionType) { + tagAsUnnecessaryCast(scope, castType); + return true; // identity conversion + } + + if (castType.isArrayType()) { + //------- (castType.isArray) expressionType.isArray ----------- + TypeBinding exprElementType = ((ArrayBinding) expressionType).elementsType(); + if (exprElementType.isBaseType()) { + // <---stop the recursion------- + if (((ArrayBinding) castType).elementsType() == exprElementType) { + tagAsNeedCheckCast(); + return true; + } else { + reportIllegalCast(scope, castType, expressionType); + return false; + } + } + // recursively on the elements... + return checkCastTypesCompatibility( + scope, + ((ArrayBinding) castType).elementsType(), + exprElementType, + expression); + } else if ( + castType.isClass()) { + //------(castType.isClass) expressionType.isArray --------------- + if (castType.id == T_JavaLangObject) { + tagAsUnnecessaryCast(scope, castType); + return true; + } + } else { //------- (castType.isInterface) expressionType.isArray ----------- + if (castType.id == T_JavaLangCloneable || castType.id == T_JavaIoSerializable) { + tagAsNeedCheckCast(); + return true; + } + } + reportIllegalCast(scope, castType, expressionType); + return false; + } + + if (expressionType.isClass()) { + if (castType.isArrayType()) { + // ---- (castType.isArray) expressionType.isClass ------- + if (expressionType.id == T_JavaLangObject) { // potential runtime error + tagAsNeedCheckCast(); + return true; + } + } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isClass ------ + + ReferenceBinding match = ((ReferenceBinding)expressionType).findSuperTypeErasingTo((ReferenceBinding)castType.erasure()); + if (match != null) { + if (expression != null && castType.id == T_JavaLangString) this.constant = expression.constant; // (String) cst is still a constant + return checkUnsafeCast(scope, castType, expressionType, match, false); + } + match = ((ReferenceBinding)castType).findSuperTypeErasingTo((ReferenceBinding)expressionType.erasure()); + if (match != null) { + tagAsNeedCheckCast(); + return checkUnsafeCast(scope, castType, expressionType, match, true); + } + } else { // ----- (castType.isInterface) expressionType.isClass ------- + + ReferenceBinding match = ((ReferenceBinding)expressionType).findSuperTypeErasingTo((ReferenceBinding)castType.erasure()); + if (match != null) { + return checkUnsafeCast(scope, castType, expressionType, match, false); + } + // a subclass may implement the interface ==> no check at compile time + if (!((ReferenceBinding) expressionType).isFinal()) { + tagAsNeedCheckCast(); + match = ((ReferenceBinding)castType).findSuperTypeErasingTo((ReferenceBinding)expressionType.erasure()); + if (match != null) { + return checkUnsafeCast(scope, castType, expressionType, match, true); + } + return true; + } + // no subclass for expressionType, thus compile-time check is valid + } + reportIllegalCast(scope, castType, expressionType); + return false; + } + + // if (expressionType.isInterface()) { cannot be anything else + if (castType.isArrayType()) { + // ----- (castType.isArray) expressionType.isInterface ------ + if (expressionType.id == T_JavaLangCloneable + || expressionType.id == T_JavaIoSerializable) {// potential runtime error + tagAsNeedCheckCast(); + return true; + } else { + reportIllegalCast(scope, castType, expressionType); + return false; + } + } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isInterface -------- + + if (castType.id == T_JavaLangObject) { // no runtime error + tagAsUnnecessaryCast(scope, castType); + return true; + } + if (((ReferenceBinding) castType).isFinal()) { + // no subclass for castType, thus compile-time check is valid + ReferenceBinding match = ((ReferenceBinding)castType).findSuperTypeErasingTo((ReferenceBinding)expressionType.erasure()); + if (match == null) { + // potential runtime error + reportIllegalCast(scope, castType, expressionType); + return false; + } + } + } else { // ----- (castType.isInterface) expressionType.isInterface ------- + + ReferenceBinding match = ((ReferenceBinding)expressionType).findSuperTypeErasingTo((ReferenceBinding)castType.erasure()); + if (match != null) { + return checkUnsafeCast(scope, castType, expressionType, match, false); + } + + match = ((ReferenceBinding)castType).findSuperTypeErasingTo((ReferenceBinding)expressionType.erasure()); + if (match != null) { + tagAsNeedCheckCast(); + return checkUnsafeCast(scope, castType, expressionType, match, true); + } else { + MethodBinding[] castTypeMethods = getAllInheritedMethods((ReferenceBinding) castType); + MethodBinding[] expressionTypeMethods = + getAllInheritedMethods((ReferenceBinding) expressionType); + int exprMethodsLength = expressionTypeMethods.length; + for (int i = 0, castMethodsLength = castTypeMethods.length; i < castMethodsLength; i++) { + for (int j = 0; j < exprMethodsLength; j++) { + if ((castTypeMethods[i].returnType != expressionTypeMethods[j].returnType) + && (CharOperation.equals(castTypeMethods[i].selector, expressionTypeMethods[j].selector)) + && castTypeMethods[i].areParametersEqual(expressionTypeMethods[j])) { + reportIllegalCast(scope, castType, expressionType); + return false; + + } + } + } + } + } + tagAsNeedCheckCast(); + return true; + } + + public FlowInfo checkNullStatus(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int nullStatus) { + + LocalVariableBinding local = this.localVariableBinding(); + if (local != null) { + switch(nullStatus) { + case FlowInfo.NULL : + flowContext.recordUsingNullReference(scope, local, this, FlowInfo.NULL, flowInfo); + flowInfo.markAsDefinitelyNull(local); // from thereon it is set + break; + case FlowInfo.NON_NULL : + flowContext.recordUsingNullReference(scope, local, this, FlowInfo.NON_NULL, flowInfo); + flowInfo.markAsDefinitelyNonNull(local); // from thereon it is set + break; + case FlowInfo.UNKNOWN : + break; + } + } + return flowInfo; + } + + private MethodBinding[] getAllInheritedMethods(ReferenceBinding binding) { + ArrayList collector = new ArrayList(); + getAllInheritedMethods0(binding, collector); + return (MethodBinding[]) collector.toArray(new MethodBinding[collector.size()]); + } + + private void getAllInheritedMethods0(ReferenceBinding binding, ArrayList collector) { + if (!binding.isInterface()) return; + MethodBinding[] methodBindings = binding.methods(); + for (int i = 0, max = methodBindings.length; i < max; i++) { + collector.add(methodBindings[i]); + } + ReferenceBinding[] superInterfaces = binding.superInterfaces(); + for (int i = 0, max = superInterfaces.length; i < max; i++) { + getAllInheritedMethods0(superInterfaces[i], collector); + } + } + public void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) { + // do nothing by default - see EqualExpression + } + + public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) { + if (match == castType) { + if (!isNarrowing) tagAsUnnecessaryCast(scope, castType); + return true; + } + if (castType.isBoundParameterizedType() || castType.isGenericType()) { + if (match.isProvablyDistinctFrom(isNarrowing ? expressionType : castType, 0)) { + reportIllegalCast(scope, castType, expressionType); + return false; + } + } + if (!isNarrowing) tagAsUnnecessaryCast(scope, castType); + return true; + } + + /** + * Base types need that the widening is explicitly done by the compiler using some bytecode like i2f. + * Also check unsafe type operations. + */ + public void computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType) { + if (runtimeTimeType == null || compileTimeType == null) + return; + if (this.implicitConversion != 0) return; // already set independantly + + // it is possible for a Byte to be unboxed to a byte & then converted to an int + // but it is not possible for a byte to become Byte & then assigned to an Integer, + // or to become an int before boxed into an Integer + if (runtimeTimeType != NullBinding && runtimeTimeType.isBaseType()) { + if (!compileTimeType.isBaseType()) { + compileTimeType = scope.environment().computeBoxingType(compileTimeType); + this.implicitConversion = UNBOXING; + } + } else { + if (compileTimeType != NullBinding && compileTimeType.isBaseType()) { + TypeBinding boxedType = scope.environment().computeBoxingType(runtimeTimeType); + if (boxedType == runtimeTimeType) // Object o = 12; + boxedType = compileTimeType; + this.implicitConversion = BOXING | (boxedType.id << 4) + compileTimeType.id; + return; + } + } + + switch (runtimeTimeType.id) { + case T_byte : + case T_short : + case T_char : + this.implicitConversion |= (T_int << 4) + compileTimeType.id; + break; + case T_JavaLangString : + case T_float : + case T_boolean : + case T_double : + case T_int : //implicitConversion may result in i2i which will result in NO code gen + case T_long : + this.implicitConversion |= (runtimeTimeType.id << 4) + compileTimeType.id; + break; + default : // regular object ref +// if (compileTimeType.isRawType() && runtimeTimeType.isBoundParameterizedType()) { +// scope.problemReporter().unsafeRawExpression(this, compileTimeType, runtimeTimeType); +// } + } + } /** * Expression statements are plain expressions, however they generate like * normal expressions with no value required. @@ -317,93 +624,58 @@ public abstract class Expression extends Statement { * creation, further operands should rather be only appended to the current one. * By default: no optimization. */ - public void generateOptimizedStringBuffer( + public void generateOptimizedStringConcatenation( BlockScope blockScope, - org.eclipse.jdt.internal.compiler.codegen.CodeStream codeStream, + CodeStream codeStream, int typeID) { - if (typeID == T_String && this.constant != NotAConstant && this.constant.stringValue().length() == 0) { + if (typeID == T_JavaLangString && this.constant != NotAConstant && this.constant.stringValue().length() == 0) { return; // optimize str + "" } generateCode(blockScope, codeStream, true); - codeStream.invokeStringBufferAppendForType(typeID); + codeStream.invokeStringConcatenationAppendForType(typeID); } /* Optimized (java) code generation for string concatenations that involve StringBuffer * creation: going through this path means that there is no need for a new StringBuffer * creation, further operands should rather be only appended to the current one. */ - public void generateOptimizedStringBufferCreation( + public void generateOptimizedStringConcatenationCreation( BlockScope blockScope, CodeStream codeStream, int typeID) { - // Optimization only for integers and strings - if (typeID == T_Object) { - // in the case the runtime value of valueOf(Object) returns null, we have to use append(Object) instead of directly valueOf(Object) - // append(Object) returns append(valueOf(Object)), which means that the null case is handled by append(String). - codeStream.newStringBuffer(); - codeStream.dup(); - codeStream.invokeStringBufferDefaultConstructor(); - generateCode(blockScope, codeStream, true); - codeStream.invokeStringBufferAppendForType(T_Object); - return; - } - codeStream.newStringBuffer(); + codeStream.newStringContatenation(); codeStream.dup(); - if (typeID == T_String || typeID == T_null) { - if (constant != NotAConstant) { - String stringValue = constant.stringValue(); - if (stringValue.length() == 0) { // optimize ""+ - codeStream.invokeStringBufferDefaultConstructor(); - return; - } - codeStream.ldc(stringValue); - } else { + switch (typeID) { + case T_JavaLangObject : + case T_undefined : + // in the case the runtime value of valueOf(Object) returns null, we have to use append(Object) instead of directly valueOf(Object) + // append(Object) returns append(valueOf(Object)), which means that the null case is handled by the next case. + codeStream.invokeStringConcatenationDefaultConstructor(); generateCode(blockScope, codeStream, true); - codeStream.invokeStringValueOf(T_Object); - } - } else { - generateCode(blockScope, codeStream, true); - codeStream.invokeStringValueOf(typeID); - } - codeStream.invokeStringBufferStringConstructor(); - } - - // Base types need that the widening is explicitly done by the compiler using some bytecode like i2f - public void implicitWidening( - TypeBinding runtimeTimeType, - TypeBinding compileTimeType) { - - if (runtimeTimeType == null || compileTimeType == null) - return; - -// if (compileTimeType.id == T_null) { -// // this case is possible only for constant null -// // The type of runtime is a reference type -// // The code gen use the constant id thus any value -// // for the runtime id (akak the <<4) could be used. -// // T_Object is used as some general T_reference -// implicitConversion = (T_Object << 4) + T_null; -// return; -// } - - switch (runtimeTimeType.id) { - case T_byte : - case T_short : - case T_char : - implicitConversion = (T_int << 4) + compileTimeType.id; - break; - case T_String : - case T_float : - case T_boolean : - case T_double : - case T_int : //implicitConversion may result in i2i which will result in NO code gen - case T_long : - implicitConversion = (runtimeTimeType.id << 4) + compileTimeType.id; + codeStream.invokeStringConcatenationAppendForType(T_JavaLangObject); + return; + case T_JavaLangString : + case T_null : + if (constant != NotAConstant) { + String stringValue = constant.stringValue(); + if (stringValue.length() == 0) { // optimize ""+ + codeStream.invokeStringConcatenationDefaultConstructor(); + return; + } + codeStream.ldc(stringValue); + } else { + // null case is not a constant + generateCode(blockScope, codeStream, true); + codeStream.invokeStringValueOf(T_JavaLangObject); + } break; - default : //nothing on regular object ref + default : + generateCode(blockScope, codeStream, true); + codeStream.invokeStringValueOf(typeID); } + codeStream.invokeStringConcatenationStringConstructor(); } public boolean isCompactableOperation() { @@ -413,11 +685,9 @@ public abstract class Expression extends Statement { //Return true if the conversion is done AUTOMATICALLY by the vm //while the javaVM is an int based-machine, thus for example pushing - //a byte onto the stack , will automatically creates a int on the stack + //a byte onto the stack , will automatically create an int on the stack //(this request some work d be done by the VM on signed numbers) - public boolean isConstantValueOfTypeAssignableToType( - TypeBinding constantType, - TypeBinding targetType) { + public boolean isConstantValueOfTypeAssignableToType(TypeBinding constantType, TypeBinding targetType) { if (constant == Constant.NotAConstant) return false; @@ -438,7 +708,49 @@ public abstract class Expression extends Statement { public boolean isTypeReference() { return false; } + + public int nullStatus(FlowInfo flowInfo) { + + if (this.constant != null && this.constant != NotAConstant) + return FlowInfo.NON_NULL; // constant expression cannot be null + + LocalVariableBinding local = localVariableBinding(); + if (local != null) { + if (flowInfo.isDefinitelyNull(local)) + return FlowInfo.NULL; + if (flowInfo.isDefinitelyNonNull(local)) + return FlowInfo.NON_NULL; + return FlowInfo.UNKNOWN; + } + return FlowInfo.NON_NULL; + } + + /** + * Constant usable for bytecode pattern optimizations, but cannot be inlined + * since it is not strictly equivalent to the definition of constant expressions. + * In particular, some side-effects may be required to occur (only the end value + * is known). + * @return Constant known to be of boolean type + */ + public Constant optimizedBooleanConstant() { + return this.constant; + } + + public StringBuffer print(int indent, StringBuffer output) { + printIndent(indent, output); + return printExpression(indent, output); + } + public abstract StringBuffer printExpression(int indent, StringBuffer output); + + public StringBuffer printStatement(int indent, StringBuffer output) { + return print(indent, output).append(";"); //$NON-NLS-1$ + } + + public void reportIllegalCast(Scope scope, TypeBinding castType, TypeBinding expressionType) { + // do nothing by default + } + public void resolve(BlockScope scope) { // drops the returning expression's type whatever the type is. @@ -461,28 +773,39 @@ public abstract class Expression extends Statement { BlockScope scope, TypeBinding expectedType) { + this.setExpectedType(expectedType); // needed in case of generic method invocation TypeBinding expressionType = this.resolveType(scope); if (expressionType == null) return null; if (expressionType == expectedType) return expressionType; if (!expressionType.isCompatibleWith(expectedType)) { - scope.problemReporter().typeMismatchError(expressionType, expectedType, this); - return null; + if (scope.isBoxingCompatibleWith(expressionType, expectedType)) { + this.computeConversion(scope, expectedType, expressionType); + } else { + scope.problemReporter().typeMismatchError(expressionType, expectedType, this); + return null; + } } return expressionType; } - public StringBuffer print(int indent, StringBuffer output) { - printIndent(indent, output); - return printExpression(indent, output); + /** + * Record the type expectation before this expression is typechecked. + * e.g. String s = foo();, foo() will be tagged as being expected of type String + * Used to trigger proper inference of generic method invocations. + */ + public void setExpectedType(TypeBinding expectedType) { + // do nothing by default } - public abstract StringBuffer printExpression(int indent, StringBuffer output); + public void tagAsUnnecessaryCast(Scope scope, TypeBinding castType) { + // do nothing by default + } - public StringBuffer printStatement(int indent, StringBuffer output) { - return print(indent, output).append(";"); //$NON-NLS-1$ + public void tagAsNeedCheckCast() { + // do nothing by default } - + public Expression toTypeReference() { //by default undefined @@ -493,4 +816,21 @@ public abstract class Expression extends Statement { return this; } + + public void traverse(ASTVisitor visitor, BlockScope scope) { + // do nothing by default + } + public void traverse(ASTVisitor visitor, ClassScope scope) { + // do nothing by default + } + public void traverse(ASTVisitor visitor, CompilationUnitScope scope) { + // do nothing by default + } + /** + * Returns the local variable referenced by this node. Can be a direct reference (SingleNameReference) + * or thru a cast expression etc... + */ + public LocalVariableBinding localVariableBinding() { + return null; + } } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/FalseLiteral.java b/src/org/eclipse/jdt/internal/compiler/ast/FalseLiteral.java index 82bc72d..6219111 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/FalseLiteral.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/FalseLiteral.java @@ -21,8 +21,8 @@ public FalseLiteral(int s , int e) { super(s,e); } public void computeConstant() { - - constant = Constant.fromValue(false);} + constant = Constant.fromValue(false); +} /** * Code generation for false literal * @@ -32,8 +32,9 @@ public void computeConstant() { */ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { int pc = codeStream.position; - if (valueRequired) - codeStream.iconst_0(); + if (valueRequired) { + codeStream.generateConstant(this.constant, this.implicitConversion); + } codeStream.recordPositionsFrom(pc, this.sourceStart); } public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) { diff --git a/src/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java b/src/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java index 7f001c3..9c08cb0 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java @@ -17,6 +17,7 @@ import org.eclipse.jdt.internal.compiler.flow.*; import org.eclipse.jdt.internal.compiler.lookup.*; public class FieldDeclaration extends AbstractVariableDeclaration { + public FieldBinding binding; boolean hasBeenResolved = false; public Javadoc javadoc; @@ -62,17 +63,28 @@ public class FieldDeclaration extends AbstractVariableDeclaration { } // cannot define static non-constant field inside nested class if (this.binding != null - && this.binding.isValidBinding() - && this.binding.isStatic() - && this.binding.constant == NotAConstant - && this.binding.declaringClass.isNestedType() - && this.binding.declaringClass.isClass() - && !this.binding.declaringClass.isStatic()) { + && this.binding.isValidBinding() + && this.binding.isStatic() + && !this.binding.isConstantValue() + && this.binding.declaringClass.isNestedType() + && this.binding.declaringClass.isClass() + && !this.binding.declaringClass.isStatic()) { initializationScope.problemReporter().unexpectedStaticModifierForField( (SourceTypeBinding) this.binding.declaringClass, this); } + checkAnnotationField: { + if (!this.binding.declaringClass.isAnnotationType()) + break checkAnnotationField; + if (this.initialization != null) { + if (this.binding.type.isArrayType() && (this.initialization instanceof ArrayInitializer)) + break checkAnnotationField; + if (this.initialization.constant != NotAConstant) + break checkAnnotationField; + } + initializationScope.problemReporter().annotationFieldNeedConstantInitialization(this); + } if (this.initialization != null) { flowInfo = this.initialization @@ -100,7 +112,7 @@ public class FieldDeclaration extends AbstractVariableDeclaration { int pc = codeStream.position; boolean isStatic; if (this.initialization != null - && !((isStatic = this.binding.isStatic()) && this.binding.constant != NotAConstant)) { + && !((isStatic = this.binding.isStatic()) && this.binding.isConstantValue())) { // non-static field, need receiver if (!isStatic) codeStream.aload_0(); @@ -116,16 +128,13 @@ public class FieldDeclaration extends AbstractVariableDeclaration { codeStream.recordPositionsFrom(pc, this.sourceStart); } - public TypeBinding getTypeBinding(Scope scope) { - - return this.type.getTypeBinding(scope); - } - - public boolean isField() { - - return true; + /** + * @see org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration#getKind() + */ + public int getKind() { + return this.type == null ? ENUM_CONSTANT : FIELD; } - + public boolean isStatic() { if (this.binding != null) @@ -144,6 +153,8 @@ public class FieldDeclaration extends AbstractVariableDeclaration { this.hasBeenResolved = true; + resolveAnnotations(initializationScope, this.annotations, this.binding); + // check if field is hiding some variable - issue is that field binding already got inserted in scope // thus must lookup separately in super type and outer context ClassScope classScope = initializationScope.enclosingClassScope(); @@ -153,7 +164,7 @@ public class FieldDeclaration extends AbstractVariableDeclaration { boolean checkLocal = true; if (declaringType.superclass != null) { Binding existingVariable = classScope.findField(declaringType.superclass, this.name, this, false /*do not resolve hidden field*/); - if (existingVariable != null && existingVariable.isValidBinding()){ + if (existingVariable != null && this.binding != existingVariable && existingVariable.isValidBinding()){ initializationScope.problemReporter().fieldHiding(this, existingVariable); checkLocal = false; // already found a matching field } @@ -162,8 +173,8 @@ public class FieldDeclaration extends AbstractVariableDeclaration { Scope outerScope = classScope.parent; // only corner case is: lookup of outer field through static declaringType, which isn't detected by #getBinding as lookup starts // from outer scope. Subsequent static contexts are detected for free. - Binding existingVariable = outerScope.getBinding(this.name, BindingIds.VARIABLE, this, false /*do not resolve hidden field*/); - if (existingVariable != null && existingVariable.isValidBinding() + Binding existingVariable = outerScope.getBinding(this.name, Binding.VARIABLE, this, false /*do not resolve hidden field*/); + if (existingVariable != null && this.binding != existingVariable && existingVariable.isValidBinding() && (!(existingVariable instanceof FieldBinding) || ((FieldBinding) existingVariable).isStatic() || !declaringType.isStatic())) { @@ -172,7 +183,9 @@ public class FieldDeclaration extends AbstractVariableDeclaration { } } - this.type.resolvedType = this.binding.type; // update binding for type reference + if (this.type != null ) { // enum constants have no declared type + this.type.resolvedType = this.binding.type; // update binding for type reference + } FieldBinding previousField = initializationScope.initializedField; int previousFieldID = initializationScope.lastVisibleFieldID; @@ -180,45 +193,48 @@ public class FieldDeclaration extends AbstractVariableDeclaration { initializationScope.initializedField = this.binding; initializationScope.lastVisibleFieldID = this.binding.id; - if (isTypeUseDeprecated(this.binding.type, initializationScope)) { - initializationScope.problemReporter().deprecatedType(this.binding.type, this.type); - } // the resolution of the initialization hasn't been done if (this.initialization == null) { - this.binding.constant = Constant.NotAConstant; + this.binding.setConstant(Constant.NotAConstant); } else { // break dead-lock cycles by forcing constant to NotAConstant - this.binding.constant = Constant.NotAConstant; - - TypeBinding typeBinding = this.binding.type; - TypeBinding initializationTypeBinding; + this.binding.setConstant(Constant.NotAConstant); + TypeBinding fieldType = this.binding.type; + TypeBinding initializationType; + this.initialization.setExpectedType(fieldType); // needed in case of generic method invocation if (this.initialization instanceof ArrayInitializer) { - if ((initializationTypeBinding = this.initialization.resolveTypeExpecting(initializationScope, typeBinding)) != null) { - ((ArrayInitializer) this.initialization).binding = (ArrayBinding) initializationTypeBinding; - this.initialization.implicitWidening(typeBinding, initializationTypeBinding); + if ((initializationType = this.initialization.resolveTypeExpecting(initializationScope, fieldType)) != null) { + ((ArrayInitializer) this.initialization).binding = (ArrayBinding) initializationType; + this.initialization.computeConversion(initializationScope, fieldType, initializationType); } - } else if ((initializationTypeBinding = this.initialization.resolveType(initializationScope)) != null) { - - if (this.initialization.isConstantValueOfTypeAssignableToType(initializationTypeBinding, typeBinding) - || (typeBinding.isBaseType() && BaseTypeBinding.isWidening(typeBinding.id, initializationTypeBinding.id))) { - - this.initialization.implicitWidening(typeBinding, initializationTypeBinding); - - } else if (initializationTypeBinding.isCompatibleWith(typeBinding)) { - this.initialization.implicitWidening(typeBinding, initializationTypeBinding); - + } else if ((initializationType = this.initialization.resolveType(initializationScope)) != null) { + + if (fieldType != initializationType) // must call before computeConversion() and typeMismatchError() + initializationScope.compilationUnitScope().recordTypeConversion(fieldType, initializationType); + if (this.initialization.isConstantValueOfTypeAssignableToType(initializationType, fieldType) + || (fieldType.isBaseType() && BaseTypeBinding.isWidening(fieldType.id, initializationType.id)) + || initializationType.isCompatibleWith(fieldType)) { + this.initialization.computeConversion(initializationScope, fieldType, initializationType); + if (initializationType.needsUncheckedConversion(fieldType)) { + initializationScope.problemReporter().unsafeRawConversion(this.initialization, initializationType, fieldType); + } + } else if (initializationScope.environment().options.sourceLevel >= JDK1_5 // autoboxing + && (initializationScope.isBoxingCompatibleWith(initializationType, fieldType) + || (initializationType.isBaseType() // narrowing then boxing ? + && initializationType != null + && !fieldType.isBaseType() + && initialization.isConstantValueOfTypeAssignableToType(initializationType, initializationScope.environment().computeBoxingType(fieldType))))) { + this.initialization.computeConversion(initializationScope, fieldType, initializationType); } else { - initializationScope.problemReporter().typeMismatchError(initializationTypeBinding, typeBinding, this); + initializationScope.problemReporter().typeMismatchError(initializationType, fieldType, this); } if (this.binding.isFinal()){ // cast from constant actual type to variable type - this.binding.constant = - this.initialization.constant.castTo( - (this.binding.type.id << 4) + this.initialization.constant.typeID()); + this.binding.setConstant(this.initialization.constant.castTo((this.binding.type.id << 4) + this.initialization.constant.typeID())); } } else { - this.binding.constant = NotAConstant; + this.binding.setConstant(NotAConstant); } } // Resolve Javadoc comment if one is present @@ -235,8 +251,8 @@ public class FieldDeclaration extends AbstractVariableDeclaration { } finally { initializationScope.initializedField = previousField; initializationScope.lastVisibleFieldID = previousFieldID; - if (this.binding.constant == null) - this.binding.constant = Constant.NotAConstant; + if (this.binding.constant() == null) + this.binding.setConstant(Constant.NotAConstant); } } } @@ -244,7 +260,14 @@ public class FieldDeclaration extends AbstractVariableDeclaration { public void traverse(ASTVisitor visitor, MethodScope scope) { if (visitor.visit(this, scope)) { - this.type.traverse(visitor, scope); + if (this.annotations != null) { + int annotationsLength = this.annotations.length; + for (int i = 0; i < annotationsLength; i++) + this.annotations[i].traverse(visitor, scope); + } + if (this.type != null) { + this.type.traverse(visitor, scope); + } if (this.initialization != null) this.initialization.traverse(visitor, scope); } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/FieldReference.java b/src/org/eclipse/jdt/internal/compiler/ast/FieldReference.java index 9c4a86d..1e00105 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/FieldReference.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/FieldReference.java @@ -21,11 +21,16 @@ public class FieldReference extends Reference implements InvocationSite { public Expression receiver; public char[] token; - public FieldBinding binding, codegenBinding; + public FieldBinding binding; // exact binding resulting from lookup + protected FieldBinding codegenBinding; // actual binding used for code generation (if no synthetic accessor) + public MethodBinding[] syntheticAccessors; // [0]=read accessor [1]=write accessor + public static final int READ = 0; + public static final int WRITE = 1; + public long nameSourcePosition; //(start<<32)+end - MethodBinding syntheticReadAccessor, syntheticWriteAccessor; public TypeBinding receiverType; - + public TypeBinding genericCast; + public FieldReference(char[] source, long pos) { token = source; @@ -33,7 +38,7 @@ public class FieldReference extends Reference implements InvocationSite { //by default the position are the one of the field (not true for super access) sourceStart = (int) (pos >>> 32); sourceEnd = (int) (pos & 0x00000000FFFFFFFFL); - bits |= BindingIds.FIELD; + bits |= Binding.FIELD; } @@ -53,7 +58,7 @@ public class FieldReference extends Reference implements InvocationSite { currentScope.problemReporter().uninitializedBlankFinalField(binding, this); // we could improve error msg here telling "cannot use compound assignment on final blank field" } - manageSyntheticReadAccessIfNecessary(currentScope, flowInfo); + manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/); } flowInfo = receiver @@ -66,7 +71,7 @@ public class FieldReference extends Reference implements InvocationSite { .analyseCode(currentScope, flowContext, flowInfo) .unconditionalInits(); } - manageSyntheticWriteAccessIfNecessary(currentScope, flowInfo); + manageSyntheticAccessIfNecessary(currentScope, flowInfo, false /*write-access*/); // check if assigning a final field if (binding.isFinal()) { @@ -107,13 +112,35 @@ public class FieldReference extends Reference implements InvocationSite { FlowInfo flowInfo, boolean valueRequired) { - receiver.analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic()); + boolean nonStatic = !binding.isStatic(); + receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic); + if (nonStatic) receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); + if (valueRequired) { - manageSyntheticReadAccessIfNecessary(currentScope, flowInfo); + manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/); } return flowInfo; } + /** + * @see org.eclipse.jdt.internal.compiler.ast.Expression#computeConversion(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding) + */ + public void computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType) { + if (runtimeTimeType == null || compileTimeType == null) + return; + // set the generic cast after the fact, once the type expectation is fully known (no need for strict cast) + if (this.binding != null && this.binding.isValidBinding()) { + FieldBinding originalBinding = this.binding.original(); + if (originalBinding != this.binding) { + // extra cast needed if method return type has type variable + if ((originalBinding.type.tagBits & TagBits.HasTypeVariable) != 0 && runtimeTimeType.id != T_JavaLangObject) { + this.genericCast = originalBinding.type.genericCast(scope.boxing(runtimeTimeType)); // runtimeType could be base type in boxing case + } + } + } + super.computeConversion(scope, runtimeTimeType, compileTimeType); + } + public FieldBinding fieldBinding() { return binding; @@ -133,11 +160,12 @@ public class FieldReference extends Reference implements InvocationSite { fieldStore( codeStream, this.codegenBinding, - syntheticWriteAccessor, + syntheticAccessors == null ? null : syntheticAccessors[WRITE], valueRequired); if (valueRequired) { codeStream.generateImplicitConversion(assignment.implicitConversion); } + // no need for generic cast as value got dupped } /** @@ -161,27 +189,28 @@ public class FieldReference extends Reference implements InvocationSite { boolean isStatic = this.codegenBinding.isStatic(); receiver.generateCode(currentScope, codeStream, !isStatic); if (valueRequired) { - if (this.codegenBinding.constant == NotAConstant) { + if (!this.codegenBinding.isConstantValue()) { if (this.codegenBinding.declaringClass == null) { // array length codeStream.arraylength(); } else { - if (syntheticReadAccessor == null) { + if (syntheticAccessors == null || syntheticAccessors[READ] == null) { if (isStatic) { codeStream.getstatic(this.codegenBinding); } else { codeStream.getfield(this.codegenBinding); } } else { - codeStream.invokestatic(syntheticReadAccessor); + codeStream.invokestatic(syntheticAccessors[READ]); } } + if (this.genericCast != null) codeStream.checkcast(this.genericCast); codeStream.generateImplicitConversion(implicitConversion); } else { if (!isStatic) { codeStream.invokeObjectGetClass(); // perform null check codeStream.pop(); } - codeStream.generateConstant(this.codegenBinding.constant, implicitConversion); + codeStream.generateConstant(this.codegenBinding.constant(), implicitConversion); } } else { if (!isStatic){ @@ -207,41 +236,46 @@ public class FieldReference extends Reference implements InvocationSite { codeStream, !(isStatic = this.codegenBinding.isStatic())); if (isStatic) { - if (syntheticReadAccessor == null) { + if (syntheticAccessors == null || syntheticAccessors[READ] == null) { codeStream.getstatic(this.codegenBinding); } else { - codeStream.invokestatic(syntheticReadAccessor); + codeStream.invokestatic(syntheticAccessors[READ]); } } else { codeStream.dup(); - if (syntheticReadAccessor == null) { + if (syntheticAccessors == null || syntheticAccessors[READ] == null) { codeStream.getfield(this.codegenBinding); } else { - codeStream.invokestatic(syntheticReadAccessor); + codeStream.invokestatic(syntheticAccessors[READ]); } } int operationTypeID; - if ((operationTypeID = implicitConversion >> 4) == T_String) { - codeStream.generateStringAppend(currentScope, null, expression); - } else { - // promote the array reference to the suitable operation type - codeStream.generateImplicitConversion(implicitConversion); - // generate the increment value (will by itself be promoted to the operation value) - if (expression == IntLiteral.One) { // prefix operation - codeStream.generateConstant(expression.constant, implicitConversion); - } else { - expression.generateCode(currentScope, codeStream, true); - } - // perform the operation - codeStream.sendOperator(operator, operationTypeID); - // cast the value back to the array reference type - codeStream.generateImplicitConversion(assignmentImplicitConversion); + switch(operationTypeID = (implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) { + case T_JavaLangString : + case T_JavaLangObject : + case T_undefined : + codeStream.generateStringConcatenationAppend(currentScope, null, expression); + break; + default : + // promote the array reference to the suitable operation type + codeStream.generateImplicitConversion(implicitConversion); + // generate the increment value (will by itself be promoted to the operation value) + if (expression == IntLiteral.One) { // prefix operation + codeStream.generateConstant(expression.constant, implicitConversion); + } else { + expression.generateCode(currentScope, codeStream, true); + } + // perform the operation + codeStream.sendOperator(operator, operationTypeID); + // cast the value back to the array reference type + codeStream.generateImplicitConversion(assignmentImplicitConversion); } fieldStore( codeStream, this.codegenBinding, - syntheticWriteAccessor, + syntheticAccessors == null ? null : syntheticAccessors[WRITE], valueRequired); + // no need for generic cast as value got dupped } public void generatePostIncrement( @@ -256,17 +290,17 @@ public class FieldReference extends Reference implements InvocationSite { codeStream, !(isStatic = this.codegenBinding.isStatic())); if (isStatic) { - if (syntheticReadAccessor == null) { + if (syntheticAccessors == null || syntheticAccessors[READ] == null) { codeStream.getstatic(this.codegenBinding); } else { - codeStream.invokestatic(syntheticReadAccessor); + codeStream.invokestatic(syntheticAccessors[READ]); } } else { codeStream.dup(); - if (syntheticReadAccessor == null) { + if (syntheticAccessors == null || syntheticAccessors[READ] == null) { codeStream.getfield(this.codegenBinding); } else { - codeStream.invokestatic(syntheticReadAccessor); + codeStream.invokestatic(syntheticAccessors[READ]); } } if (valueRequired) { @@ -286,15 +320,21 @@ public class FieldReference extends Reference implements InvocationSite { } } } + codeStream.generateImplicitConversion(implicitConversion); codeStream.generateConstant( postIncrement.expression.constant, implicitConversion); - codeStream.sendOperator(postIncrement.operator, this.codegenBinding.type.id); + codeStream.sendOperator(postIncrement.operator, this.implicitConversion & COMPILE_TYPE_MASK); codeStream.generateImplicitConversion( postIncrement.assignmentImplicitConversion); - fieldStore(codeStream, this.codegenBinding, syntheticWriteAccessor, false); + fieldStore(codeStream, this.codegenBinding, syntheticAccessors == null ? null : syntheticAccessors[WRITE], false); + } + /** + * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments() + */ + public TypeBinding[] genericTypeArguments() { + return null; } - public static final Constant getConstantFor( FieldBinding binding, Reference reference, @@ -318,12 +358,14 @@ public class FieldReference extends Reference implements InvocationSite { return NotAConstant; } if (!binding.isFinal()) { - return binding.constant = NotAConstant; + binding.setConstant(NotAConstant); + return NotAConstant; } - if (binding.constant != null) { + Constant fieldConstant = binding.constant(); + if (fieldConstant != null) { if (isImplicit || (reference instanceof QualifiedNameReference && binding == ((QualifiedNameReference)reference).binding)) { - return binding.constant; + return fieldConstant; } return NotAConstant; } @@ -333,17 +375,18 @@ public class FieldReference extends Reference implements InvocationSite { //has already been compiled. It can only be from a class within //compilation units to process. Thus the field is NOT from a BinaryTypeBinbing - SourceTypeBinding typeBinding = (SourceTypeBinding) binding.declaringClass; - TypeDeclaration typeDecl = typeBinding.scope.referenceContext; - FieldDeclaration fieldDecl = typeDecl.declarationOf(binding); + FieldBinding originalField = binding.original(); + SourceTypeBinding sourceType = (SourceTypeBinding) originalField.declaringClass; + TypeDeclaration typeDecl = sourceType.scope.referenceContext; + FieldDeclaration fieldDecl = typeDecl.declarationOf(originalField); - fieldDecl.resolve(binding.isStatic() //side effect on binding + fieldDecl.resolve(originalField.isStatic() //side effect on binding ? typeDecl.staticInitializerScope : typeDecl.initializerScope); if (isImplicit || (reference instanceof QualifiedNameReference && binding == ((QualifiedNameReference)reference).binding)) { - return binding.constant; + return binding.constant(); } return NotAConstant; } @@ -361,15 +404,19 @@ public class FieldReference extends Reference implements InvocationSite { /* * No need to emulate access to protected fields since not implicitly accessed */ - public void manageSyntheticReadAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { + public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess) { if (!flowInfo.isReachable()) return; + // if field from parameterized type got found, use the original field at codegen time + this.codegenBinding = this.binding.original(); + if (binding.isPrivate()) { - if ((currentScope.enclosingSourceType() != binding.declaringClass) - && (binding.constant == NotAConstant)) { - syntheticReadAccessor = - ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, true); - currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this); + if ((currentScope.enclosingSourceType() != this.codegenBinding.declaringClass) && !binding.isConstantValue()) { + if (syntheticAccessors == null) + syntheticAccessors = new MethodBinding[2]; + syntheticAccessors[isReadAccess ? READ : WRITE] = + ((SourceTypeBinding) this.codegenBinding.declaringClass).addSyntheticMethod(this.codegenBinding, isReadAccess); + currentScope.problemReporter().needToEmulateFieldAccess(this.codegenBinding, this, isReadAccess); return; } @@ -379,8 +426,10 @@ public class FieldReference extends Reference implements InvocationSite { SourceTypeBinding destinationType = (SourceTypeBinding) (((QualifiedSuperReference) receiver) .currentCompatibleType); - syntheticReadAccessor = destinationType.addSyntheticMethod(binding, true); - currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this); + if (syntheticAccessors == null) + syntheticAccessors = new MethodBinding[2]; + syntheticAccessors[isReadAccess ? READ : WRITE] = destinationType.addSyntheticMethod(this.codegenBinding, isReadAccess); + currentScope.problemReporter().needToEmulateFieldAccess(this.codegenBinding, this, isReadAccess); return; } else if (binding.isProtected()) { @@ -393,86 +442,31 @@ public class FieldReference extends Reference implements InvocationSite { SourceTypeBinding currentCompatibleType = (SourceTypeBinding) enclosingSourceType.enclosingTypeAt( (bits & DepthMASK) >> DepthSHIFT); - syntheticReadAccessor = currentCompatibleType.addSyntheticMethod(binding, true); - currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this); + if (syntheticAccessors == null) + syntheticAccessors = new MethodBinding[2]; + syntheticAccessors[isReadAccess ? READ : WRITE] = currentCompatibleType.addSyntheticMethod(this.codegenBinding, isReadAccess); + currentScope.problemReporter().needToEmulateFieldAccess(this.codegenBinding, this, isReadAccess); return; } } // if the binding declaring class is not visible, need special action // for runtime compatibility on 1.2 VMs : change the declaring class of the binding // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type - if (binding.declaringClass != this.receiverType + if (this.binding.declaringClass != this.receiverType && !this.receiverType.isArrayType() - && binding.declaringClass != null // array.length - && binding.constant == NotAConstant + && this.binding.declaringClass != null // array.length + && !this.binding.isConstantValue() && ((currentScope.environment().options.targetJDK >= ClassFileConstants.JDK1_2 - && binding.declaringClass.id != T_Object) + && this.binding.declaringClass.id != T_JavaLangObject) //no change for Object fields (in case there was) - || !binding.declaringClass.canBeSeenBy(currentScope))) { + || !this.codegenBinding.declaringClass.canBeSeenBy(currentScope))) { this.codegenBinding = currentScope.enclosingSourceType().getUpdatedFieldBinding( - binding, - (ReferenceBinding) this.receiverType); + this.codegenBinding, + (ReferenceBinding) this.receiverType.erasure()); } } - /* - * No need to emulate access to protected fields since not implicitly accessed - */ - public void manageSyntheticWriteAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { - - if (!flowInfo.isReachable()) return; - if (binding.isPrivate()) { - if (currentScope.enclosingSourceType() != binding.declaringClass) { - syntheticWriteAccessor = - ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, false); - currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this); - return; - } - - } else if (receiver instanceof QualifiedSuperReference) { // qualified super - - // qualified super need emulation always - SourceTypeBinding destinationType = - (SourceTypeBinding) (((QualifiedSuperReference) receiver) - .currentCompatibleType); - syntheticWriteAccessor = destinationType.addSyntheticMethod(binding, false); - currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this); - return; - - } else if (binding.isProtected()) { - - SourceTypeBinding enclosingSourceType; - if (((bits & DepthMASK) != 0) - && binding.declaringClass.getPackage() - != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) { - - SourceTypeBinding currentCompatibleType = - (SourceTypeBinding) enclosingSourceType.enclosingTypeAt( - (bits & DepthMASK) >> DepthSHIFT); - syntheticWriteAccessor = - currentCompatibleType.addSyntheticMethod(binding, false); - currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this); - return; - } - } - // if the binding declaring class is not visible, need special action - // for runtime compatibility on 1.2 VMs : change the declaring class of the binding - // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type - if (binding.declaringClass != this.receiverType - && !this.receiverType.isArrayType() - && binding.declaringClass != null // array.length - && binding.constant == NotAConstant - && ((currentScope.environment().options.targetJDK >= ClassFileConstants.JDK1_2 - && binding.declaringClass.id != T_Object) - //no change for Object fields (in case there was) - || !binding.declaringClass.canBeSeenBy(currentScope))) { - this.codegenBinding = - currentScope.enclosingSourceType().getUpdatedFieldBinding( - binding, - (ReferenceBinding) this.receiverType); - } - } public StringBuffer printExpression(int indent, StringBuffer output) { @@ -509,7 +503,7 @@ public class FieldReference extends Reference implements InvocationSite { scope.problemReporter().invalidField(this, this.receiverType); return null; } - + this.receiver.computeConversion(scope, this.receiverType, this.receiverType); if (isFieldUseDeprecated(binding, scope, (this.bits & IsStrictlyAssignedMASK) !=0)) { scope.problemReporter().deprecatedField(binding, this); } @@ -521,9 +515,8 @@ public class FieldReference extends Reference implements InvocationSite { if (binding.isStatic()) { // static field accessed through receiver? legal but unoptimal (optional warning) if (!(isImplicitThisRcv - || receiver.isSuper() || (receiver instanceof NameReference - && (((NameReference) receiver).bits & BindingIds.TYPE) != 0))) { + && (((NameReference) receiver).bits & Binding.TYPE) != 0))) { scope.problemReporter().nonStaticAccessToStaticField(this, binding); } if (!isImplicitThisRcv && binding.declaringClass != receiverType) { diff --git a/src/org/eclipse/jdt/internal/compiler/ast/FloatLiteral.java b/src/org/eclipse/jdt/internal/compiler/ast/FloatLiteral.java index c7d53d6..0fa1908 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/FloatLiteral.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/FloatLiteral.java @@ -11,72 +11,105 @@ package org.eclipse.jdt.internal.compiler.ast; import org.eclipse.jdt.internal.compiler.ASTVisitor; -import org.eclipse.jdt.internal.compiler.impl.*; -import org.eclipse.jdt.internal.compiler.codegen.*; -import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.codegen.CodeStream; +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.util.FloatUtil; public class FloatLiteral extends NumberLiteral { float value; final static float Float_MIN_VALUE = Float.intBitsToFloat(1); // work-around VAJ problem 1F6IGUU -public FloatLiteral(char[] token, int s, int e) { - super(token, s,e); -} -public void computeConstant() { - - //the source is correctly formated so the exception should never occurs - - Float computedValue; - try { - computedValue = Float.valueOf(String.valueOf(source)); - } catch (NumberFormatException e) { - return; - } - - if (computedValue.doubleValue() > Float.MAX_VALUE){ - return; //may be Infinity + public FloatLiteral(char[] token, int s, int e) { + super(token, s, e); } - if (computedValue.floatValue() < Float_MIN_VALUE){ - // see 1F6IGUU - //only a true 0 can be made of zeros - //1.00000000e-46f is illegal .... - label : for (int i = 0; i < source.length; i++) { - switch (source[i]) { - case '.' : - case 'f' : - case 'F' : - case '0' : - break; - case 'e' : - case 'E' : - break label; //exposant are valid !.... - default : - return; //error + public void computeConstant() { + Float computedValue; + try { + computedValue = Float.valueOf(String.valueOf(source)); + } catch (NumberFormatException e) { + // hex floating point literal + // being rejected by 1.4 libraries where Float.valueOf(...) doesn't handle hex decimal floats + try { + float v = FloatUtil.valueOfHexFloatLiteral(source); + if (v == Float.POSITIVE_INFINITY) { + // error: the number is too large to represent + return; + } + if (Float.isNaN(v)) { + // error: the number is too small to represent + return; + } + value = v; + constant = Constant.fromValue(v); + } catch (NumberFormatException e1) { + // if the computation of the constant fails + } + return; + } + + final float floatValue = computedValue.floatValue(); + if (floatValue > Float.MAX_VALUE) { + // error: the number is too large to represent + return; + } + if (floatValue < Float.MIN_VALUE) { + // see 1F6IGUU + // a true 0 only has '0' and '.' in mantissa + // 1.0e-5000d is non-zero, but underflows to 0 + boolean isHexaDecimal = false; + label : for (int i = 0; i < source.length; i++) { //it is welled formated so just test against '0' and potential . D d + switch (source[i]) { + case '0' : + case '.' : + break; + case 'x' : + case 'X' : + isHexaDecimal = true; + break; + case 'e' : + case 'E' : + case 'f' : + case 'F' : + case 'd' : + case 'D' : + if (isHexaDecimal) { + return; + } + // starting the exponent - mantissa is all zero + // no exponent - mantissa is all zero + break label; + case 'p' : + case 'P' : + break label; + default : + // error: the number is too small to represent + return; + } } } + value = floatValue; + constant = Constant.fromValue(value); } - constant = Constant.fromValue(value = computedValue.floatValue()); -} -/** - * Code generation for float literal - * - * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope - * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream - * @param valueRequired boolean - */ -public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { - int pc = codeStream.position; - if (valueRequired) - if ((implicitConversion >> 4) == T_float) - codeStream.generateInlinedValue(value); - else + /** + * Code generation for float literal + * + * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope + * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream + * @param valueRequired boolean + */ + public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { + int pc = codeStream.position; + if (valueRequired) { codeStream.generateConstant(constant, implicitConversion); - codeStream.recordPositionsFrom(pc, this.sourceStart); -} -public TypeBinding literalType(BlockScope scope) { - return FloatBinding; -} -public void traverse(ASTVisitor visitor, BlockScope blockScope) { - visitor.visit(this, blockScope); - visitor.endVisit(this, blockScope); -} + } + codeStream.recordPositionsFrom(pc, this.sourceStart); + } + public TypeBinding literalType(BlockScope scope) { + return FloatBinding; + } + public void traverse(ASTVisitor visitor, BlockScope blockScope) { + visitor.visit(this, blockScope); + visitor.endVisit(this, blockScope); + } } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ForStatement.java b/src/org/eclipse/jdt/internal/compiler/ast/ForStatement.java index 51c5db6..ad6c97a 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ForStatement.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ForStatement.java @@ -83,14 +83,15 @@ public class ForStatement extends Statement { // process the condition LoopingFlowContext condLoopContext = null; + FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations(); if (condition != null) { if (!isConditionTrue) { - flowInfo = + condInfo = condition.analyseCode( scope, (condLoopContext = new LoopingFlowContext(flowContext, this, null, null, scope)), - flowInfo); + condInfo); } } @@ -100,28 +101,28 @@ public class ForStatement extends Statement { if (action == null || (action.isEmptyBlock() && currentScope.environment().options.complianceLevel <= ClassFileConstants.JDK1_3)) { if (condLoopContext != null) - condLoopContext.complainOnFinalAssignmentsInLoop(scope, flowInfo); + condLoopContext.complainOnDeferredChecks(scope, condInfo); if (isConditionTrue) { return FlowInfo.DEAD_END; } else { if (isConditionFalse){ continueLabel = null; // for(;false;p()); } - actionInfo = flowInfo.initsWhenTrue().copy(); + actionInfo = condInfo.initsWhenTrue().copy().unconditionalInits().discardNullRelatedInitializations(); loopingContext = new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope); } } else { loopingContext = new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope); - FlowInfo initsWhenTrue = flowInfo.initsWhenTrue(); + FlowInfo initsWhenTrue = condInfo.initsWhenTrue(); condIfTrueInitStateIndex = currentScope.methodScope().recordInitializationStates(initsWhenTrue); if (isConditionFalse) { actionInfo = FlowInfo.DEAD_END; } else { - actionInfo = initsWhenTrue.copy(); + actionInfo = initsWhenTrue.copy().unconditionalInits().discardNullRelatedInitializations(); if (isConditionOptimizedFalse){ actionInfo.setReachMode(FlowInfo.UNREACHABLE); } @@ -135,26 +136,31 @@ public class ForStatement extends Statement { continueLabel = null; } else { if (condLoopContext != null) - condLoopContext.complainOnFinalAssignmentsInLoop(scope, flowInfo); + condLoopContext.complainOnDeferredChecks(scope, condInfo); actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits()); - loopingContext.complainOnFinalAssignmentsInLoop(scope, actionInfo); + loopingContext.complainOnDeferredChecks(scope, actionInfo); } } // for increments - if ((continueLabel != null) && (increments != null)) { - LoopingFlowContext loopContext = - new LoopingFlowContext(flowContext, this, null, null, scope); - for (int i = 0, count = increments.length; i < count; i++) { - actionInfo = increments[i].analyseCode(scope, loopContext, actionInfo); + FlowInfo exitBranch = condInfo.initsWhenFalse(); + exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis + if (continueLabel != null) { + if (increments != null) { + LoopingFlowContext loopContext = + new LoopingFlowContext(flowContext, this, null, null, scope); + for (int i = 0, count = increments.length; i < count; i++) { + actionInfo = increments[i].analyseCode(scope, loopContext, actionInfo); + } + loopContext.complainOnDeferredChecks(scope, actionInfo); } - loopContext.complainOnFinalAssignmentsInLoop(scope, actionInfo); + exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits()); } //end of loop FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( loopingContext.initsOnBreak, isConditionOptimizedTrue, - flowInfo.initsWhenFalse(), + exitBranch, isConditionOptimizedFalse, !isConditionTrue /*for(;;){}while(true); unreachable(); */); mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); @@ -221,9 +227,7 @@ public class ForStatement extends Statement { // May loose some local variable initializations : affecting the local variable attributes if (preCondInitStateIndex != -1) { - codeStream.removeNotDefinitelyAssignedVariables( - currentScope, - preCondInitStateIndex); + codeStream.removeNotDefinitelyAssignedVariables(currentScope, preCondInitStateIndex); } // generate the condition @@ -290,7 +294,7 @@ public class ForStatement extends Statement { initializations[i].resolve(scope); if (condition != null) { TypeBinding type = condition.resolveTypeExpecting(scope, BooleanBinding); - condition.implicitWidening(type, type); + condition.computeConversion(scope, type, type); } if (increments != null) for (int i = 0, length = increments.length; i < length; i++) diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java b/src/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java new file mode 100644 index 0000000..1c1dbb1 --- /dev/null +++ b/src/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java @@ -0,0 +1,461 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.ast; + +import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.codegen.CodeStream; +import org.eclipse.jdt.internal.compiler.codegen.Label; +import org.eclipse.jdt.internal.compiler.flow.FlowContext; +import org.eclipse.jdt.internal.compiler.flow.FlowInfo; +import org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext; +import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; + +public class ForeachStatement extends Statement { + + public LocalDeclaration elementVariable; + public int elementVariableImplicitWidening = -1; + public Expression collection; + public Statement action; + + // set the kind of foreach + private int kind; + // possible kinds of iterating behavior + private static final int ARRAY = 0; + private static final int RAW_ITERABLE = 1; + private static final int GENERIC_ITERABLE = 2; + + private TypeBinding collectionElementType; + + // loop labels + private Label breakLabel; + private Label continueLabel; + + public BlockScope scope; + + // secret variables for codegen + public LocalVariableBinding indexVariable; + public LocalVariableBinding collectionVariable; // to store the collection expression value + public LocalVariableBinding maxVariable; + // secret variable names + private static final char[] SecretIndexVariableName = " index".toCharArray(); //$NON-NLS-1$ + private static final char[] SecretCollectionVariableName = " collection".toCharArray(); //$NON-NLS-1$ + private static final char[] SecretMaxVariableName = " max".toCharArray(); //$NON-NLS-1$ + + int postCollectionInitStateIndex = -1; + int mergedInitStateIndex = -1; + + public ForeachStatement( + LocalDeclaration elementVariable, + Expression collection, + int start) { + + this.elementVariable = elementVariable; + this.collection = collection; + this.sourceStart = start; + this.kind = -1; + } + + public FlowInfo analyseCode( + BlockScope currentScope, + FlowContext flowContext, + FlowInfo flowInfo) { + // initialize break and continue labels + breakLabel = new Label(); + continueLabel = new Label(); + + // process the element variable and collection + flowInfo = this.elementVariable.analyseCode(scope, flowContext, flowInfo); + FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations(); + condInfo = this.collection.analyseCode(scope, flowContext, condInfo); + + // element variable will be assigned when iterating + condInfo.markAsDefinitelyAssigned(this.elementVariable.binding); + + this.postCollectionInitStateIndex = currentScope.methodScope().recordInitializationStates(condInfo); + + // process the action + LoopingFlowContext loopingContext = new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope); + FlowInfo actionInfo = condInfo.initsWhenTrue().copy(); + FlowInfo exitBranch; + if (!(action == null || (action.isEmptyBlock() + && currentScope.environment().options.complianceLevel <= ClassFileConstants.JDK1_3))) { + + if (!this.action.complainIfUnreachable(actionInfo, scope, false)) { + actionInfo = action.analyseCode(scope, loopingContext, actionInfo); + } + + // code generation can be optimized when no need to continue in the loop + exitBranch = condInfo.initsWhenFalse(); + exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis + if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) { + continueLabel = null; + } else { + actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits()); + loopingContext.complainOnDeferredChecks(scope, actionInfo); + exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits()); + } + } else { + exitBranch = condInfo.initsWhenFalse(); + } + + // we need the variable to iterate the collection even if the + // element variable is not used + if (!(this.action == null + || this.action.isEmptyBlock() + || ((this.action.bits & IsUsefulEmptyStatementMASK) != 0))) { + switch(this.kind) { + case ARRAY : + this.collectionVariable.useFlag = LocalVariableBinding.USED; + this.indexVariable.useFlag = LocalVariableBinding.USED; + this.maxVariable.useFlag = LocalVariableBinding.USED; + break; + case RAW_ITERABLE : + case GENERIC_ITERABLE : + this.indexVariable.useFlag = LocalVariableBinding.USED; + break; + } + } + //end of loop + FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( + loopingContext.initsOnBreak, + false, + exitBranch, + false, + true /*for(;;){}while(true); unreachable(); */); + mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); + return mergedInfo; + } + + /** + * For statement code generation + * + * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope + * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream + */ + public void generateCode(BlockScope currentScope, CodeStream codeStream) { + + if ((bits & IsReachableMASK) == 0) { + return; + } + int pc = codeStream.position; + if (this.action == null + || this.action.isEmptyBlock() + || ((this.action.bits & IsUsefulEmptyStatementMASK) != 0)) { + codeStream.exitUserScope(scope); + if (mergedInitStateIndex != -1) { + codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex); + codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex); + } + codeStream.recordPositionsFrom(pc, this.sourceStart); + return; + } + // generate the initializations + switch(this.kind) { + case ARRAY : + collection.generateCode(scope, codeStream, true); + codeStream.store(this.collectionVariable, false); + codeStream.iconst_0(); + codeStream.store(this.indexVariable, false); + codeStream.load(this.collectionVariable); + codeStream.arraylength(); + codeStream.store(this.maxVariable, false); + break; + case RAW_ITERABLE : + case GENERIC_ITERABLE : + collection.generateCode(scope, codeStream, true); + // declaringClass.iterator(); + final TypeBinding collectionTypeBinding = collection.resolvedType; + MethodBinding iteratorMethodBinding = + new MethodBinding( + AccPublic, + "iterator".toCharArray(),//$NON-NLS-1$ + scope.getJavaUtilIterator(), + TypeConstants.NoParameters, + TypeConstants.NoExceptions, + (ReferenceBinding) collectionTypeBinding); + if (collectionTypeBinding.isInterface()) { + codeStream.invokeinterface(iteratorMethodBinding); + } else { + codeStream.invokevirtual(iteratorMethodBinding); + } + codeStream.store(this.indexVariable, false); + break; + } + + // label management + Label actionLabel = new Label(codeStream); + Label conditionLabel = new Label(codeStream); + breakLabel.initialize(codeStream); + if (this.continueLabel != null) { + this.continueLabel.initialize(codeStream); + } + // jump over the actionBlock + codeStream.goto_(conditionLabel); + + // generate the loop action + actionLabel.place(); + + // generate the loop action + if (this.elementVariable.binding.resolvedPosition != -1) { + switch(this.kind) { + case ARRAY : + codeStream.load(this.collectionVariable); + codeStream.load(this.indexVariable); + codeStream.arrayAt(this.collectionElementType.id); + if (this.elementVariableImplicitWidening != -1) { + codeStream.generateImplicitConversion(this.elementVariableImplicitWidening); + } + codeStream.store(this.elementVariable.binding, false); + break; + case RAW_ITERABLE : + case GENERIC_ITERABLE : + codeStream.load(this.indexVariable); + codeStream.invokeJavaUtilIteratorNext(); + if (this.elementVariable.binding.type.id != T_JavaLangObject) { + if (this.elementVariableImplicitWidening != -1) { + codeStream.checkcast(this.collectionElementType); + codeStream.generateImplicitConversion(this.elementVariableImplicitWidening); + } else { + codeStream.checkcast(this.elementVariable.binding.type); + } + } + codeStream.store(this.elementVariable.binding, false); + break; + } + codeStream.addVisibleLocalVariable(this.elementVariable.binding); + if (this.postCollectionInitStateIndex != -1) { + codeStream.addDefinitelyAssignedVariables( + currentScope, + this.postCollectionInitStateIndex); + } + } + this.action.generateCode(scope, codeStream); + + // continuation point + int continuationPC = codeStream.position; + if (this.continueLabel != null) { + this.continueLabel.place(); + // generate the increments for next iteration + switch(this.kind) { + case ARRAY : + codeStream.iinc(this.indexVariable.resolvedPosition, 1); + break; + case RAW_ITERABLE : + case GENERIC_ITERABLE : + break; + } + } + // generate the condition + conditionLabel.place(); + if (this.postCollectionInitStateIndex != -1) { + codeStream.removeNotDefinitelyAssignedVariables(currentScope, postCollectionInitStateIndex); + } + switch(this.kind) { + case ARRAY : + codeStream.load(this.indexVariable); + codeStream.load(this.maxVariable); + codeStream.if_icmplt(actionLabel); + break; + case RAW_ITERABLE : + case GENERIC_ITERABLE : + codeStream.load(this.indexVariable); + codeStream.invokeJavaUtilIteratorHasNext(); + codeStream.ifne(actionLabel); + break; + } + codeStream.recordPositionsFrom(continuationPC, this.elementVariable.sourceStart); + + breakLabel.place(); + codeStream.exitUserScope(scope); + if (mergedInitStateIndex != -1) { + codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex); + codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex); + } + codeStream.recordPositionsFrom(pc, this.sourceStart); + } + + public StringBuffer printStatement(int tab, StringBuffer output) { + + printIndent(tab, output).append("for ("); //$NON-NLS-1$ + this.elementVariable.print(0, output); + output.append(" : ");//$NON-NLS-1$ + this.collection.print(0, output).append(") "); //$NON-NLS-1$ + //block + if (this.action == null) { + output.append(';'); + } else { + output.append('\n'); + this.action.printStatement(tab + 1, output); //$NON-NLS-1$ + } + return output; + } + + public void resolve(BlockScope upperScope) { + // use the scope that will hold the init declarations + scope = new BlockScope(upperScope); + this.elementVariable.resolve(scope); // collection expression can see itemVariable + TypeBinding elementType = this.elementVariable.type.resolvedType; + TypeBinding collectionType = this.collection.resolveType(scope); + this.collection.computeConversion(scope, collectionType, collectionType); + boolean hasError = elementType == null || collectionType == null; + + if (!hasError) { + if (collectionType.isArrayType()) { // for(E e : E[]) + this.kind = ARRAY; + this.collectionElementType = ((ArrayBinding) collectionType).elementsType(); + if (!collectionElementType.isCompatibleWith(elementType) + && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) { + scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType); + } + // in case we need to do a conversion + int compileTimeTypeID = collectionElementType.id; + if (elementType.isBaseType()) { + if (!collectionElementType.isBaseType()) { + compileTimeTypeID = scope.environment().computeBoxingType(collectionElementType).id; + this.elementVariableImplicitWidening = UNBOXING; + if (elementType.isBaseType()) { + this.elementVariableImplicitWidening |= (elementType.id << 4) + compileTimeTypeID; + } + } else { + this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID; + } + } else { + if (collectionElementType.isBaseType()) { + int boxedID = scope.environment().computeBoxingType(collectionElementType).id; + this.elementVariableImplicitWidening = BOXING | (compileTimeTypeID << 4) | compileTimeTypeID; // use primitive type in implicit conversion + compileTimeTypeID = boxedID; + } + } + } else if (collectionType instanceof ReferenceBinding) { + ReferenceBinding iterableType = ((ReferenceBinding)collectionType).findSuperTypeErasingTo(T_JavaLangIterable, false /*Iterable is not a class*/); + if (iterableType != null) { + if (iterableType.isParameterizedType()) { // for(E e : Iterable) + ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding)iterableType; + if (parameterizedType.arguments.length == 1) { // per construction can only be one + this.kind = GENERIC_ITERABLE; + this.collectionElementType = parameterizedType.arguments[0]; + if (!collectionElementType.isCompatibleWith(elementType) + && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) { + scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType); + } + int compileTimeTypeID = collectionElementType.id; + // no conversion needed as only for reference types + if (elementType.isBaseType()) { + if (!collectionElementType.isBaseType()) { + compileTimeTypeID = scope.environment().computeBoxingType(collectionElementType).id; + this.elementVariableImplicitWidening = UNBOXING; + if (elementType.isBaseType()) { + this.elementVariableImplicitWidening |= (elementType.id << 4) + compileTimeTypeID; + } + } else { + this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID; + } + } else { + if (collectionElementType.isBaseType()) { + int boxedID = scope.environment().computeBoxingType(collectionElementType).id; + this.elementVariableImplicitWidening = BOXING | (compileTimeTypeID << 4) | compileTimeTypeID; // use primitive type in implicit conversion + compileTimeTypeID = boxedID; + } + } + } + } else if (iterableType.isGenericType()) { // for (T t : Iterable) - in case used inside Iterable itself + if (iterableType.typeVariables().length == 1) { + this.kind = GENERIC_ITERABLE; + this.collectionElementType = iterableType.typeVariables()[0]; + if (!collectionElementType.isCompatibleWith(elementType) + && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) { + scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType); + } + int compileTimeTypeID = collectionElementType.id; + // no conversion needed as only for reference types + if (elementType.isBaseType()) { + if (!collectionElementType.isBaseType()) { + compileTimeTypeID = scope.environment().computeBoxingType(collectionElementType).id; + this.elementVariableImplicitWidening = UNBOXING; + if (elementType.isBaseType()) { + this.elementVariableImplicitWidening |= (elementType.id << 4) + compileTimeTypeID; + } + } else { + this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID; + } + } else { + if (collectionElementType.isBaseType()) { + int boxedID = scope.environment().computeBoxingType(collectionElementType).id; + this.elementVariableImplicitWidening = BOXING | (compileTimeTypeID << 4) | compileTimeTypeID; // use primitive type in implicit conversion + compileTimeTypeID = boxedID; + } + } + } + } else if (iterableType.isRawType()) { // for(Object o : Iterable) + this.kind = RAW_ITERABLE; + this.collectionElementType = scope.getJavaLangObject(); + if (!collectionElementType.isCompatibleWith(elementType) + && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) { + scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType); + } + // no conversion needed as only for reference types + } + } + } + switch(this.kind) { + case ARRAY : + // allocate #index secret variable (of type int) + this.indexVariable = new LocalVariableBinding(SecretIndexVariableName, IntBinding, AccDefault, false); + scope.addLocalVariable(this.indexVariable); + this.indexVariable.setConstant(NotAConstant); // not inlinable + + // allocate #max secret variable + this.maxVariable = new LocalVariableBinding(SecretMaxVariableName, IntBinding, AccDefault, false); + scope.addLocalVariable(this.maxVariable); + this.maxVariable.setConstant(NotAConstant); // not inlinable + // add #array secret variable (of collection type) + this.collectionVariable = new LocalVariableBinding(SecretCollectionVariableName, collectionType, AccDefault, false); + scope.addLocalVariable(this.collectionVariable); + this.collectionVariable.setConstant(NotAConstant); // not inlinable + break; + case RAW_ITERABLE : + case GENERIC_ITERABLE : + // allocate #index secret variable (of type Iterator) + this.indexVariable = new LocalVariableBinding(SecretIndexVariableName, scope.getJavaUtilIterator(), AccDefault, false); + scope.addLocalVariable(this.indexVariable); + this.indexVariable.setConstant(NotAConstant); // not inlinable + break; + default : + scope.problemReporter().invalidTypeForCollection(collection); + } + } + if (action != null) { + action.resolve(scope); + } + } + + public void traverse( + ASTVisitor visitor, + BlockScope blockScope) { + + if (visitor.visit(this, blockScope)) { + this.elementVariable.traverse(visitor, scope); + this.collection.traverse(visitor, scope); + if (action != null) { + action.traverse(visitor, scope); + } + } + visitor.endVisit(this, blockScope); + } +} diff --git a/src/org/eclipse/jdt/internal/compiler/ast/IfStatement.java b/src/org/eclipse/jdt/internal/compiler/ast/IfStatement.java index a262104..223a9ed 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/IfStatement.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/IfStatement.java @@ -71,6 +71,11 @@ public class IfStatement extends Statement { if (isConditionOptimizedFalse) { thenFlowInfo.setReachMode(FlowInfo.UNREACHABLE); } + FlowInfo elseFlowInfo = flowInfo.initsWhenFalse().copy(); + if (isConditionOptimizedTrue) { + elseFlowInfo.setReachMode(FlowInfo.UNREACHABLE); + } + this.condition.checkNullComparison(currentScope, flowContext, flowInfo, thenFlowInfo, elseFlowInfo); if (this.thenStatement != null) { // Save info for code gen thenInitStateIndex = @@ -84,10 +89,6 @@ public class IfStatement extends Statement { this.thenExit = !thenFlowInfo.isReachable(); // process the ELSE part - FlowInfo elseFlowInfo = flowInfo.initsWhenFalse().copy(); - if (isConditionOptimizedTrue) { - elseFlowInfo.setReachMode(FlowInfo.UNREACHABLE); - } if (this.elseStatement != null) { // signal else clause unnecessarily nested, tolerate else-if code pattern if (thenFlowInfo == FlowInfo.DEAD_END @@ -152,9 +153,7 @@ public class IfStatement extends Statement { true); // May loose some local variable initializations : affecting the local variable attributes if (thenInitStateIndex != -1) { - codeStream.removeNotDefinitelyAssignedVariables( - currentScope, - thenInitStateIndex); + codeStream.removeNotDefinitelyAssignedVariables(currentScope, thenInitStateIndex); codeStream.addDefinitelyAssignedVariables(currentScope, thenInitStateIndex); } // generate then statement @@ -221,7 +220,7 @@ public class IfStatement extends Statement { public void resolve(BlockScope scope) { TypeBinding type = condition.resolveTypeExpecting(scope, BooleanBinding); - condition.implicitWidening(type, type); + condition.computeConversion(scope, type, type); if (thenStatement != null) thenStatement.resolve(scope); if (elseStatement != null) diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ImplicitDocTypeReference.java b/src/org/eclipse/jdt/internal/compiler/ast/ImplicitDocTypeReference.java index 9dda67e..546e57c 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ImplicitDocTypeReference.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ImplicitDocTypeReference.java @@ -11,6 +11,7 @@ package org.eclipse.jdt.internal.compiler.ast; import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; @@ -34,7 +35,7 @@ public class ImplicitDocTypeReference extends TypeReference { /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.ast.TypeReference#getTypeBinding(org.eclipse.jdt.internal.compiler.lookup.Scope) */ - public TypeBinding getTypeBinding(Scope scope) { + protected TypeBinding getTypeBinding(Scope scope) { this.constant = NotAConstant; return this.resolvedType = scope.enclosingSourceType(); } @@ -52,6 +53,12 @@ public class ImplicitDocTypeReference extends TypeReference { return true; } /* (non-Javadoc) + * @see org.eclipse.jdt.internal.compiler.ast.TypeReference#traverse(org.eclipse.jdt.internal.compiler.ASTVisitor, org.eclipse.jdt.internal.compiler.lookup.BlockScope) + */ + public void traverse(ASTVisitor visitor, BlockScope classScope) { + // Do nothing + } + /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.ast.TypeReference#traverse(org.eclipse.jdt.internal.compiler.ASTVisitor, org.eclipse.jdt.internal.compiler.lookup.ClassScope) */ public void traverse(ASTVisitor visitor, ClassScope classScope) { diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ImportReference.java b/src/org/eclipse/jdt/internal/compiler/ast/ImportReference.java index 4623b2d..fca4408 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/ImportReference.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/ImportReference.java @@ -23,6 +23,7 @@ public class ImportReference extends ASTNode { public int declarationSourceEnd; public boolean used; public int modifiers; // 1.5 addition for static imports + public Annotation[] annotations; public ImportReference( char[][] tokens, @@ -37,6 +38,10 @@ public class ImportReference extends ASTNode { this.sourceStart = (int) (sourcePositions[0] >>> 32); this.modifiers = modifiers; } + + public boolean isStatic() { + return (this.modifiers & AccStatic) != 0; + } /** * @return char[][] @@ -67,6 +72,11 @@ public class ImportReference extends ASTNode { public void traverse(ASTVisitor visitor, CompilationUnitScope scope) { visitor.visit(this, scope); + if (this.annotations != null) { + int annotationsLength = this.annotations.length; + for (int i = 0; i < annotationsLength; i++) + this.annotations[i].traverse(visitor, scope); + } visitor.endVisit(this, scope); } } diff --git a/src/org/eclipse/jdt/internal/compiler/ast/Initializer.java b/src/org/eclipse/jdt/internal/compiler/ast/Initializer.java index ff53718..987149f 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/Initializer.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/Initializer.java @@ -57,16 +57,18 @@ public class Initializer extends FieldDeclaration { codeStream.recordPositionsFrom(pc, this.sourceStart); } - public boolean isField() { - - return false; + /** + * @see org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration#getKind() + */ + public int getKind() { + return INITIALIZER; } - + public boolean isStatic() { return (modifiers & AccStatic) != 0; } - + public void parseStatements( Parser parser, TypeDeclaration typeDeclaration, @@ -80,7 +82,9 @@ public class Initializer extends FieldDeclaration { if (modifiers != 0) { printIndent(indent, output); - printModifiers(modifiers, output).append("{\n"); //$NON-NLS-1$ + printModifiers(modifiers, output); + if (this.annotations != null) printAnnotations(this.annotations, output); + output.append("{\n"); //$NON-NLS-1$ block.printBody(indent, output); printIndent(indent, output).append('}'); return output; diff --git a/src/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java b/src/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java index feacdaf..5a47db8 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; -import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.codegen.*; import org.eclipse.jdt.internal.compiler.flow.*; @@ -38,139 +37,13 @@ public class InstanceOfExpression extends OperatorExpression { FlowContext flowContext, FlowInfo flowInfo) { - return expression + flowInfo = expression .analyseCode(currentScope, flowContext, flowInfo) .unconditionalInits(); + expression.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); + return flowInfo; } - /** - * Returns false if the instanceof unnecessary - */ - public final boolean checkCastTypesCompatibility( - BlockScope scope, - TypeBinding castType, - TypeBinding expressionType) { - - //A more complete version of this method is provided on - //CastExpression (it deals with constant and need runtime checkcast) - - if (castType == expressionType) return false; - - //by grammatical construction, the base type check is not necessary - if (castType == null || expressionType == null) return true; - - //-----------cast to something which is NOT a base type-------------------------- - if (expressionType == NullBinding) { - // if (castType.isArrayType()){ // 26903 - need checkcast when casting null to array type - // needRuntimeCheckcast = true; - // } - return false; //null is compatible with every thing - } - if (expressionType.isBaseType()) { - scope.problemReporter().notCompatibleTypesError(this, expressionType, castType); - return true; - } - - if (expressionType.isArrayType()) { - if (castType == expressionType) return false; // identity conversion - - if (castType.isArrayType()) { - //------- (castType.isArray) expressionType.isArray ----------- - TypeBinding exprElementType = ((ArrayBinding) expressionType).elementsType(scope); - if (exprElementType.isBaseType()) { - // <---stop the recursion------- - if (((ArrayBinding) castType).elementsType(scope) != exprElementType) - scope.problemReporter().notCompatibleTypesError(this, expressionType, castType); - return true; - } - // recursively on the elements... - return checkCastTypesCompatibility( - scope, - ((ArrayBinding) castType).elementsType(scope), - exprElementType); - } else if ( - castType.isClass()) { - //------(castType.isClass) expressionType.isArray --------------- - if (castType.id == T_Object) { - return false; - } - } else { //------- (castType.isInterface) expressionType.isArray ----------- - if (castType.id == T_JavaLangCloneable || castType.id == T_JavaIoSerializable) { - return true; - } - } - scope.problemReporter().notCompatibleTypesError(this, expressionType, castType); - return true; - } - - if (expressionType.isClass()) { - if (castType.isArrayType()) { - // ---- (castType.isArray) expressionType.isClass ------- - if (expressionType.id == T_Object) { // potential runtime error - return true; - } - } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isClass ------ - if (expressionType.isCompatibleWith(castType)){ // no runtime error - return false; - } - if (castType.isCompatibleWith(expressionType)) { - // potential runtime error - return true; - } - } else { // ----- (castType.isInterface) expressionType.isClass ------- - if (expressionType.isCompatibleWith(castType)) - return false; - if (!((ReferenceBinding) expressionType).isFinal()) { - // a subclass may implement the interface ==> no check at compile time - return true; - } - // no subclass for expressionType, thus compile-time check is valid - } - scope.problemReporter().notCompatibleTypesError(this, expressionType, castType); - return true; - } - - // if (expressionType.isInterface()) { cannot be anything else - if (castType.isArrayType()) { - // ----- (castType.isArray) expressionType.isInterface ------ - if (!(expressionType.id == T_JavaLangCloneable - || expressionType.id == T_JavaIoSerializable)) {// potential runtime error - scope.problemReporter().notCompatibleTypesError(this, expressionType, castType); - } - return true; - } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isInterface -------- - if (castType.id == T_Object) { // no runtime error - return false; - } - if (((ReferenceBinding) castType).isFinal()) { - // no subclass for castType, thus compile-time check is valid - if (!castType.isCompatibleWith(expressionType)) { - // potential runtime error - scope.problemReporter().notCompatibleTypesError(this, expressionType, castType); - return true; - } - } - } else { // ----- (castType.isInterface) expressionType.isInterface ------- - if (expressionType.isCompatibleWith(castType)) { - return false; - } - if (!castType.isCompatibleWith(expressionType)) { - MethodBinding[] castTypeMethods = ((ReferenceBinding) castType).methods(); - MethodBinding[] expressionTypeMethods = - ((ReferenceBinding) expressionType).methods(); - int exprMethodsLength = expressionTypeMethods.length; - for (int i = 0, castMethodsLength = castTypeMethods.length; i < castMethodsLength; i++) - for (int j = 0; j < exprMethodsLength; j++) { - if ((castTypeMethods[i].returnType != expressionTypeMethods[j].returnType) - && CharOperation.equals(castTypeMethods[i].selector, expressionTypeMethods[j].selector) - && castTypeMethods[i].areParametersEqual(expressionTypeMethods[j])) { - scope.problemReporter().notCompatibleTypesError(this, expressionType, castType); - } - } - } - } - return true; - } /** * Code generation for instanceOfExpression * @@ -186,8 +59,11 @@ public class InstanceOfExpression extends OperatorExpression { int pc = codeStream.position; expression.generateCode(currentScope, codeStream, true); codeStream.instance_of(type.resolvedType); - if (!valueRequired) + if (valueRequired) { + codeStream.generateImplicitConversion(implicitConversion); + } else { codeStream.pop(); + } codeStream.recordPositionsFrom(pc, this.sourceStart); } @@ -196,22 +72,33 @@ public class InstanceOfExpression extends OperatorExpression { expression.printExpression(indent, output).append(" instanceof "); //$NON-NLS-1$ return type.print(0, output); } - + /** + * @see org.eclipse.jdt.internal.compiler.ast.Expression#reportIllegalCast(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding) + */ + public void reportIllegalCast(Scope scope, TypeBinding castType, TypeBinding expressionType) { + scope.problemReporter().notCompatibleTypesError(this, expressionType, castType); + } public TypeBinding resolveType(BlockScope scope) { constant = NotAConstant; TypeBinding expressionType = expression.resolveType(scope); - TypeBinding checkType = type.resolveType(scope); - if (expressionType == null || checkType == null) + TypeBinding checkedType = type.resolveType(scope, true /* check bounds*/); + if (expressionType == null || checkedType == null) return null; - boolean necessary = checkCastTypesCompatibility(scope, checkType, expressionType); - if (!necessary) { - scope.problemReporter().unnecessaryInstanceof(this, checkType); + if (checkedType.isTypeVariable() || checkedType.isBoundParameterizedType() || checkedType.isGenericType()) { + scope.problemReporter().illegalInstanceOfGenericType(checkedType, this); + } else { + checkCastTypesCompatibility(scope, checkedType, expressionType, null); } return this.resolvedType = BooleanBinding; } - + /** + * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsUnnecessaryCast(Scope,TypeBinding) + */ + public void tagAsUnnecessaryCast(Scope scope, TypeBinding castType) { + scope.problemReporter().unnecessaryInstanceof(this, castType); + } public void traverse(ASTVisitor visitor, BlockScope scope) { if (visitor.visit(this, scope)) { diff --git a/src/org/eclipse/jdt/internal/compiler/ast/IntLiteral.java b/src/org/eclipse/jdt/internal/compiler/ast/IntLiteral.java index 46a2172..0e7821e 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/IntLiteral.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/IntLiteral.java @@ -56,7 +56,7 @@ public void computeConstant() { if (length == 1) { constant = Constant.fromValue(0); return ;} final int shift,radix; int j ; - if ( (source[1] == 'x') | (source[1] == 'X') ) + if ( (source[1] == 'x') || (source[1] == 'X') ) { shift = 4 ; j = 2; radix = 16;} else { shift = 3 ; j = 1; radix = 8;} @@ -94,11 +94,9 @@ public void computeConstant() { */ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { int pc = codeStream.position; - if (valueRequired) - if ((implicitConversion >> 4) == T_int) - codeStream.generateInlinedValue(value); - else - codeStream.generateConstant(constant, implicitConversion); + if (valueRequired) { + codeStream.generateConstant(constant, implicitConversion); + } codeStream.recordPositionsFrom(pc, this.sourceStart); } public TypeBinding literalType(BlockScope scope) { diff --git a/src/org/eclipse/jdt/internal/compiler/ast/Javadoc.java b/src/org/eclipse/jdt/internal/compiler/ast/Javadoc.java index f683964..03cdba4 100644 --- a/src/org/eclipse/jdt/internal/compiler/ast/Javadoc.java +++ b/src/org/eclipse/jdt/internal/compiler/ast/Javadoc.java @@ -11,17 +11,20 @@ package org.eclipse.jdt.internal.compiler.ast; import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser; /** * Node representing a structured Javadoc comment */ public class Javadoc extends ASTNode { - public JavadocSingleNameReference[] parameters; // @param - public TypeReference[] thrownExceptions; // @throws, @exception + public JavadocSingleNameReference[] paramReferences; // @param + public JavadocSingleTypeReference[] paramTypeParameters; // @param + public TypeReference[] exceptionReferences; // @throws, @exception public JavadocReturnStatement returnStatement; // @return - public Expression[] references; // @see + public Expression[] seeReferences; // @see public boolean inherited = false; // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600 // Store param references for tag with invalid syntax @@ -37,25 +40,32 @@ public class Javadoc extends ASTNode { */ public StringBuffer print(int indent, StringBuffer output) { printIndent(indent, output).append("/**\n"); //$NON-NLS-1$ - if (this.parameters != null) { - for (int i = 0, length = this.parameters.length; i < length; i++) { + if (this.paramReferences != null) { + for (int i = 0, length = this.paramReferences.length; i < length; i++) { printIndent(indent + 1, output).append(" * @param "); //$NON-NLS-1$ - this.parameters[i].print(indent, output).append('\n'); + this.paramReferences[i].print(indent, output).append('\n'); + } + } + if (this.paramTypeParameters != null) { + for (int i = 0, length = this.paramTypeParameters.length; i < length; i++) { + printIndent(indent + 1, output).append(" * @param <"); //$NON-NLS-1$ + this.paramTypeParameters[i].print(indent, output).append(">\n"); //$NON-NLS-1$ } } if (this.returnStatement != null) { - printIndent(indent + 1, output).append(" * @return\n"); //$NON-NLS-1$ + printIndent(indent + 1, output).append(" * @"); //$NON-NLS-1$ + this.returnStatement.print(indent, output).append('\n'); } - if (this.thrownExceptions != null) { - for (int i = 0, length = this.thrownExceptions.length; i < length; i++) { + if (this.exceptionReferences != null) { + for (int i = 0, length = this.exceptionReferences.length; i < length; i++) { printIndent(indent + 1, output).append(" * @throws "); //$NON-NLS-1$ - this.thrownExceptions[i].print(indent, output).append('\n'); + this.exceptionReferences[i].print(indent, output).append('\n'); } } - if (this.references != null) { - for (int i = 0, length = this.references.length; i < length; i++) { + if (this.seeReferences != null) { + for (int i = 0, length = this.seeReferences.length; i < length; i++) { printIndent(indent + 1, output).append(" * @see"); //$NON-NLS-1$ - this.references[i].print(indent, output).append('\n'); + this.seeReferences[i].print(indent, output).append('\n'); } } printIndent(indent, output).append(" */\n"); //$NON-NLS-1$ @@ -66,14 +76,14 @@ public class Javadoc extends ASTNode { * Resolve type javadoc while a class scope */ public void resolve(ClassScope classScope) { - // @param tags - int paramTagsSize = this.parameters == null ? 0 : this.parameters.length; + int paramTagsSize = this.paramReferences == null ? 0 : this.paramReferences.length; for (int i = 0; i < paramTagsSize; i++) { - JavadocSingleNameReference param = this.parameters[i]; + JavadocSingleNameReference param = this.paramReferences[i]; classScope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd); } + resolveTypeParameterTags(classScope, true); // @return tags if (this.returnStatement != null) { @@ -81,9 +91,9 @@ public class Javadoc extends ASTNode { } // @throws/@exception tags - int throwsTagsLength = this.thrownExceptions == null ? 0 : this.thrownExceptions.length; + int throwsTagsLength = this.exceptionReferences == null ? 0 : this.exceptionReferences.length; for (int i = 0; i < throwsTagsLength; i++) { - TypeReference typeRef = this.thrownExceptions[i]; + TypeReference typeRef = this.exceptionReferences[i]; int start, end; if (typeRef instanceof JavadocSingleTypeReference) { JavadocSingleTypeReference singleRef = (JavadocSingleTypeReference) typeRef; @@ -101,27 +111,9 @@ public class Javadoc extends ASTNode { } // @see tags - int seeTagsLength = this.references == null ? 0 : this.references.length; + int seeTagsLength = this.seeReferences == null ? 0 : this.seeReferences.length; for (int i = 0; i < seeTagsLength; i++) { - - // Resolve reference - this.references[i].resolveType(classScope); - - // Some unbound field reference might be changed to message send - // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51911 - if (this.references[i] instanceof JavadocFieldReference) { - JavadocFieldReference fieldRef = (JavadocFieldReference) this.references[i]; - if (fieldRef.receiverType != null && fieldRef.binding == null) { // binding was reset in case of valid method reference - // TODO (frederic) post 3.0 - avoid new instanciation of Compiler AST node - JavadocMessageSend msgSend = new JavadocMessageSend(fieldRef.token, fieldRef.nameSourcePosition); - msgSend.receiver = fieldRef.receiver; - msgSend.receiverType = fieldRef.receiverType; - msgSend.qualifyingType = fieldRef.receiverType; - msgSend.superAccess = classScope.enclosingSourceType().isCompatibleWith(msgSend.receiverType); - msgSend.binding = classScope.findMethod((ReferenceBinding)msgSend.receiverType, msgSend.selector, new TypeBinding[0], msgSend); - this.references[i] = msgSend; - } - } + resolveReference(this.seeReferences[i], classScope); } } @@ -132,40 +124,24 @@ public class Javadoc extends ASTNode { // get method declaration AbstractMethodDeclaration methDecl = methScope.referenceMethod(); - boolean overriding = methDecl == null ? false : (methDecl.binding.modifiers & (AccImplementing+AccOverriding)) != 0; + boolean overriding = methDecl == null ? false : (methDecl.binding.modifiers & (AccImplementing | AccOverriding)) != 0; // @see tags - int seeTagsLength = this.references == null ? 0 : this.references.length; + int seeTagsLength = this.seeReferences == null ? 0 : this.seeReferences.length; boolean superRef = false; for (int i = 0; i < seeTagsLength; i++) { // Resolve reference - this.references[i].resolveType(methScope); + resolveReference(this.seeReferences[i], methScope); - // Some unbound field reference might be changed to message send - // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51911 - if (this.references[i] instanceof JavadocFieldReference) { - JavadocFieldReference fieldRef = (JavadocFieldReference) this.references[i]; - if (fieldRef.receiverType != null && fieldRef.binding == null) { // binding was reset in case of valid method reference - // TODO (frederic) post 3.0 - avoid new instanciation of Compiler AST node - JavadocMessageSend msgSend = new JavadocMessageSend(fieldRef.token, fieldRef.nameSourcePosition); - msgSend.receiver = fieldRef.receiver; - msgSend.receiverType = fieldRef.receiverType; - msgSend.qualifyingType = fieldRef.receiverType; - msgSend.superAccess = methScope.enclosingSourceType().isCompatibleWith(msgSend.receiverType); - msgSend.binding = methScope.findMethod((ReferenceBinding)msgSend.receiverType, msgSend.selector, new TypeBinding[0], msgSend); - this.references[i] = msgSend; - } - } - // see whether we can have a super reference try { if (methDecl != null && (methDecl.isConstructor() || overriding) && !superRef) { - if (this.references[i] instanceof JavadocMessageSend) { - JavadocMessageSend messageSend = (JavadocMessageSend) this.references[i]; + if (this.seeReferences[i] instanceof JavadocMessageSend) { + JavadocMessageSend messageSend = (JavadocMessageSend) this.seeReferences[i]; // if binding is valid then look if we have a reference to an overriden method/constructor if (messageSend.binding != null && messageSend.binding.isValidBinding()) { - if (methDecl.binding.declaringClass.isCompatibleWith(messageSend.receiverType) && + if (methDecl.binding.declaringClass.isCompatibleWith(messageSend.actualReceiverType) && CharOperation.equals(messageSend.selector, methDecl.selector) && (messageSend.binding.returnType == methDecl.binding.returnType)) { if (messageSend.arguments == null && methDecl.arguments == null) { @@ -177,8 +153,8 @@ public class Javadoc extends ASTNode { } } } - else if (this.references[i] instanceof JavadocAllocationExpression) { - JavadocAllocationExpression allocationExpr = (JavadocAllocationExpression) this.references[i]; + else if (this.seeReferences[i] instanceof JavadocAllocationExpression) { + JavadocAllocationExpression allocationExpr = (JavadocAllocationExpression) this.seeReferences[i]; // if binding is valid then look if we have a reference to an overriden method/constructor if (allocationExpr.binding != null && allocationExpr.binding.isValidBinding()) { if (methDecl.binding.declaringClass.isCompatibleWith(allocationExpr.resolvedType)) { @@ -203,11 +179,12 @@ public class Javadoc extends ASTNode { // @param tags resolveParamTags(methScope, reportMissing); + resolveTypeParameterTags(methScope, reportMissing); // @return tags if (this.returnStatement == null) { if (reportMissing && methDecl != null) { - if (!methDecl.isConstructor() && !methDecl.isClinit()) { + if (methDecl.isMethod()) { MethodDeclaration meth = (MethodDeclaration) methDecl; if (meth.binding.returnType != VoidBinding) { // method with return should have @return tag @@ -229,17 +206,78 @@ public class Javadoc extends ASTNode { } } + private void resolveReference(Expression reference, Scope scope) { + + // Perform resolve + switch (scope.kind) { + case Scope.METHOD_SCOPE: + reference.resolveType((MethodScope)scope); + break; + case Scope.CLASS_SCOPE: + reference.resolveType((ClassScope)scope); + break; + } + + // Verify field references + boolean verifyValues = scope.environment().options.sourceLevel >= ClassFileConstants.JDK1_5; + if (reference instanceof JavadocFieldReference) { + JavadocFieldReference fieldRef = (JavadocFieldReference) reference; + int modifiers = fieldRef.binding==null ? -1 : fieldRef.binding.modifiers; + + // Verify if this is a method reference + // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51911 + if (fieldRef.methodBinding != null) { + // cannot refer to method for @value tag + if (fieldRef.tagValue == AbstractCommentParser.TAG_VALUE_VALUE) { + scope.problemReporter().javadocInvalidValueReference(fieldRef.sourceStart, fieldRef.sourceEnd, modifiers); + } + else if (fieldRef.receiverType != null) { + fieldRef.superAccess = scope.enclosingSourceType().isCompatibleWith(fieldRef.receiverType); + fieldRef.methodBinding = scope.findMethod((ReferenceBinding)fieldRef.receiverType, fieldRef.token, new TypeBinding[0], fieldRef); + } + } + + // Verify whether field ref should be static or not (for @value tags) + else if (verifyValues && fieldRef.binding != null && fieldRef.binding.isValidBinding()) { + if (fieldRef.tagValue == AbstractCommentParser.TAG_VALUE_VALUE && !fieldRef.binding.isStatic()) { + scope.problemReporter().javadocInvalidValueReference(fieldRef.sourceStart, fieldRef.sourceEnd, modifiers); + } + } + } + + // If not 1.5 level, verification is finished + if (!verifyValues) return; + + // Verify that message reference are not used for @value tags + else if (reference instanceof JavadocMessageSend) { + JavadocMessageSend msgSend = (JavadocMessageSend) reference; + int modifiers = msgSend.binding==null ? -1 : msgSend.binding.modifiers; + if (msgSend.tagValue == AbstractCommentParser.TAG_VALUE_VALUE) { // cannot refer to method for @value tag + scope.problemReporter().javadocInvalidValueReference(msgSend.sourceStart, msgSend.sourceEnd, modifiers); + } + } + + // Verify that constructorreference are not used for @value tags + else if (reference instanceof JavadocAllocationExpression) { + JavadocAllocationExpression alloc = (JavadocAllocationExpression) reference; + int modifiers = alloc.binding==null ? -1 : alloc.binding.modifiers; + if (alloc.tagValue == AbstractCommentParser.TAG_VALUE_VALUE) { // cannot refer to method for @value tag + scope.problemReporter().javadocInvalidValueReference(alloc.sourceStart, alloc.sourceEnd, modifiers); + } + } + } + /* * Resolve @param tags while method scope */ private void resolveParamTags(MethodScope methScope, boolean reportMissing) { AbstractMethodDeclaration md = methScope.referenceMethod(); - int paramTagsSize = this.parameters == null ? 0 : this.parameters.length; + int paramTagsSize = this.paramReferences == null ? 0 : this.paramReferences.length; // If no referenced method (field initializer for example) then report a problem for each param tag if (md == null) { for (int i = 0; i < paramTagsSize; i++) { - JavadocSingleNameReference param = this.parameters[i]; + JavadocSingleNameReference param = this.paramReferences[i]; methScope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd); } return; @@ -251,7 +289,7 @@ public class Javadoc extends ASTNode { if (reportMissing) { for (int i = 0; i < argumentsSize; i++) { Argument arg = md.arguments[i]; - methScope.problemReporter().javadocMissingParamTag(arg, md.binding.modifiers); + methScope.problemReporter().javadocMissingParamTag(arg.name, arg.sourceStart, arg.sourceEnd, md.binding.modifiers); } } } else { @@ -260,14 +298,14 @@ public class Javadoc extends ASTNode { // Scan all @param tags for (int i = 0; i < paramTagsSize; i++) { - JavadocSingleNameReference param = this.parameters[i]; + JavadocSingleNameReference param = this.paramReferences[i]; param.resolve(methScope); if (param.binding != null && param.binding.isValidBinding()) { // Verify duplicated tags boolean found = false; for (int j = 0; j < maxBindings && !found; j++) { if (bindings[j] == param.binding) { - methScope.problemReporter().javadocDuplicatedParamTag(param, md.binding.modifiers); + methScope.problemReporter().javadocDuplicatedParamTag(param.token, param.sourceStart, param.sourceEnd, md.binding.modifiers); found = true; } } @@ -289,7 +327,103 @@ public class Javadoc extends ASTNode { } } if (!found) { - methScope.problemReporter().javadocMissingParamTag(arg, md.binding.modifiers); + methScope.problemReporter().javadocMissingParamTag(arg.name, arg.sourceStart, arg.sourceEnd, md.binding.modifiers); + } + } + } + } + } + + /* + * Resolve @param tags for type parameters + */ + private void resolveTypeParameterTags(Scope scope, boolean reportMissing) { + int paramTypeParamLength = this.paramTypeParameters == null ? 0 : this.paramTypeParameters.length; + + // Get declaration infos + TypeDeclaration typeDeclaration = null; + AbstractMethodDeclaration methodDeclaration = null; + TypeVariableBinding[] typeVariables = null; + int modifiers = -1; + switch (scope.kind) { + case Scope.METHOD_SCOPE: + methodDeclaration = ((MethodScope)scope).referenceMethod(); + // If no referenced method (field initializer for example) then report a problem for each param tag + if (methodDeclaration == null) { + for (int i = 0; i < paramTypeParamLength; i++) { + JavadocSingleNameReference param = this.paramReferences[i]; + scope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd); + } + return; + } + typeVariables = methodDeclaration.binding.typeVariables; + modifiers = methodDeclaration.binding.modifiers; + break; + case Scope.CLASS_SCOPE: + typeDeclaration = ((ClassScope) scope).referenceContext; + typeVariables = typeDeclaration.binding.typeVariables; + modifiers = typeDeclaration.binding.modifiers; + break; + } + + // If no type variables then report a problem for each param type parameter tag + if (typeVariables == null || typeVariables.length == 0) { + for (int i = 0; i < paramTypeParamLength; i++) { + JavadocSingleTypeReference param = this.paramTypeParameters[i]; + scope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd); + } + return; + } + + // If no param tags then report a problem for each declaration type parameter + TypeParameter[] parameters = typeDeclaration==null ? methodDeclaration.typeParameters() : typeDeclaration.typeParameters; + int typeParametersLength = parameters == null ? 0 : parameters.length; + if (paramTypeParamLength == 0) { + if (reportMissing) { + for (int i = 0, l=parameters.length; i