| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of js_backend; | |
| 6 | |
| 7 const JAVA_SCRIPT_CONSTANT_SYSTEM = const JavaScriptConstantSystem(); | |
| 8 | |
| 9 class JavaScriptBitNotOperation extends BitNotOperation { | |
| 10 const JavaScriptBitNotOperation(); | |
| 11 | |
| 12 ConstantValue fold(ConstantValue constant) { | |
| 13 if (JAVA_SCRIPT_CONSTANT_SYSTEM.isInt(constant)) { | |
| 14 // In JavaScript we don't check for -0 and treat it as if it was zero. | |
| 15 if (constant.isMinusZero) constant = DART_CONSTANT_SYSTEM.createInt(0); | |
| 16 IntConstantValue intConstant = constant; | |
| 17 // We convert the result of bit-operations to 32 bit unsigned integers. | |
| 18 return | |
| 19 JAVA_SCRIPT_CONSTANT_SYSTEM.createInt32(~intConstant.primitiveValue); | |
| 20 } | |
| 21 return null; | |
| 22 } | |
| 23 } | |
| 24 | |
| 25 /** | |
| 26 * In JavaScript we truncate the result to an unsigned 32 bit integer. Also, -0 | |
| 27 * is treated as if it was the integer 0. | |
| 28 */ | |
| 29 class JavaScriptBinaryBitOperation implements BinaryOperation { | |
| 30 final BinaryBitOperation dartBitOperation; | |
| 31 | |
| 32 const JavaScriptBinaryBitOperation(this.dartBitOperation); | |
| 33 | |
| 34 String get name => dartBitOperation.name; | |
| 35 | |
| 36 ConstantValue fold(ConstantValue left, ConstantValue right) { | |
| 37 // In JavaScript we don't check for -0 and treat it as if it was zero. | |
| 38 if (left.isMinusZero) left = DART_CONSTANT_SYSTEM.createInt(0); | |
| 39 if (right.isMinusZero) right = DART_CONSTANT_SYSTEM.createInt(0); | |
| 40 IntConstantValue result = dartBitOperation.fold(left, right); | |
| 41 if (result != null) { | |
| 42 // We convert the result of bit-operations to 32 bit unsigned integers. | |
| 43 return JAVA_SCRIPT_CONSTANT_SYSTEM.createInt32(result.primitiveValue); | |
| 44 } | |
| 45 return result; | |
| 46 } | |
| 47 | |
| 48 apply(left, right) => dartBitOperation.apply(left, right); | |
| 49 } | |
| 50 | |
| 51 class JavaScriptShiftRightOperation extends JavaScriptBinaryBitOperation { | |
| 52 const JavaScriptShiftRightOperation() : super(const ShiftRightOperation()); | |
| 53 | |
| 54 ConstantValue fold(ConstantValue left, ConstantValue right) { | |
| 55 // Truncate the input value to 32 bits if necessary. | |
| 56 if (left.isInt) { | |
| 57 IntConstantValue intConstant = left; | |
| 58 int value = intConstant.primitiveValue; | |
| 59 int truncatedValue = value & JAVA_SCRIPT_CONSTANT_SYSTEM.BITS32; | |
| 60 if (value < 0) { | |
| 61 // Sign-extend if the input was negative. The current semantics don't | |
| 62 // make much sense, since we only look at bit 31. | |
| 63 // TODO(floitsch): we should treat the input to right shifts as | |
| 64 // unsigned. | |
| 65 | |
| 66 // A 32 bit complement-two value x can be computed by: | |
| 67 // x_u - 2^32 (where x_u is its unsigned representation). | |
| 68 // Example: 0xFFFFFFFF - 0x100000000 => -1. | |
| 69 // We simply and with the sign-bit and multiply by two. If the sign-bit | |
| 70 // was set, then the result is 0. Otherwise it will become 2^32. | |
| 71 final int SIGN_BIT = 0x80000000; | |
| 72 truncatedValue -= 2 * (truncatedValue & SIGN_BIT); | |
| 73 } | |
| 74 if (value != truncatedValue) { | |
| 75 left = DART_CONSTANT_SYSTEM.createInt(truncatedValue); | |
| 76 } | |
| 77 } | |
| 78 return super.fold(left, right); | |
| 79 } | |
| 80 } | |
| 81 | |
| 82 class JavaScriptNegateOperation implements UnaryOperation { | |
| 83 final NegateOperation dartNegateOperation = const NegateOperation(); | |
| 84 | |
| 85 const JavaScriptNegateOperation(); | |
| 86 | |
| 87 String get name => dartNegateOperation.name; | |
| 88 | |
| 89 ConstantValue fold(ConstantValue constant) { | |
| 90 if (constant.isInt) { | |
| 91 IntConstantValue intConstant = constant; | |
| 92 if (intConstant.primitiveValue == 0) { | |
| 93 return JAVA_SCRIPT_CONSTANT_SYSTEM.createDouble(-0.0); | |
| 94 } | |
| 95 } | |
| 96 return dartNegateOperation.fold(constant); | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 class JavaScriptBinaryArithmeticOperation implements BinaryOperation { | |
| 101 final BinaryOperation dartArithmeticOperation; | |
| 102 | |
| 103 const JavaScriptBinaryArithmeticOperation(this.dartArithmeticOperation); | |
| 104 | |
| 105 String get name => dartArithmeticOperation.name; | |
| 106 | |
| 107 ConstantValue fold(ConstantValue left, ConstantValue right) { | |
| 108 ConstantValue result = dartArithmeticOperation.fold(left, right); | |
| 109 if (result == null) return result; | |
| 110 return JAVA_SCRIPT_CONSTANT_SYSTEM.convertToJavaScriptConstant(result); | |
| 111 } | |
| 112 | |
| 113 apply(left, right) => dartArithmeticOperation.apply(left, right); | |
| 114 } | |
| 115 | |
| 116 class JavaScriptIdentityOperation implements BinaryOperation { | |
| 117 final IdentityOperation dartIdentityOperation = const IdentityOperation(); | |
| 118 | |
| 119 const JavaScriptIdentityOperation(); | |
| 120 | |
| 121 String get name => dartIdentityOperation.name; | |
| 122 | |
| 123 BoolConstantValue fold(ConstantValue left, ConstantValue right) { | |
| 124 BoolConstantValue result = dartIdentityOperation.fold(left, right); | |
| 125 if (result == null || result.primitiveValue) return result; | |
| 126 // In JavaScript -0.0 === 0 and all doubles are equal to their integer | |
| 127 // values. Furthermore NaN !== NaN. | |
| 128 if (left.isNum && right.isNum) { | |
| 129 NumConstantValue leftNum = left; | |
| 130 NumConstantValue rightNum = right; | |
| 131 double leftDouble = leftNum.primitiveValue.toDouble(); | |
| 132 double rightDouble = rightNum.primitiveValue.toDouble(); | |
| 133 return new BoolConstantValue(leftDouble == rightDouble); | |
| 134 } | |
| 135 return result; | |
| 136 } | |
| 137 | |
| 138 apply(left, right) => identical(left, right); | |
| 139 } | |
| 140 | |
| 141 /** | |
| 142 * Constant system following the semantics for Dart code that has been | |
| 143 * compiled to JavaScript. | |
| 144 */ | |
| 145 class JavaScriptConstantSystem extends ConstantSystem { | |
| 146 final int BITS31 = 0x8FFFFFFF; | |
| 147 final int BITS32 = 0xFFFFFFFF; | |
| 148 | |
| 149 final add = const JavaScriptBinaryArithmeticOperation(const AddOperation()); | |
| 150 final bitAnd = const JavaScriptBinaryBitOperation(const BitAndOperation()); | |
| 151 final bitNot = const JavaScriptBitNotOperation(); | |
| 152 final bitOr = const JavaScriptBinaryBitOperation(const BitOrOperation()); | |
| 153 final bitXor = const JavaScriptBinaryBitOperation(const BitXorOperation()); | |
| 154 final booleanAnd = const BooleanAndOperation(); | |
| 155 final booleanOr = const BooleanOrOperation(); | |
| 156 final divide = | |
| 157 const JavaScriptBinaryArithmeticOperation(const DivideOperation()); | |
| 158 final equal = const EqualsOperation(); | |
| 159 final greaterEqual = const GreaterEqualOperation(); | |
| 160 final greater = const GreaterOperation(); | |
| 161 final identity = const JavaScriptIdentityOperation(); | |
| 162 final lessEqual = const LessEqualOperation(); | |
| 163 final less = const LessOperation(); | |
| 164 final modulo = | |
| 165 const JavaScriptBinaryArithmeticOperation(const ModuloOperation()); | |
| 166 final multiply = | |
| 167 const JavaScriptBinaryArithmeticOperation(const MultiplyOperation()); | |
| 168 final negate = const JavaScriptNegateOperation(); | |
| 169 final not = const NotOperation(); | |
| 170 final shiftLeft = | |
| 171 const JavaScriptBinaryBitOperation(const ShiftLeftOperation()); | |
| 172 final shiftRight = const JavaScriptShiftRightOperation(); | |
| 173 final subtract = | |
| 174 const JavaScriptBinaryArithmeticOperation(const SubtractOperation()); | |
| 175 final truncatingDivide = const JavaScriptBinaryArithmeticOperation( | |
| 176 const TruncatingDivideOperation()); | |
| 177 final codeUnitAt = const CodeUnitAtRuntimeOperation(); | |
| 178 | |
| 179 const JavaScriptConstantSystem(); | |
| 180 | |
| 181 /** | |
| 182 * Returns true if [value] will turn into NaN or infinity | |
| 183 * at runtime. | |
| 184 */ | |
| 185 bool integerBecomesNanOrInfinity(int value) { | |
| 186 double doubleValue = value.toDouble(); | |
| 187 return doubleValue.isNaN || doubleValue.isInfinite; | |
| 188 } | |
| 189 | |
| 190 NumConstantValue convertToJavaScriptConstant(NumConstantValue constant) { | |
| 191 if (constant.isInt) { | |
| 192 IntConstantValue intConstant = constant; | |
| 193 int intValue = intConstant.primitiveValue; | |
| 194 if (integerBecomesNanOrInfinity(intValue)) { | |
| 195 return new DoubleConstantValue(intValue.toDouble()); | |
| 196 } | |
| 197 // If the integer loses precision with JavaScript numbers, use | |
| 198 // the floored version JavaScript will use. | |
| 199 int floorValue = intValue.toDouble().floor().toInt(); | |
| 200 if (floorValue != intValue) { | |
| 201 return new IntConstantValue(floorValue); | |
| 202 } | |
| 203 } else if (constant.isDouble) { | |
| 204 DoubleConstantValue doubleResult = constant; | |
| 205 double doubleValue = doubleResult.primitiveValue; | |
| 206 if (!doubleValue.isInfinite && !doubleValue.isNaN && | |
| 207 !constant.isMinusZero) { | |
| 208 int intValue = doubleValue.truncate(); | |
| 209 if (intValue == doubleValue) { | |
| 210 return new IntConstantValue(intValue); | |
| 211 } | |
| 212 } | |
| 213 } | |
| 214 return constant; | |
| 215 } | |
| 216 | |
| 217 NumConstantValue createInt(int i) | |
| 218 => convertToJavaScriptConstant(new IntConstantValue(i)); | |
| 219 NumConstantValue createInt32(int i) => new IntConstantValue(i & BITS32); | |
| 220 NumConstantValue createDouble(double d) | |
| 221 => convertToJavaScriptConstant(new DoubleConstantValue(d)); | |
| 222 StringConstantValue createString(DartString string) { | |
| 223 return new StringConstantValue(string); | |
| 224 } | |
| 225 BoolConstantValue createBool(bool value) => new BoolConstantValue(value); | |
| 226 NullConstantValue createNull() => new NullConstantValue(); | |
| 227 | |
| 228 // Integer checks don't verify that the number is not -0.0. | |
| 229 bool isInt(ConstantValue constant) => constant.isInt || constant.isMinusZero; | |
| 230 bool isDouble(ConstantValue constant) | |
| 231 => constant.isDouble && !constant.isMinusZero; | |
| 232 bool isString(ConstantValue constant) => constant.isString; | |
| 233 bool isBool(ConstantValue constant) => constant.isBool; | |
| 234 bool isNull(ConstantValue constant) => constant.isNull; | |
| 235 | |
| 236 bool isSubtype(Compiler compiler, DartType s, DartType t) { | |
| 237 // At runtime, an integer is both an integer and a double: the | |
| 238 // integer type check is Math.floor, which will return true only | |
| 239 // for real integers, and our double type check is 'typeof number' | |
| 240 // which will return true for both integers and doubles. | |
| 241 if (s.element == compiler.intClass && t.element == compiler.doubleClass) { | |
| 242 return true; | |
| 243 } | |
| 244 return compiler.types.isSubtype(s, t); | |
| 245 } | |
| 246 | |
| 247 MapConstantValue createMap(Compiler compiler, | |
| 248 InterfaceType sourceType, | |
| 249 List<ConstantValue> keys, | |
| 250 List<ConstantValue> values) { | |
| 251 JavaScriptBackend backend = compiler.backend; | |
| 252 | |
| 253 bool onlyStringKeys = true; | |
| 254 ConstantValue protoValue = null; | |
| 255 for (int i = 0; i < keys.length ; i++) { | |
| 256 var key = keys[i]; | |
| 257 if (key.isString) { | |
| 258 if (key.primitiveValue == JavaScriptMapConstant.PROTO_PROPERTY) { | |
| 259 protoValue = values[i]; | |
| 260 } | |
| 261 } else { | |
| 262 onlyStringKeys = false; | |
| 263 // Don't handle __proto__ values specially in the general map case. | |
| 264 protoValue = null; | |
| 265 break; | |
| 266 } | |
| 267 } | |
| 268 | |
| 269 bool hasProtoKey = (protoValue != null); | |
| 270 DartType keysType; | |
| 271 if (sourceType.treatAsRaw) { | |
| 272 keysType = compiler.listClass.rawType; | |
| 273 } else { | |
| 274 List<DartType> arguments = <DartType>[sourceType.typeArguments.first]; | |
| 275 keysType = new InterfaceType(compiler.listClass, arguments); | |
| 276 } | |
| 277 ListConstantValue keysList = new ListConstantValue(keysType, keys); | |
| 278 String className = onlyStringKeys | |
| 279 ? (hasProtoKey ? JavaScriptMapConstant.DART_PROTO_CLASS | |
| 280 : JavaScriptMapConstant.DART_STRING_CLASS) | |
| 281 : JavaScriptMapConstant.DART_GENERAL_CLASS; | |
| 282 ClassElement classElement = backend.jsHelperLibrary.find(className); | |
| 283 classElement.ensureResolved(compiler); | |
| 284 List<DartType> typeArgument = sourceType.typeArguments; | |
| 285 InterfaceType type; | |
| 286 if (sourceType.treatAsRaw) { | |
| 287 type = classElement.rawType; | |
| 288 } else { | |
| 289 type = new InterfaceType(classElement, typeArgument); | |
| 290 } | |
| 291 return new JavaScriptMapConstant( | |
| 292 type, keysList, values, protoValue, onlyStringKeys); | |
| 293 | |
| 294 } | |
| 295 } | |
| 296 | |
| 297 class JavaScriptMapConstant extends MapConstantValue { | |
| 298 /** | |
| 299 * The [PROTO_PROPERTY] must not be used as normal property in any JavaScript | |
| 300 * object. It would change the prototype chain. | |
| 301 */ | |
| 302 static const LiteralDartString PROTO_PROPERTY = | |
| 303 const LiteralDartString("__proto__"); | |
| 304 | |
| 305 /** The dart class implementing constant map literals. */ | |
| 306 static const String DART_CLASS = "ConstantMap"; | |
| 307 static const String DART_STRING_CLASS = "ConstantStringMap"; | |
| 308 static const String DART_PROTO_CLASS = "ConstantProtoMap"; | |
| 309 static const String DART_GENERAL_CLASS = "GeneralConstantMap"; | |
| 310 static const String LENGTH_NAME = "length"; | |
| 311 static const String JS_OBJECT_NAME = "_jsObject"; | |
| 312 static const String KEYS_NAME = "_keys"; | |
| 313 static const String PROTO_VALUE = "_protoValue"; | |
| 314 static const String JS_DATA_NAME = "_jsData"; | |
| 315 | |
| 316 final ListConstantValue keyList; | |
| 317 final ConstantValue protoValue; | |
| 318 final bool onlyStringKeys; | |
| 319 | |
| 320 JavaScriptMapConstant(InterfaceType type, | |
| 321 ListConstantValue keyList, | |
| 322 List<ConstantValue> values, | |
| 323 this.protoValue, | |
| 324 this.onlyStringKeys) | |
| 325 : this.keyList = keyList, | |
| 326 super(type, keyList.entries, values); | |
| 327 bool get isMap => true; | |
| 328 | |
| 329 TypeMask computeMask(Compiler compiler) { | |
| 330 return compiler.typesTask.constMapType; | |
| 331 } | |
| 332 | |
| 333 List<ConstantValue> getDependencies() { | |
| 334 List<ConstantValue> result = <ConstantValue>[]; | |
| 335 if (onlyStringKeys) { | |
| 336 result.add(keyList); | |
| 337 } else { | |
| 338 // Add the keys individually to avoid generating an unused list constant | |
| 339 // for the keys. | |
| 340 result.addAll(keys); | |
| 341 } | |
| 342 result.addAll(values); | |
| 343 return result; | |
| 344 } | |
| 345 } | |
| OLD | NEW |