### Eclipse Workspace Patch 1.0
#P org.eclipse.jdt.core
Index: compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java,v
retrieving revision 1.88
diff -u -r1.88 Assignment.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 7 Mar 2009 00:58:57 -0000 1.88
+++ compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 11 Apr 2009 18:53:57 -0000
@@ -154,7 +154,15 @@
if (lhsType != null) {
this.resolvedType = lhsType.capture(scope, this.sourceEnd);
}
- TypeBinding rhsType = this.expression.resolveType(scope);
+ TypeBinding rhsType ;
+ if(this.expression instanceof BinaryExpression){
+ BinaryExpression bin=(BinaryExpression)this.expression;
+ bin.operatorOverloader=true;
+ rhsType= this.expression.resolveType(scope);
+ if(rhsType!=null && bin.messageSend !=null)
+ this.expression = bin.messageSend;
+ }else
+ rhsType= this.expression.resolveType(scope);
if (lhsType == null || rhsType == null) {
return null;
}
Index: compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java,v
retrieving revision 1.68
diff -u -r1.68 BinaryExpression.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java 26 Nov 2008 17:56:55 -0000 1.68
+++ compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java 11 Apr 2009 18:53:58 -0000
@@ -1788,6 +1788,9 @@
return this.right.printExpression(0, output);
}
+public boolean operatorOverloader;
+public MessageSend messageSend;
+
public TypeBinding resolveType(BlockScope scope) {
// keep implementation in sync with CombinedBinaryExpression#resolveType
// and nonRecursiveResolveTypeUpwards
@@ -1817,6 +1820,7 @@
rightTypeID = scope.environment().computeBoxingType(rightType).id;
}
}
+ int operator = (this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
if (leftTypeID > 15
|| rightTypeID > 15) { // must convert String + Object || Object + String
if (leftTypeID == TypeIds.T_JavaLangString) {
@@ -1824,12 +1828,23 @@
} else if (rightTypeID == TypeIds.T_JavaLangString) {
leftTypeID = TypeIds.T_JavaLangObject;
} else {
+ if(this.operatorOverloader && (operator==PLUS || operator==MINUS || operator==MULTIPLY || operator==DIVIDE)){
+ this.messageSend=new Expression2MessageSend(this, leftType, rightType);
+// this.messageSend.expectedType = expectedType;
+ TypeBinding resolveType = this.messageSend.resolveType(scope);
+ if(resolveType!=null){
+ return resolveType;
+ }
+ this.messageSend = null;
+ this.operatorOverloader = false;
+ //continue to old process
+ }
this.constant = Constant.NotAConstant;
scope.problemReporter().invalidOperator(this, leftType, rightType);
return null;
}
}
- if (((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS) {
+ if (operator == OperatorIds.PLUS) {
if (leftTypeID == TypeIds.T_JavaLangString) {
this.left.computeConversion(scope, leftType, leftType);
if (rightType.isArrayType() && ((ArrayBinding) rightType).elementsType() == TypeBinding.CHAR) {
@@ -1851,7 +1866,6 @@
// 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 = (this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
int operatorSignature = OperatorExpression.OperatorSignatures[operator][(leftTypeID << 4) + rightTypeID];
this.left.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F), leftType);
Index: compiler/org/eclipse/jdt/internal/compiler/ast/Expression2MessageSend.java
===================================================================
RCS file: compiler/org/eclipse/jdt/internal/compiler/ast/Expression2MessageSend.java
diff -N compiler/org/eclipse/jdt/internal/compiler/ast/Expression2MessageSend.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ compiler/org/eclipse/jdt/internal/compiler/ast/Expression2MessageSend.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,306 @@
+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.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.impl.Constant;
+import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
+import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TagBits;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
+
+/**
+ * Operator overload implementation prototype.
+ *
process 3 kind of case:
+ * - default operator names, such as add, subtract
+ *
- annotated method,
+ *
+ *
+ * package javax.syntax.sugar;
+ * public static @interface Operator{
+ string value();
+ //or OperatorEnum type
+ }
+ class Model{
+ @Operator(PLUS)
+ public void add(){}
+ }
+ new added class maybe copy this annotation,
+ Don't compare with Annotation.class, just for name "javax.syntax.sugar.Operator",
+ because of OSGi bundle dependency;
+
+ *
+ * - external described annotated method, ON document format
+ * Operator{
+ * Java.math.BigInteger{PLUS: add; minus: anotherSubtract}
+ * test.model.Account {PLUS: withdraw; minus: rob}
+ * }
+ * configure JDT preference to setting external document location.
+ *
+ * @author XIANGYA
+ * @since 0.1
+ * @version 0.1
+ */
+public class Expression2MessageSend extends MessageSend{
+ private final static char[][] DefaultOperatorNames=new char[][]{//
+ "add".toCharArray(), //$NON-NLS-1$
+ "subtract".toCharArray(),//$NON-NLS-1$
+ "multiply".toCharArray(),//$NON-NLS-1$
+ "divide".toCharArray(),//$NON-NLS-1$
+ };
+
+ private TypeBinding[] resolvedArgumentTypes;
+ public Expression2MessageSend(BinaryExpression binaryExpression, TypeBinding leftType, TypeBinding rightType) {
+ this.receiver = binaryExpression.left;
+ switch((binaryExpression.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT){
+ case OperatorIds.PLUS:
+ this.selector = DefaultOperatorNames[0];
+ break;
+ case OperatorIds.MINUS:
+ this.selector = DefaultOperatorNames[0];
+ break;
+ case OperatorIds.MULTIPLY:
+ this.selector = DefaultOperatorNames[0];
+ break;
+ case OperatorIds.DIVIDE:
+ this.selector = DefaultOperatorNames[0];
+ break;
+ }
+ this.arguments = new Expression[] { binaryExpression.right };
+ this.sourceStart = binaryExpression.sourceStart;
+ this.sourceEnd = binaryExpression.sourceEnd;
+ this.actualReceiverType = leftType;
+ this.resolvedArgumentTypes = new TypeBinding[]{rightType};
+ }
+
+ public TypeBinding resolveType(BlockScope scope) {
+
+ // Answer the signature return type
+ // Base type promotion
+
+ this.constant = Constant.NotAConstant;
+ boolean receiverCast = false, argsContainCast = false;
+ if (this.receiver instanceof CastExpression) {
+ this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
+ receiverCast = true;
+ }
+ //this.actualReceiverType = this.receiver.resolveType(scope);
+ boolean receiverIsType = this.receiver instanceof NameReference && (((NameReference) this.receiver).bits & Binding.TYPE) != 0;
+ if (receiverCast && this.actualReceiverType != null) {
+ // due to change of declaring class with receiver type, only identity cast should be notified
+ if (((CastExpression)this.receiver).expression.resolvedType == this.actualReceiverType) {
+ return null;
+ //scope.problemReporter().unnecessaryCast((CastExpression)this.receiver);
+ }
+ }
+ // resolve type arguments (for generic constructor call)
+ if (this.typeArguments != null) {
+ int length = this.typeArguments.length;
+ boolean argHasError = scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_5; // typeChecks all arguments
+ this.genericTypeArguments = new TypeBinding[length];
+ for (int i = 0; i < length; i++) {
+ TypeReference typeReference = this.typeArguments[i];
+ if ((this.genericTypeArguments[i] = typeReference.resolveType(scope, true /* check bounds*/)) == null) {
+ argHasError = true;
+ }
+ if (argHasError && typeReference instanceof Wildcard) {
+ return null;
+ //scope.problemReporter().illegalUsageOfWildcard(typeReference);
+ }
+ }
+ if (argHasError) {
+ if (this.arguments != null) { // still attempt to resolve arguments
+ for (int i = 0, max = this.arguments.length; i < max; i++) {
+ this.arguments[i].resolveType(scope);
+ }
+ }
+ return null;
+ }
+ }
+ // will check for null after args are resolved
+// TypeBinding[] argumentTypes = Binding.NO_PARAMETERS;
+ TypeBinding[] argumentTypes = this.resolvedArgumentTypes;
+ if (this.arguments != null) {
+// boolean argHasError = false; // typeChecks all arguments
+ int length = this.arguments.length;
+ boolean argHasError = this.resolvedArgumentTypes[0]==null;
+// argumentTypes = new TypeBinding[length];
+// for (int i = 0; i < length; i++){
+// Expression argument = this.arguments[i];
+// if (argument instanceof CastExpression) {
+// argument.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
+// argsContainCast = true;
+// }
+// if ((argumentTypes[i] = argument.resolveType(scope)) == null){
+// argHasError = true;
+// }
+// }
+ if (argHasError) {
+ if (this.actualReceiverType instanceof ReferenceBinding) {
+ // record a best guess, for clients who need hint about possible method match
+ TypeBinding[] pseudoArgs = new TypeBinding[length];
+ for (int i = length; --i >= 0;)
+ pseudoArgs[i] = argumentTypes[i] == null ? TypeBinding.NULL : argumentTypes[i]; // replace args with errors with null type
+ this.binding =
+ this.receiver.isImplicitThis()
+ ? scope.getImplicitMethod(this.selector, pseudoArgs, this)
+ : scope.findMethod((ReferenceBinding) this.actualReceiverType, this.selector, pseudoArgs, this);
+ if (this.binding != null && !this.binding.isValidBinding()) {
+ MethodBinding closestMatch = ((ProblemMethodBinding)this.binding).closestMatch;
+ // record the closest match, for clients who may still need hint about possible method match
+ if (closestMatch != null) {
+ if (closestMatch.original().typeVariables != Binding.NO_TYPE_VARIABLES) { // generic method
+ // shouldn't return generic method outside its context, rather convert it to raw method (175409)
+ closestMatch = scope.environment().createParameterizedGenericMethod(closestMatch.original(), (RawTypeBinding)null);
+ }
+ this.binding = closestMatch;
+ MethodBinding closestMatchOriginal = closestMatch.original();
+ if (closestMatchOriginal.isOrEnclosedByPrivateType() && !scope.isDefinedInMethod(closestMatchOriginal)) {
+ // ignore cases where method is used from within inside itself (e.g. direct recursions)
+ closestMatchOriginal.modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
+ }
+ }
+ }
+ }
+ return null;
+ }
+ }
+ if (this.actualReceiverType == null) {
+ return null;
+ }
+ // base type cannot receive any message
+ if (this.actualReceiverType.isBaseType()) {
+ return null;
+ // scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes);
+ }
+ this.binding = this.receiver.isImplicitThis()
+ ? scope.getImplicitMethod(this.selector, argumentTypes, this)
+ : scope.getMethod(this.actualReceiverType, this.selector, argumentTypes, this);
+ if (!this.binding.isValidBinding()) {
+ if (this.binding.declaringClass == null) {
+ if (this.actualReceiverType instanceof ReferenceBinding) {
+ this.binding.declaringClass = (ReferenceBinding) this.actualReceiverType;
+ } else {
+ return null;
+// scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes);
+ }
+ }
+ scope.problemReporter().invalidMethod(this, this.binding);
+ MethodBinding closestMatch = ((ProblemMethodBinding)this.binding).closestMatch;
+ switch (this.binding.problemId()) {
+ case ProblemReasons.Ambiguous :
+ break; // no resilience on ambiguous
+ case ProblemReasons.NotVisible :
+ case ProblemReasons.NonStaticReferenceInConstructorInvocation :
+ case ProblemReasons.NonStaticReferenceInStaticContext :
+ case ProblemReasons.ReceiverTypeNotVisible :
+ case ProblemReasons.ParameterBoundMismatch :
+ // only steal returnType in cases listed above
+ if (closestMatch != null) this.resolvedType = closestMatch.returnType;
+ break;
+ }
+ // record the closest match, for clients who may still need hint about possible method match
+ if (closestMatch != null) {
+ this.binding = closestMatch;
+ MethodBinding closestMatchOriginal = closestMatch.original();
+ if (closestMatchOriginal.isOrEnclosedByPrivateType() && !scope.isDefinedInMethod(closestMatchOriginal)) {
+ // ignore cases where method is used from within inside itself (e.g. direct recursions)
+ closestMatchOriginal.modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
+ }
+ }
+ return (this.resolvedType != null && (this.resolvedType.tagBits & TagBits.HasMissingType) == 0)
+ ? this.resolvedType
+ : null;
+ }
+ if ((this.binding.tagBits & TagBits.HasMissingType) != 0) {
+ scope.problemReporter().missingTypeInMethod(this, this.binding);
+ }
+ final CompilerOptions compilerOptions = scope.compilerOptions();
+ if (!this.binding.isStatic()) {
+ // the "receiver" must not be a type
+ if (receiverIsType) {
+ scope.problemReporter().mustUseAStaticMethod(this, this.binding);
+ if (this.actualReceiverType.isRawType()
+ && (this.receiver.bits & ASTNode.IgnoreRawTypeCheck) == 0
+ && compilerOptions.getSeverity(CompilerOptions.RawTypeReference) != ProblemSeverities.Ignore) {
+ scope.problemReporter().rawTypeReference(this.receiver, this.actualReceiverType);
+ }
+ } else {
+ // handle indirect inheritance thru variable secondary bound
+ // receiver may receive generic cast, as part of implicit conversion
+ TypeBinding oldReceiverType = this.actualReceiverType;
+ this.actualReceiverType = this.actualReceiverType.getErasureCompatibleType(this.binding.declaringClass);
+ this.receiver.computeConversion(scope, this.actualReceiverType, this.actualReceiverType);
+ if (this.actualReceiverType != oldReceiverType && this.receiver.postConversionType(scope) != this.actualReceiverType) { // record need for explicit cast at codegen since receiver could not handle it
+ this.bits |= NeedReceiverGenericCast;
+ }
+ }
+ } else {
+ // static message invoked through receiver? legal but unoptimal (optional warning).
+ if (!(this.receiver.isImplicitThis() || this.receiver.isSuper() || receiverIsType)) {
+ scope.problemReporter().nonStaticAccessToStaticMethod(this, this.binding);
+ }
+ if (!this.receiver.isImplicitThis() && this.binding.declaringClass != this.actualReceiverType) {
+ scope.problemReporter().indirectAccessToStaticMethod(this, this.binding);
+ }
+ }
+ if (checkInvocationArguments(scope, this.receiver, this.actualReceiverType, this.binding, this.arguments, argumentTypes, argsContainCast, this)) {
+ this.bits |= ASTNode.Unchecked;
+ }
+
+ //-------message send that are known to fail at compile time-----------
+ if (this.binding.isAbstract()) {
+ if (this.receiver.isSuper()) {
+ scope.problemReporter().cannotDireclyInvokeAbstractMethod(this, this.binding);
+ }
+ // abstract private methods cannot occur nor abstract static............
+ }
+ if (isMethodUseDeprecated(this.binding, scope, true))
+ scope.problemReporter().deprecatedMethod(this.binding, this);
+
+ // from 1.5 source level on, array#clone() returns the array type (but binding still shows Object)
+ if (this.binding == scope.environment().arrayClone && compilerOptions.sourceLevel >= ClassFileConstants.JDK1_5) {
+ this.resolvedType = this.actualReceiverType;
+ } else {
+ TypeBinding returnType;
+ if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) {
+ returnType = this.binding.original().returnType;
+ if (returnType != null) {
+ returnType = scope.environment().convertToRawType(returnType.erasure(), true);
+ }
+ } else {
+ returnType = this.binding.returnType;
+ if (returnType != null) {
+ returnType = returnType.capture(scope, this.sourceEnd);
+ }
+ }
+ this.resolvedType = returnType;
+ }
+ if (this.receiver.isSuper() && compilerOptions.getSeverity(CompilerOptions.OverridingMethodWithoutSuperInvocation) != ProblemSeverities.Ignore) {
+ final ReferenceContext referenceContext = scope.methodScope().referenceContext;
+ if (referenceContext instanceof AbstractMethodDeclaration) {
+ final AbstractMethodDeclaration abstractMethodDeclaration = (AbstractMethodDeclaration) referenceContext;
+ MethodBinding enclosingMethodBinding = abstractMethodDeclaration.binding;
+ if (enclosingMethodBinding.isOverriding()
+ && CharOperation.equals(this.binding.selector, enclosingMethodBinding.selector)
+ && this.binding.areParametersEqual(enclosingMethodBinding)) {
+ abstractMethodDeclaration.bits |= ASTNode.OverridingMethodWithSupercall;
+ }
+ }
+ }
+ if (this.typeArguments != null && this.binding.original().typeVariables == Binding.NO_TYPE_VARIABLES) {
+ scope.problemReporter().unnecessaryTypeArgumentsForMethodInvocation(this.binding, this.genericTypeArguments, this.typeArguments);
+ }
+ return (this.resolvedType.tagBits & TagBits.HasMissingType) == 0
+ ? this.resolvedType
+ : null;
+ }
+}