001    /* Copyright (c) 2005-2008, Torbjorn Ekman
002     * All rights reserved.
003     *
004     * Redistribution and use in source and binary forms, with or without
005     * modification, are permitted provided that the following conditions are met:
006     *
007     * 1. Redistributions of source code must retain the above copyright notice,
008     * this list of conditions and the following disclaimer.
009     *
010     * 2. Redistributions in binary form must reproduce the above copyright notice,
011     * this list of conditions and the following disclaimer in the documentation
012     * and/or other materials provided with the distribution.
013     *
014     * 3. Neither the name of the copyright holder nor the names of its
015     * contributors may be used to endorse or promote products derived from this
016     * software without specific prior written permission.
017     *
018     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
022     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028     * POSSIBILITY OF SUCH DAMAGE.
029     */
030    
031    aspect AutoBoxing {
032    
033    
034      /* NumericTypes, BooleanTypes
035         TypeChecking (ensure that an expression of a certain type is valid in a particular context)
036         TypeComputation (compute the type of an expression)
037         CodeGeneration (output code including implicit type conversions and promotions)
038    
039         NumericTypes:
040           binaryNumericPromotion, unaryNumericPromotion, assignmentConversion, methodInvocationConversion, castingConversion
041           numeric operations that do not use these kinds of conversions and promotions explicitly need to be refined
042         BooleanTypes:
043           assignmentConversion, methodInvocationConversion, castingConversion
044    
045      */
046    
047    
048    
049    
050      // 5.1.7 Boxing Conversion
051    
052      syn boolean TypeDecl.boxingConversionTo(TypeDecl typeDecl) = false;
053      eq PrimitiveType.boxingConversionTo(TypeDecl typeDecl) = boxed() == typeDecl;
054    
055      // Mapping between Primitive type and corresponding boxed Reference type
056      syn lazy TypeDecl TypeDecl.boxed() = unknownType();
057      eq BooleanType.boxed() = lookupType("java.lang", "Boolean");
058      eq ByteType.boxed() = lookupType("java.lang", "Byte");
059      eq CharType.boxed() = lookupType("java.lang", "Character");
060      eq ShortType.boxed() = lookupType("java.lang", "Short");
061      eq IntType.boxed() = lookupType("java.lang", "Integer");
062      eq LongType.boxed() = lookupType("java.lang", "Long");
063      eq FloatType.boxed() = lookupType("java.lang", "Float");
064      eq DoubleType.boxed() = lookupType("java.lang", "Double");
065    
066      // 5.1.8 Unboxing Conversion
067    
068      syn boolean TypeDecl.unboxingConversionTo(TypeDecl typeDecl) = false;
069      eq ReferenceType.unboxingConversionTo(TypeDecl typeDecl) = unboxed() == typeDecl;
070    
071      // Mapping between Reference type and corresponding unboxed Primitive type
072      syn lazy TypeDecl TypeDecl.unboxed() = unknownType();
073      eq ReferenceType.unboxed() {
074        if (packageName().equals("java.lang") && isTopLevelType()) {
075          String n = name();
076          if (n.equals("Boolean")) {
077            return typeBoolean();
078          }
079          if (n.equals("Byte")) {
080            return typeByte();
081          }
082          if (n.equals("Character")) {
083            return typeChar();
084          }
085          if (n.equals("Short")) {
086            return typeShort();
087          }
088          if (n.equals("Integer")) {
089            return typeInt();
090          }
091          if (n.equals("Long")) {
092            return typeLong();
093          }
094          if (n.equals("Float")) {
095            return typeFloat();
096          }
097          if (n.equals("Double")) {
098            return typeDouble();
099          }
100        }
101        return unknownType();
102      }
103      inh TypeDecl ReferenceType.typeBoolean();
104      inh TypeDecl ReferenceType.typeByte();
105      inh TypeDecl ReferenceType.typeChar();
106      inh TypeDecl ReferenceType.typeShort();
107      inh TypeDecl ReferenceType.typeInt();
108      inh TypeDecl ReferenceType.typeLong();
109      inh TypeDecl ReferenceType.typeFloat();
110      inh TypeDecl ReferenceType.typeDouble();
111    
112      /**
113       * Unboxing a type variable is possible if it has an unboxable type as type
114       * bound.
115       */
116      eq TypeVariable.unboxed() {
117        for (Access bound: getTypeBoundList()) {
118          TypeDecl unboxed = bound.type().unboxed();
119          if (!unboxed.isUnknown()) {
120            return unboxed;
121          }
122        }
123        return unknownType();
124      }
125    
126    
127      // 5.2 Assignment Conversion
128      refine TypeConversion eq TypeDecl.assignConversionTo(TypeDecl type, Expr expr) {
129        if (refined(type, expr)) {
130          return true;
131        }
132        boolean canBoxThis = this instanceof PrimitiveType;
133        boolean canBoxType = type instanceof PrimitiveType;
134        boolean canUnboxThis = !unboxed().isUnknown();
135        boolean canUnboxType = !type.unboxed().isUnknown();
136        TypeDecl t = !canUnboxThis && canUnboxType ? type.unboxed() : type;
137        boolean sourceIsConstant = expr != null ? expr.isConstant() : false;
138        if (sourceIsConstant && (isInt() || isChar() || isShort() || isByte()) &&
139            (t.isByte() || t.isShort() || t.isChar()) &&
140            narrowingConversionTo(t) && expr.representableIn(t))
141          return true;
142        if (canBoxThis && !canBoxType && boxed().wideningConversionTo(type)) {
143          return true;
144        } else if (canUnboxThis && !canUnboxType && unboxed().wideningConversionTo(type)) {
145          return true;
146        }
147    
148        return false;
149      }
150    
151      // 5.3 Method Invocation Conversion
152      refine TypeConversion eq TypeDecl.methodInvocationConversionTo(TypeDecl type) {
153        if (refined(type)) {
154          return true;
155        }
156        boolean canBoxThis = this instanceof PrimitiveType;
157        boolean canBoxType = type instanceof PrimitiveType;
158        boolean canUnboxThis = !unboxed().isUnknown();
159        boolean canUnboxType = !type.unboxed().isUnknown();
160        if (canBoxThis && !canBoxType) {
161          return boxed().wideningConversionTo(type);
162        } else if (canUnboxThis && !canUnboxType) {
163          return unboxed().wideningConversionTo(type);
164        }
165        return false;
166      }
167    
168      // 5.5 Casting Conversion
169      refine TypeConversion eq TypeDecl.castingConversionTo(TypeDecl type) {
170        if (refined(type)) {
171          return true;
172        }
173        boolean canBoxThis = this instanceof PrimitiveType;
174        boolean canBoxType = type instanceof PrimitiveType;
175        boolean canUnboxThis = !unboxed().isUnknown();
176        boolean canUnboxType = !type.unboxed().isUnknown();
177        if (canBoxThis && !canBoxType) {
178          return boxed().wideningConversionTo(type);
179        } else if (canUnboxThis && !canUnboxType) {
180          return unboxed().wideningConversionTo(type);
181        }
182        return false;
183        /*
184        else if (boxingConversionTo(type)) {
185          return true;
186        } else if (unboxingConversionTo(type)) {
187          return true;
188        }
189        return false;
190        */
191      }
192    
193      refine Generics eq ClassDecl.castingConversionTo(TypeDecl type) {
194        if (refined(type)) {
195          return true;
196        }
197        boolean canUnboxThis = !unboxed().isUnknown();
198        boolean canUnboxType = !type.unboxed().isUnknown();
199        if (canUnboxThis && !canUnboxType) {
200          return unboxed().wideningConversionTo(type);
201        }
202        return false;
203        /*
204        else if (unboxingConversionTo(type)) {
205          return true;
206        }
207        return false;
208        */
209      }
210      refine Generics eq InterfaceDecl.castingConversionTo(TypeDecl type) {
211        if (refined(type)) {
212          return true;
213        }
214        boolean canUnboxThis = !unboxed().isUnknown();
215        boolean canUnboxType = !type.unboxed().isUnknown();
216        if (canUnboxThis && !canUnboxType) {
217          return unboxed().wideningConversionTo(type);
218        }
219        return false;
220        /*
221        else if (unboxingConversionTo(type)) {
222          return true;
223        }
224        return false;
225        */
226      }
227      //eq PrimitiveType.castingConversionTo(TypeDecl type) =
228      //  type.isReferenceType() ? boxed().castingConversionTo(type) : super.castingConversionTo(type);
229    
230      //refine Generics eq ClassDecl.castingConversionTo(TypeDecl type) {
231      //  return type.isPrimitiveType() && !unboxed().isUnknown() ? unboxed().castingConversionTo(type) : refined(type);
232      //}
233    
234      // 5.6.1 Unary Numeric Promotion
235      eq ReferenceType.unaryNumericPromotion() = isNumericType() && !isUnknown() ? unboxed().unaryNumericPromotion() : this;
236      eq UnknownType.unaryNumericPromotion() = this;
237    
238      // 5.6.2 Binary Numeric Promotion
239      eq ReferenceType.binaryNumericPromotion(TypeDecl type) = unboxed().binaryNumericPromotion(type);
240      refine NumericPromotion eq NumericType.binaryNumericPromotion(TypeDecl type) {
241        if (type.isReferenceType()) {
242          return refined(type.unboxed());
243        } else {
244          return refined(type);
245        }
246      }
247    
248      refine TypeAnalysis eq ConditionalExpr.type() {
249        TypeDecl trueType = getTrueExpr().type();
250        TypeDecl falseType = getFalseExpr().type();
251        if (trueType.isBoolean() && falseType.isBoolean()) {
252          if (trueType == falseType) {
253            return trueType;
254          }
255          if (trueType.isReferenceType()) {
256            return trueType.unboxed();
257          }
258          return trueType;
259        }
260        return refined();
261      }
262    
263      eq UnknownType.binaryNumericPromotion(TypeDecl type) = this;
264    
265      eq ReferenceType.isNumericType() =
266          !unboxed().isUnknown() && unboxed().isNumericType();
267    
268      eq ReferenceType.isIntegralType() =
269          !unboxed().isUnknown() && unboxed().isIntegralType();
270    
271      eq ReferenceType.isPrimitive() =
272          !unboxed().isUnknown() && unboxed().isPrimitive();
273    
274      refine ConstantExpression eq Binary.binaryNumericPromotedType() {
275        TypeDecl leftType = left().type();
276        TypeDecl rightType = right().type();
277        if (leftType.isBoolean() && rightType.isBoolean()) {
278          return leftType.isReferenceType() ? leftType.unboxed() : leftType;
279        }
280        return refined();
281      }
282    
283      // Affects type checking of 14.9 If, 14.10 Assert, 14.12 While, 14.13 Do, 14.14 For
284      eq ReferenceType.isBoolean() = fullName().equals("java.lang.Boolean") && unboxed().isBoolean();
285      // Code generation need to add unboxing if the conditional is a Boxed value
286      // this is done by inserting a new node with an explicit cast
287    
288    
289    
290      // 15.15.6 Logical Complement Operator !
291      // type()
292    
293      // 15.21.2 Boolean Equality Operators == and !=
294      // If the operands of an equality operator are both of type boolean, or if
295      // one operand is of type boolean and the other is of type Boolean, then
296      // the operation is boolean equality. The boolean equality operators are
297      // associative. If one of the operands is of type Boolean it is subjected
298      // to unboxing conversion (�5.1.8)
299    
300      // 15.22.2 Boolean Logical Operators &, ^, and |
301      // emitCastTo(), type()
302    
303      // When both operands of a &, ^, or | operator are of type boolean or
304      // Boolean, then the type of the bitwise operator expression is boolean.
305      // In all cases, the operands are subject to unboxing conversion (�5.1.8)
306      // as necessary.
307    
308      // 15.23 Conditional-And Operator &&
309      // Each operand of && must be of type boolean or Boolean, or a compile-time error occurs.
310      // At run time, the left-hand operand expression is evaluated first; if
311      // the result has type Boolean, it is subjected to unboxing conversion
312      // (�5.1.8);
313    
314      // 15.24 Conditional-Or Operator ||
315      // See 15.23
316    
317      // 15.25 Conditional Operator ? :
318      // See Spec
319    
320    
321    }