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 }