1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
13 import java.util.ArrayList;
15 import org.eclipse.jdt.core.compiler.CharOperation;
16 import org.eclipse.jdt.internal.compiler.ASTVisitor;
17 import org.eclipse.jdt.internal.compiler.impl.*;
18 import org.eclipse.jdt.internal.compiler.codegen.*;
19 import org.eclipse.jdt.internal.compiler.flow.*;
20 import org.eclipse.jdt.internal.compiler.lookup.*;
21 import org.eclipse.jdt.internal.compiler.problem.*;
22 import org.eclipse.jdt.internal.compiler.util.Util;
24 public abstract class Expression extends Statement {
26 public static final boolean isConstantValueRepresentable(
31 //true if there is no loss of precision while casting.
32 // constantTypeID == constant.typeID
33 if (targetTypeID == constantTypeID)
35 switch (targetTypeID) {
37 switch (constantTypeID) {
41 return constant.doubleValue() == constant.charValue();
43 return constant.floatValue() == constant.charValue();
45 return constant.intValue() == constant.charValue();
47 return constant.shortValue() == constant.charValue();
49 return constant.byteValue() == constant.charValue();
51 return constant.longValue() == constant.charValue();
53 return false;//boolean
57 switch (constantTypeID) {
59 return constant.charValue() == constant.floatValue();
61 return constant.doubleValue() == constant.floatValue();
65 return constant.intValue() == constant.floatValue();
67 return constant.shortValue() == constant.floatValue();
69 return constant.byteValue() == constant.floatValue();
71 return constant.longValue() == constant.floatValue();
73 return false;//boolean
77 switch (constantTypeID) {
79 return constant.charValue() == constant.doubleValue();
83 return constant.floatValue() == constant.doubleValue();
85 return constant.intValue() == constant.doubleValue();
87 return constant.shortValue() == constant.doubleValue();
89 return constant.byteValue() == constant.doubleValue();
91 return constant.longValue() == constant.doubleValue();
93 return false; //boolean
97 switch (constantTypeID) {
99 return constant.charValue() == constant.byteValue();
101 return constant.doubleValue() == constant.byteValue();
103 return constant.floatValue() == constant.byteValue();
105 return constant.intValue() == constant.byteValue();
107 return constant.shortValue() == constant.byteValue();
111 return constant.longValue() == constant.byteValue();
113 return false; //boolean
117 switch (constantTypeID) {
119 return constant.charValue() == constant.shortValue();
121 return constant.doubleValue() == constant.shortValue();
123 return constant.floatValue() == constant.shortValue();
125 return constant.intValue() == constant.shortValue();
129 return constant.byteValue() == constant.shortValue();
131 return constant.longValue() == constant.shortValue();
133 return false; //boolean
137 switch (constantTypeID) {
139 return constant.charValue() == constant.intValue();
141 return constant.doubleValue() == constant.intValue();
143 return constant.floatValue() == constant.intValue();
147 return constant.shortValue() == constant.intValue();
149 return constant.byteValue() == constant.intValue();
151 return constant.longValue() == constant.intValue();
153 return false; //boolean
157 switch (constantTypeID) {
159 return constant.charValue() == constant.longValue();
161 return constant.doubleValue() == constant.longValue();
163 return constant.floatValue() == constant.longValue();
165 return constant.intValue() == constant.longValue();
167 return constant.shortValue() == constant.longValue();
169 return constant.byteValue() == constant.longValue();
173 return false; //boolean
177 return false; //boolean
181 public Constant constant;
183 //Some expression may not be used - from a java semantic point
184 //of view only - as statements. Other may. In order to avoid the creation
185 //of wrappers around expression in order to tune them as expression
186 //Expression is a subclass of Statement. See the message isValidJavaStatement()
188 public int implicitConversion;
189 public TypeBinding resolvedType;
191 public Expression() {
195 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
200 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) {
202 return analyseCode(currentScope, flowContext, flowInfo);
206 * Returns false if cast is not legal.
208 public final boolean checkCastTypesCompatibility(
210 TypeBinding castType,
211 TypeBinding expressionType,
212 Expression expression) {
214 // see specifications 5.5
215 // handle errors and process constant when needed
217 // if either one of the type is null ==>
218 // some error has been already reported some where ==>
219 // we then do not report an obvious-cascade-error.
221 if (castType == null || expressionType == null) return true;
223 // identity conversion cannot be performed upfront, due to side-effects
224 // like constant propagation
225 LookupEnvironment env = scope.environment();
226 boolean use15specifics = env.options.sourceLevel >= JDK1_5;
227 if (castType.isBaseType()) {
228 if (expressionType.isBaseType()) {
229 if (expressionType == castType) {
230 if (expression != null) {
231 this.constant = expression.constant; //use the same constant
233 tagAsUnnecessaryCast(scope, castType);
236 boolean necessary = false;
237 if (expressionType.isCompatibleWith(castType)
238 || (necessary = BaseTypeBinding.isNarrowing(castType.id, expressionType.id))) {
239 if (expression != null) {
240 expression.implicitConversion = (castType.id << 4) + expressionType.id;
241 if (expression.constant != Constant.NotAConstant) {
242 constant = expression.constant.castTo(expression.implicitConversion);
245 if (!necessary) tagAsUnnecessaryCast(scope, castType);
249 } else if (use15specifics) { // unboxing - only exact match is allowed
250 if (env.computeBoxingType(expressionType) == castType) {
251 // TODO (philippe) could tagAsUnnecessaryCast(scope, castType);
255 reportIllegalCast(scope, castType, expressionType);
257 } else if (use15specifics && expressionType.isBaseType()) { // boxing - only exact match is allowed
258 if (env.computeBoxingType(castType) == expressionType) {
259 // TODO (philippe) could tagAsUnnecessaryCast(scope, castType);
264 //-----------cast to something which is NOT a base type--------------------------
265 if (expressionType == NullBinding) {
266 tagAsUnnecessaryCast(scope, castType);
267 return true; //null is compatible with every thing
269 if (expressionType.isBaseType()) {
270 reportIllegalCast(scope, castType, expressionType);
274 if (expressionType.isArrayType()) {
275 if (castType == expressionType) {
276 tagAsUnnecessaryCast(scope, castType);
277 return true; // identity conversion
280 if (castType.isArrayType()) {
281 //------- (castType.isArray) expressionType.isArray -----------
282 TypeBinding exprElementType = ((ArrayBinding) expressionType).elementsType();
283 if (exprElementType.isBaseType()) {
284 // <---stop the recursion-------
285 if (((ArrayBinding) castType).elementsType() == exprElementType) {
286 tagAsNeedCheckCast();
289 reportIllegalCast(scope, castType, expressionType);
293 // recursively on the elements...
294 return checkCastTypesCompatibility(
296 ((ArrayBinding) castType).elementsType(),
300 castType.isClass()) {
301 //------(castType.isClass) expressionType.isArray ---------------
302 if (castType.id == T_JavaLangObject) {
303 tagAsUnnecessaryCast(scope, castType);
306 } else { //------- (castType.isInterface) expressionType.isArray -----------
307 if (castType.id == T_JavaLangCloneable || castType.id == T_JavaIoSerializable) {
308 tagAsNeedCheckCast();
312 reportIllegalCast(scope, castType, expressionType);
316 if (expressionType.isClass()) {
317 if (castType.isArrayType()) {
318 // ---- (castType.isArray) expressionType.isClass -------
319 if (expressionType.id == T_JavaLangObject) { // potential runtime error
320 tagAsNeedCheckCast();
323 } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isClass ------
325 ReferenceBinding match = ((ReferenceBinding)expressionType).findSuperTypeErasingTo((ReferenceBinding)castType.erasure());
327 if (expression != null && castType.id == T_JavaLangString) this.constant = expression.constant; // (String) cst is still a constant
328 return checkUnsafeCast(scope, castType, expressionType, match, false);
330 match = ((ReferenceBinding)castType).findSuperTypeErasingTo((ReferenceBinding)expressionType.erasure());
332 tagAsNeedCheckCast();
333 return checkUnsafeCast(scope, castType, expressionType, match, true);
335 } else { // ----- (castType.isInterface) expressionType.isClass -------
337 ReferenceBinding match = ((ReferenceBinding)expressionType).findSuperTypeErasingTo((ReferenceBinding)castType.erasure());
339 return checkUnsafeCast(scope, castType, expressionType, match, false);
341 // a subclass may implement the interface ==> no check at compile time
342 if (!((ReferenceBinding) expressionType).isFinal()) {
343 tagAsNeedCheckCast();
344 match = ((ReferenceBinding)castType).findSuperTypeErasingTo((ReferenceBinding)expressionType.erasure());
346 return checkUnsafeCast(scope, castType, expressionType, match, true);
350 // no subclass for expressionType, thus compile-time check is valid
352 reportIllegalCast(scope, castType, expressionType);
356 // if (expressionType.isInterface()) { cannot be anything else
357 if (castType.isArrayType()) {
358 // ----- (castType.isArray) expressionType.isInterface ------
359 if (expressionType.id == T_JavaLangCloneable
360 || expressionType.id == T_JavaIoSerializable) {// potential runtime error
361 tagAsNeedCheckCast();
364 reportIllegalCast(scope, castType, expressionType);
367 } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isInterface --------
369 if (castType.id == T_JavaLangObject) { // no runtime error
370 tagAsUnnecessaryCast(scope, castType);
373 if (((ReferenceBinding) castType).isFinal()) {
374 // no subclass for castType, thus compile-time check is valid
375 ReferenceBinding match = ((ReferenceBinding)castType).findSuperTypeErasingTo((ReferenceBinding)expressionType.erasure());
377 // potential runtime error
378 reportIllegalCast(scope, castType, expressionType);
382 } else { // ----- (castType.isInterface) expressionType.isInterface -------
384 ReferenceBinding match = ((ReferenceBinding)expressionType).findSuperTypeErasingTo((ReferenceBinding)castType.erasure());
386 return checkUnsafeCast(scope, castType, expressionType, match, false);
389 match = ((ReferenceBinding)castType).findSuperTypeErasingTo((ReferenceBinding)expressionType.erasure());
391 tagAsNeedCheckCast();
392 return checkUnsafeCast(scope, castType, expressionType, match, true);
394 MethodBinding[] castTypeMethods = getAllInheritedMethods((ReferenceBinding) castType);
395 MethodBinding[] expressionTypeMethods =
396 getAllInheritedMethods((ReferenceBinding) expressionType);
397 int exprMethodsLength = expressionTypeMethods.length;
398 for (int i = 0, castMethodsLength = castTypeMethods.length; i < castMethodsLength; i++) {
399 for (int j = 0; j < exprMethodsLength; j++) {
400 if ((castTypeMethods[i].returnType != expressionTypeMethods[j].returnType)
401 && (CharOperation.equals(castTypeMethods[i].selector, expressionTypeMethods[j].selector))
402 && castTypeMethods[i].areParametersEqual(expressionTypeMethods[j])) {
403 reportIllegalCast(scope, castType, expressionType);
411 tagAsNeedCheckCast();
415 public FlowInfo checkNullStatus(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int nullStatus) {
417 LocalVariableBinding local = this.localVariableBinding();
421 flowContext.recordUsingNullReference(scope, local, this, FlowInfo.NULL, flowInfo);
422 flowInfo.markAsDefinitelyNull(local); // from thereon it is set
424 case FlowInfo.NON_NULL :
425 flowContext.recordUsingNullReference(scope, local, this, FlowInfo.NON_NULL, flowInfo);
426 flowInfo.markAsDefinitelyNonNull(local); // from thereon it is set
428 case FlowInfo.UNKNOWN :
435 private MethodBinding[] getAllInheritedMethods(ReferenceBinding binding) {
436 ArrayList collector = new ArrayList();
437 getAllInheritedMethods0(binding, collector);
438 return (MethodBinding[]) collector.toArray(new MethodBinding[collector.size()]);
441 private void getAllInheritedMethods0(ReferenceBinding binding, ArrayList collector) {
442 if (!binding.isInterface()) return;
443 MethodBinding[] methodBindings = binding.methods();
444 for (int i = 0, max = methodBindings.length; i < max; i++) {
445 collector.add(methodBindings[i]);
447 ReferenceBinding[] superInterfaces = binding.superInterfaces();
448 for (int i = 0, max = superInterfaces.length; i < max; i++) {
449 getAllInheritedMethods0(superInterfaces[i], collector);
452 public void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) {
453 // do nothing by default - see EqualExpression
456 public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) {
457 if (match == castType) {
458 if (!isNarrowing) tagAsUnnecessaryCast(scope, castType);
461 if (castType.isBoundParameterizedType() || castType.isGenericType()) {
462 if (match.isProvablyDistinctFrom(isNarrowing ? expressionType : castType, 0)) {
463 reportIllegalCast(scope, castType, expressionType);
467 if (!isNarrowing) tagAsUnnecessaryCast(scope, castType);
472 * Base types need that the widening is explicitly done by the compiler using some bytecode like i2f.
473 * Also check unsafe type operations.
475 public void computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType) {
477 if (runtimeTimeType == null || compileTimeType == null)
479 if (this.implicitConversion != 0) return; // already set independantly
481 // it is possible for a Byte to be unboxed to a byte & then converted to an int
482 // but it is not possible for a byte to become Byte & then assigned to an Integer,
483 // or to become an int before boxed into an Integer
484 if (runtimeTimeType != NullBinding && runtimeTimeType.isBaseType()) {
485 if (!compileTimeType.isBaseType()) {
486 compileTimeType = scope.environment().computeBoxingType(compileTimeType);
487 this.implicitConversion = UNBOXING;
490 if (compileTimeType != NullBinding && compileTimeType.isBaseType()) {
491 TypeBinding boxedType = scope.environment().computeBoxingType(runtimeTimeType);
492 if (boxedType == runtimeTimeType) // Object o = 12;
493 boxedType = compileTimeType;
494 this.implicitConversion = BOXING | (boxedType.id << 4) + compileTimeType.id;
499 switch (runtimeTimeType.id) {
503 this.implicitConversion |= (T_int << 4) + compileTimeType.id;
505 case T_JavaLangString :
509 case T_int : //implicitConversion may result in i2i which will result in NO code gen
511 this.implicitConversion |= (runtimeTimeType.id << 4) + compileTimeType.id;
513 default : // regular object ref
514 // if (compileTimeType.isRawType() && runtimeTimeType.isBoundParameterizedType()) {
515 // scope.problemReporter().unsafeRawExpression(this, compileTimeType, runtimeTimeType);
520 * Expression statements are plain expressions, however they generate like
521 * normal expressions with no value required.
523 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
524 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
526 public void generateCode(BlockScope currentScope, CodeStream codeStream) {
528 if ((bits & IsReachableMASK) == 0) {
531 generateCode(currentScope, codeStream, false);
535 * Every expression is responsible for generating its implicit conversion when necessary.
537 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
538 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
539 * @param valueRequired boolean
541 public void generateCode(
542 BlockScope currentScope,
543 CodeStream codeStream,
544 boolean valueRequired) {
546 if (constant != NotAConstant) {
547 // generate a constant expression
548 int pc = codeStream.position;
549 codeStream.generateConstant(constant, implicitConversion);
550 codeStream.recordPositionsFrom(pc, this.sourceStart);
552 // actual non-constant code generation
553 throw new ShouldNotImplement(Util.bind("ast.missingCode")); //$NON-NLS-1$
558 * Default generation of a boolean value
559 * @param currentScope
563 * @param valueRequired
565 public void generateOptimizedBoolean(
566 BlockScope currentScope,
567 CodeStream codeStream,
570 boolean valueRequired) {
572 // a label valued to nil means: by default we fall through the case...
573 // both nil means we leave the value on the stack
575 if ((constant != Constant.NotAConstant) && (constant.typeID() == T_boolean)) {
576 int pc = codeStream.position;
577 if (constant.booleanValue() == true) {
580 if (falseLabel == null) {
581 // implicit falling through the FALSE case
582 if (trueLabel != null) {
583 codeStream.goto_(trueLabel);
589 if (falseLabel != null) {
590 // implicit falling through the TRUE case
591 if (trueLabel == null) {
592 codeStream.goto_(falseLabel);
597 codeStream.recordPositionsFrom(pc, this.sourceStart);
600 generateCode(currentScope, codeStream, valueRequired);
602 int position = codeStream.position;
604 if (falseLabel == null) {
605 if (trueLabel != null) {
606 // Implicit falling through the FALSE case
607 codeStream.ifne(trueLabel);
610 if (trueLabel == null) {
611 // Implicit falling through the TRUE case
612 codeStream.ifeq(falseLabel);
614 // No implicit fall through TRUE/FALSE --> should never occur
618 // reposition the endPC
619 codeStream.updateLastRecordedEndPC(position);
622 /* Optimized (java) code generation for string concatenations that involve StringBuffer
623 * creation: going through this path means that there is no need for a new StringBuffer
624 * creation, further operands should rather be only appended to the current one.
625 * By default: no optimization.
627 public void generateOptimizedStringConcatenation(
628 BlockScope blockScope,
629 CodeStream codeStream,
632 if (typeID == T_JavaLangString && this.constant != NotAConstant && this.constant.stringValue().length() == 0) {
633 return; // optimize str + ""
635 generateCode(blockScope, codeStream, true);
636 codeStream.invokeStringConcatenationAppendForType(typeID);
639 /* Optimized (java) code generation for string concatenations that involve StringBuffer
640 * creation: going through this path means that there is no need for a new StringBuffer
641 * creation, further operands should rather be only appended to the current one.
643 public void generateOptimizedStringConcatenationCreation(
644 BlockScope blockScope,
645 CodeStream codeStream,
648 codeStream.newStringContatenation();
651 case T_JavaLangObject :
653 // in the case the runtime value of valueOf(Object) returns null, we have to use append(Object) instead of directly valueOf(Object)
654 // append(Object) returns append(valueOf(Object)), which means that the null case is handled by the next case.
655 codeStream.invokeStringConcatenationDefaultConstructor();
656 generateCode(blockScope, codeStream, true);
657 codeStream.invokeStringConcatenationAppendForType(T_JavaLangObject);
659 case T_JavaLangString :
661 if (constant != NotAConstant) {
662 String stringValue = constant.stringValue();
663 if (stringValue.length() == 0) { // optimize ""+<str>
664 codeStream.invokeStringConcatenationDefaultConstructor();
667 codeStream.ldc(stringValue);
669 // null case is not a constant
670 generateCode(blockScope, codeStream, true);
671 codeStream.invokeStringValueOf(T_JavaLangObject);
675 generateCode(blockScope, codeStream, true);
676 codeStream.invokeStringValueOf(typeID);
678 codeStream.invokeStringConcatenationStringConstructor();
681 public boolean isCompactableOperation() {
686 //Return true if the conversion is done AUTOMATICALLY by the vm
687 //while the javaVM is an int based-machine, thus for example pushing
688 //a byte onto the stack , will automatically create an int on the stack
689 //(this request some work d be done by the VM on signed numbers)
690 public boolean isConstantValueOfTypeAssignableToType(TypeBinding constantType, TypeBinding targetType) {
692 if (constant == Constant.NotAConstant)
694 if (constantType == targetType)
696 if (constantType.isBaseType() && targetType.isBaseType()) {
697 //No free assignment conversion from anything but to integral ones.
698 if ((constantType == IntBinding
699 || BaseTypeBinding.isWidening(T_int, constantType.id))
700 && (BaseTypeBinding.isNarrowing(targetType.id, T_int))) {
701 //use current explicit conversion in order to get some new value to compare with current one
702 return isConstantValueRepresentable(constant, constantType.id, targetType.id);
708 public boolean isTypeReference() {
712 public int nullStatus(FlowInfo flowInfo) {
714 if (this.constant != null && this.constant != NotAConstant)
715 return FlowInfo.NON_NULL; // constant expression cannot be null
717 LocalVariableBinding local = localVariableBinding();
719 if (flowInfo.isDefinitelyNull(local))
720 return FlowInfo.NULL;
721 if (flowInfo.isDefinitelyNonNull(local))
722 return FlowInfo.NON_NULL;
723 return FlowInfo.UNKNOWN;
725 return FlowInfo.NON_NULL;
729 * Constant usable for bytecode pattern optimizations, but cannot be inlined
730 * since it is not strictly equivalent to the definition of constant expressions.
731 * In particular, some side-effects may be required to occur (only the end value
733 * @return Constant known to be of boolean type
735 public Constant optimizedBooleanConstant() {
736 return this.constant;
739 public StringBuffer print(int indent, StringBuffer output) {
740 printIndent(indent, output);
741 return printExpression(indent, output);
744 public abstract StringBuffer printExpression(int indent, StringBuffer output);
746 public StringBuffer printStatement(int indent, StringBuffer output) {
747 return print(indent, output).append(";"); //$NON-NLS-1$
750 public void reportIllegalCast(Scope scope, TypeBinding castType, TypeBinding expressionType) {
751 // do nothing by default
754 public void resolve(BlockScope scope) {
755 // drops the returning expression's type whatever the type is.
757 this.resolveType(scope);
761 public TypeBinding resolveType(BlockScope scope) {
762 // by default... subclasses should implement a better TC if required.
767 public TypeBinding resolveType(ClassScope classScope) {
768 // by default... subclasses should implement a better TB if required.
772 public TypeBinding resolveTypeExpecting(
774 TypeBinding expectedType) {
776 this.setExpectedType(expectedType); // needed in case of generic method invocation
777 TypeBinding expressionType = this.resolveType(scope);
778 if (expressionType == null) return null;
779 if (expressionType == expectedType) return expressionType;
781 if (!expressionType.isCompatibleWith(expectedType)) {
782 if (scope.isBoxingCompatibleWith(expressionType, expectedType)) {
783 this.computeConversion(scope, expectedType, expressionType);
785 scope.problemReporter().typeMismatchError(expressionType, expectedType, this);
789 return expressionType;
793 * Record the type expectation before this expression is typechecked.
794 * e.g. String s = foo();, foo() will be tagged as being expected of type String
795 * Used to trigger proper inference of generic method invocations.
797 public void setExpectedType(TypeBinding expectedType) {
798 // do nothing by default
801 public void tagAsUnnecessaryCast(Scope scope, TypeBinding castType) {
802 // do nothing by default
805 public void tagAsNeedCheckCast() {
806 // do nothing by default
809 public Expression toTypeReference() {
810 //by default undefined
812 //this method is meanly used by the parser in order to transform
813 //an expression that is used as a type reference in a cast ....
814 //--appreciate the fact that castExpression and ExpressionWithParenthesis
815 //--starts with the same pattern.....
820 public void traverse(ASTVisitor visitor, BlockScope scope) {
821 // do nothing by default
823 public void traverse(ASTVisitor visitor, ClassScope scope) {
824 // do nothing by default
826 public void traverse(ASTVisitor visitor, CompilationUnitScope scope) {
827 // do nothing by default
830 * Returns the local variable referenced by this node. Can be a direct reference (SingleNameReference)
831 * or thru a cast expression etc...
833 public LocalVariableBinding localVariableBinding() {