| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // TODO(floitsch): finish implementation. | |
| 6 class Constant implements Hashable { | 5 class Constant implements Hashable { |
| 7 // TODO(floitsch): remove the direct access to the string. | 6 const Constant(); |
| 8 final String jsCode; | 7 |
| 9 Constant(this.jsCode); | 8 bool isNull() => false; |
| 10 | 9 /** [isInt] implies [isNum]. */ |
| 11 int hashCode() => jsCode.hashCode(); | 10 bool isInt() => false; |
| 12 bool operator ==(var other) { | 11 /** [isDouble] implies [isNum]. */ |
| 13 if (other is !Constant) return false; | 12 bool isDouble() => false; |
| 14 Constant otherConstant = other; | 13 bool isBool() => false; |
| 15 return jsCode == otherConstant.jsCode; | 14 bool isString() => false; |
| 16 } | 15 /** [isList] implies [isObject]. */ |
| 16 bool isList() => false; |
| 17 /** [isMap] implies [isObject]. */ |
| 18 bool isMap() => false; |
| 19 bool isConstructedObject() => false; |
| 20 |
| 21 bool isNum() => isInt() || isDouble(); |
| 22 bool isObject() => isList() || isMap() || isConstructedObject(); |
| 23 |
| 24 /** |
| 25 * Returns [:null:] if the operation is not supported on this constant. |
| 26 * The [op] operator is assumed to be a prefix operator. |
| 27 */ |
| 28 Constant unaryFold(String op) => null; |
| 29 |
| 30 /** |
| 31 * Returns [:null:] if the operation is not supported on this constant, or |
| 32 * if the operation would have thrown an exception. |
| 33 */ |
| 34 Constant binaryFold(String op, Constant other) { |
| 35 if (op == "==" || op == "===") { |
| 36 return new BoolConstant(this == other); |
| 37 } else if (op == "!=" || op == "!==") { |
| 38 return new BoolConstant(this != other); |
| 39 } |
| 40 } |
| 41 |
| 42 abstract void writeJsCode(StringBuffer buffer, |
| 43 CompileTimeConstantHandler handler); |
| 44 } |
| 45 |
| 46 class PrimitiveConstant extends Constant { |
| 47 // TODO(floitsch): this should be an abstract getter, but there is a bug in |
| 48 // the VM. |
| 49 get value() => null; |
| 50 const PrimitiveConstant(); |
| 51 |
| 52 bool operator ==(var other) { |
| 53 if (other is !PrimitiveConstant) return false; |
| 54 PrimitiveConstant otherPrimitive = other; |
| 55 // We use == instead of === so that DartStrings compare correctly. |
| 56 return value == otherPrimitive.value; |
| 57 } |
| 58 } |
| 59 |
| 60 class NullConstant extends PrimitiveConstant { |
| 61 const NullConstant(); |
| 62 bool isNull() => true; |
| 63 get value() => null; |
| 64 |
| 65 void writeJsCode(StringBuffer buffer, CompileTimeConstantHandler handler) { |
| 66 buffer.add("(void 0)"); |
| 67 } |
| 68 |
| 69 // The magic constant has no meaning. It is just a random value. |
| 70 int hashCode() => 785965825; |
| 71 } |
| 72 |
| 73 class IntConstant extends PrimitiveConstant { |
| 74 final int value; |
| 75 // TODO(floitsch): cache the most common integer values. |
| 76 const IntConstant(this.value); |
| 77 bool isInt() => true; |
| 78 |
| 79 void writeJsCode(StringBuffer buffer, CompileTimeConstantHandler handler) { |
| 80 buffer.add("($value)"); |
| 81 } |
| 82 |
| 83 IntConstant unaryFold(String op) { |
| 84 if (op == "-") return new IntConstant(-value); |
| 85 if (op == "~") return new IntConstant(~value); |
| 86 return null; |
| 87 } |
| 88 |
| 89 Constant binaryFold(String op, Constant other) { |
| 90 if (other.isNum()) { |
| 91 PrimitiveConstant otherPrimitive = other; |
| 92 num rightNum = otherPrimitive.value; |
| 93 switch (op) { |
| 94 case "<": return new BoolConstant(value < rightNum); |
| 95 case "<=": return new BoolConstant(value <= rightNum); |
| 96 case ">": return new BoolConstant(value > rightNum); |
| 97 case ">=": return new BoolConstant(value >= rightNum); |
| 98 case "/": return new DoubleConstant(value / rightNum); |
| 99 // We have to treat '==' and '!=' here in case rightNum is a double. |
| 100 case "==": return new BoolConstant(value == rightNum); |
| 101 case "!=": return new BoolConstant(value != rightNum); |
| 102 } |
| 103 if (other.isInt()) { |
| 104 int right = rightNum; |
| 105 switch (op) { |
| 106 case "+": return new IntConstant(value + right); |
| 107 case "-": return new IntConstant(value - right); |
| 108 case "*": return new IntConstant(value * right); |
| 109 case "%": return new IntConstant(value % right); |
| 110 case "~/": return new IntConstant(value ~/ right); |
| 111 case "|": return new IntConstant(value | right); |
| 112 case "&": return new IntConstant(value & right); |
| 113 case "^": return new IntConstant(value ^ right); |
| 114 case "<<": |
| 115 // TODO(floitsch): find a better way to guard against shifts to the |
| 116 // left. |
| 117 if (right > 100) null; |
| 118 if (right < 0) null; |
| 119 return new IntConstant(value << right); |
| 120 case ">>": |
| 121 if (right < 0) return null; |
| 122 return new IntConstant(value >> right); |
| 123 } |
| 124 } else if (other.isDouble()) { |
| 125 double right = rightNum; |
| 126 switch (op) { |
| 127 case "+": return new DoubleConstant(value + right); |
| 128 case "-": return new DoubleConstant(value - right); |
| 129 case "*": return new DoubleConstant(value * right); |
| 130 case "~/": return new DoubleConstant(value ~/ right); |
| 131 case "%": return new DoubleConstant(value % right); |
| 132 } |
| 133 } |
| 134 } |
| 135 // Visit super in case the [op] was "==", "===", "!=" or "!==". |
| 136 return super.binaryFold(op, other); |
| 137 } |
| 138 |
| 139 // We have to override the equality operator so that ints and doubles are |
| 140 // treated as separate constants. |
| 141 // The is [:!IntConstant:] check at the beginning of the function makes sure |
| 142 // that we compare only equal to integer constants. |
| 143 bool operator ==(var other) { |
| 144 if (other is !IntConstant) return false; |
| 145 IntConstant otherInt = other; |
| 146 return value == otherInt.value; |
| 147 } |
| 148 |
| 149 int hashCode() => value.hashCode(); |
| 150 } |
| 151 |
| 152 class DoubleConstant extends PrimitiveConstant { |
| 153 final double value; |
| 154 const DoubleConstant(this.value); |
| 155 bool isDouble() => true; |
| 156 |
| 157 void writeJsCode(StringBuffer buffer, CompileTimeConstantHandler handler) { |
| 158 if (value.isNaN()) { |
| 159 buffer.add("(0/0)"); |
| 160 } else if (value == double.INFINITY) { |
| 161 buffer.add("(1/0)"); |
| 162 } else if (value == -double.INFINITY) { |
| 163 buffer.add("(-1/0)"); |
| 164 } else { |
| 165 buffer.add("($value)"); |
| 166 } |
| 167 } |
| 168 |
| 169 DoubleConstant unaryFold(String op) { |
| 170 if (op == "-") return new DoubleConstant(-value); |
| 171 return null; |
| 172 } |
| 173 |
| 174 Constant binaryFold(String op, Constant other) { |
| 175 if (other.isNum()) { |
| 176 PrimitiveConstant otherPrimitive = other; |
| 177 num right = otherPrimitive.value; |
| 178 switch (op) { |
| 179 case "<": return new BoolConstant(value < right); |
| 180 case "<=": return new BoolConstant(value <= right); |
| 181 case ">": return new BoolConstant(value > right); |
| 182 case ">=": return new BoolConstant(value >= right); |
| 183 case "+": return new DoubleConstant(value + right); |
| 184 case "-": return new DoubleConstant(value - right); |
| 185 case "*": return new DoubleConstant(value * right); |
| 186 case "~/": return new DoubleConstant(value ~/ right); |
| 187 case "/": return new DoubleConstant(value / right); |
| 188 case "%": return new DoubleConstant(value % right); |
| 189 // We have to handle '==' and '!=' here in case right is an integer, |
| 190 // or one of the operands is NaN, -0.0 or 0.0. |
| 191 case "==": return new BoolConstant(value == right); |
| 192 case "!=": return new BoolConstant(value != right); |
| 193 } |
| 194 } |
| 195 // Visit super in case the [op] was "==", "===", "!=" or "!===". |
| 196 return super.binaryFold(op, other); |
| 197 } |
| 198 |
| 199 bool operator ==(var other) { |
| 200 if (other is !DoubleConstant) return false; |
| 201 DoubleConstant otherDouble = other; |
| 202 double otherValue = otherDouble.value; |
| 203 if (value == 0.0 && otherValue == 0.0) { |
| 204 return value.isNegative() == otherValue.isNegative(); |
| 205 } else if (value.isNaN()) { |
| 206 return otherValue.isNaN(); |
| 207 } else { |
| 208 return value == otherValue; |
| 209 } |
| 210 } |
| 211 |
| 212 int hashCode() => value.hashCode(); |
| 213 } |
| 214 |
| 215 class BoolConstant extends PrimitiveConstant { |
| 216 final bool value; |
| 217 const BoolConstant(this.value); |
| 218 bool isBool() => true; |
| 219 |
| 220 void writeJsCode(StringBuffer buffer, CompileTimeConstantHandler handler) { |
| 221 buffer.add(value ? "true" : "false"); |
| 222 } |
| 223 |
| 224 BoolConstant unaryFold(String op) { |
| 225 if (op == "!") return new BoolConstant(!value); |
| 226 return null; |
| 227 } |
| 228 |
| 229 bool operator ==(var other) { |
| 230 if (other is !BoolConstant) return false; |
| 231 BoolConstant otherBool = other; |
| 232 return value == otherBool.value; |
| 233 } |
| 234 |
| 235 // The magic constants are just random values. They don't have any |
| 236 // significance. |
| 237 int hashCode() => value ? 499 : 536555975; |
| 238 } |
| 239 |
| 240 class StringConstant extends PrimitiveConstant { |
| 241 final DartString value; |
| 242 int _hashCode; |
| 243 |
| 244 StringConstant(this.value) { |
| 245 // TODO(floitsch): compute hashcode without calling toString() on the |
| 246 // DartString. |
| 247 _hashCode = value.toString().hashCode(); |
| 248 } |
| 249 bool isString() => true; |
| 250 |
| 251 void writeJsCode(StringBuffer buffer, CompileTimeConstantHandler handler) { |
| 252 buffer.add("'"); |
| 253 CompileTimeConstantHandler.writeEscapedString(value, buffer, (reason) { |
| 254 throw new CompilerCancelledException(reason); |
| 255 }); |
| 256 buffer.add("'"); |
| 257 } |
| 258 |
| 259 StringConstant binaryFold(String op, Constant other) { |
| 260 if (other.isString() && op == "+") { |
| 261 StringConstant otherString = other; |
| 262 DartString right = otherString.value; |
| 263 return new StringConstant(new ConsDartString(value, right)); |
| 264 } |
| 265 // Visit super in case the [op] was "==", "===", "!=" or "!===". |
| 266 return super.binaryFold(op, other); |
| 267 } |
| 268 |
| 269 bool operator ==(var other) { |
| 270 if (other is !StringConstant) return false; |
| 271 StringConstant otherString = other; |
| 272 return (_hashCode == otherString._hashCode) && (value == otherString.value); |
| 273 } |
| 274 |
| 275 int hashCode() => _hashCode; |
| 276 } |
| 277 |
| 278 class ObjectConstant extends Constant { |
| 279 final Type type; |
| 280 |
| 281 ObjectConstant(this.type); |
| 282 } |
| 283 |
| 284 class ListConstant extends ObjectConstant { |
| 285 final List<Constant> entries; |
| 286 int _hashCode; |
| 287 |
| 288 ListConstant(Type type, this.entries) : super(type) { |
| 289 // TODO(floitsch): create a better hash. |
| 290 int hash = 0; |
| 291 for (Constant input in entries) hash ^= input.hashCode(); |
| 292 _hashCode = hash; |
| 293 } |
| 294 bool isList() => true; |
| 295 |
| 296 void writeJsCode(StringBuffer buffer, CompileTimeConstantHandler handler) { |
| 297 // TODO(floitsch): we should not need to go through the compiler to make |
| 298 // the list constant. |
| 299 buffer.add(handler.compiler.namer.ISOLATE); |
| 300 buffer.add(".prototype.makeConstantList"); |
| 301 buffer.add("(["); |
| 302 for (int i = 0; i < entries.length; i++) { |
| 303 if (i != 0) buffer.add(", "); |
| 304 Constant entry = entries[i]; |
| 305 if (entry.isObject()) { |
| 306 handler.getNameForConstant(entry); |
| 307 } else { |
| 308 entry.writeJsCode(buffer, handler); |
| 309 } |
| 310 } |
| 311 buffer.add("])"); |
| 312 } |
| 313 |
| 314 bool operator ==(var other) { |
| 315 if (other is !ListConstant) return false; |
| 316 ListConstant otherList = other; |
| 317 if (hashCode() != otherList.hashCode()) return false; |
| 318 // TODO(floitsch): verify that the types are the same. |
| 319 if (entries.length != otherList.entries.length) return false; |
| 320 for (int i = 0; i < entries.length; i++) { |
| 321 if (entries[i] != otherList.entries[i]) return false; |
| 322 } |
| 323 return true; |
| 324 } |
| 325 |
| 326 int hashCode() => _hashCode; |
| 327 } |
| 328 |
| 329 class ConstructedConstant extends ObjectConstant { |
| 330 final List<Constant> fields; |
| 331 int _hashCode; |
| 332 |
| 333 ConstructedConstant(Type type, this.fields) : super(type) { |
| 334 assert(type !== null); |
| 335 // TODO(floitsch): create a better hash. |
| 336 int hash = 0; |
| 337 for (Constant field in fields) { |
| 338 hash ^= field.hashCode(); |
| 339 } |
| 340 hash ^= type.element.hashCode(); |
| 341 _hashCode = hash; |
| 342 } |
| 343 bool isConstructedObject() => true; |
| 344 |
| 345 void writeJsCode(StringBuffer buffer, CompileTimeConstantHandler handler) { |
| 346 buffer.add("new "); |
| 347 buffer.add(handler.getJsConstructor(type.element)); |
| 348 buffer.add("("); |
| 349 for (int i = 0; i < fields.length; i++) { |
| 350 if (i != 0) buffer.add(", "); |
| 351 Constant field = fields[i]; |
| 352 // TODO(floitsch): share this code with the ListConstant. |
| 353 if (field.isObject()) { |
| 354 handler.getNameForConstant(field); |
| 355 } else { |
| 356 field.writeJsCode(buffer, handler); |
| 357 } |
| 358 } |
| 359 buffer.add(")"); |
| 360 } |
| 361 |
| 362 bool operator ==(var otherVar) { |
| 363 if (otherVar is !ConstructedConstant) return false; |
| 364 ConstructedConstant other = otherVar; |
| 365 if (hashCode() != other.hashCode()) return false; |
| 366 // TODO(floitsch): verify that the (generic) types are the same. |
| 367 if (type.element != other.type.element) return false; |
| 368 if (fields.length != other.fields.length) return false; |
| 369 for (int i = 0; i < fields.length; i++) { |
| 370 if (fields[i] != other.fields[i]) return false; |
| 371 } |
| 372 return true; |
| 373 } |
| 374 |
| 375 int hashCode() => _hashCode; |
| 17 } | 376 } |
| 18 | 377 |
| 19 /** | 378 /** |
| 20 * The [CompileTimeConstantHandler] keeps track of compile-time constants, | 379 * The [CompileTimeConstantHandler] keeps track of compile-time constants, |
| 21 * initializations of global and static fields, and default values of | 380 * initializations of global and static fields, and default values of |
| 22 * optional parameters. | 381 * optional parameters. |
| 23 */ | 382 */ |
| 24 class CompileTimeConstantHandler extends CompilerTask { | 383 class CompileTimeConstantHandler extends CompilerTask { |
| 25 // Contains the initial value of fields. Must contain all static and global | 384 // Contains the initial value of fields. Must contain all static and global |
| 26 // initializations of used fields. May contain caches for instance fields. | 385 // initializations of used fields. May contain caches for instance fields. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 51 assert(work.element.kind == ElementKind.FIELD | 410 assert(work.element.kind == ElementKind.FIELD |
| 52 || work.element.kind == ElementKind.PARAMETER); | 411 || work.element.kind == ElementKind.PARAMETER); |
| 53 VariableElement element = work.element; | 412 VariableElement element = work.element; |
| 54 // Shortcut if it has already been compiled. | 413 // Shortcut if it has already been compiled. |
| 55 if (initialVariableValues.containsKey(element)) return; | 414 if (initialVariableValues.containsKey(element)) return; |
| 56 compileVariableWithDefinitions(element, work.resolutionTree); | 415 compileVariableWithDefinitions(element, work.resolutionTree); |
| 57 } | 416 } |
| 58 | 417 |
| 59 compileVariable(VariableElement element) { | 418 compileVariable(VariableElement element) { |
| 60 if (initialVariableValues.containsKey(element)) { | 419 if (initialVariableValues.containsKey(element)) { |
| 61 return initialVariableValues[element]; | 420 Constant result = initialVariableValues[element]; |
| 421 // TODO(floitsch): remove the following line once the rest of the |
| 422 // compiler has been adapted. |
| 423 if (!result.isObject()) return result.dynamic.value; |
| 424 return result; |
| 62 } | 425 } |
| 63 // TODO(floitsch): keep track of currently compiling elements so that we | 426 // TODO(floitsch): keep track of currently compiling elements so that we |
| 64 // don't end up in an infinite loop: final x = y; final y = x; | 427 // don't end up in an infinite loop: final x = y; final y = x; |
| 65 TreeElements definitions = compiler.analyzeElement(element); | 428 TreeElements definitions = compiler.analyzeElement(element); |
| 66 return compileVariableWithDefinitions(element, definitions); | 429 Constant constant = compileVariableWithDefinitions(element, definitions); |
| 430 // TODO(floitsch): remove the following line once the rest of the |
| 431 // compiler has been adapted. |
| 432 if (!constant.isObject()) return constant.dynamic.value; |
| 433 return constant; |
| 67 } | 434 } |
| 68 | 435 |
| 69 compileVariableWithDefinitions(VariableElement element, | 436 compileVariableWithDefinitions(VariableElement element, |
| 70 TreeElements definitions) { | 437 TreeElements definitions) { |
| 71 return measure(() { | 438 return measure(() { |
| 72 Node node = element.parseNode(compiler); | 439 Node node = element.parseNode(compiler); |
| 73 assert(node !== null); | 440 assert(node !== null); |
| 74 SendSet assignment = node.asSendSet(); | 441 SendSet assignment = node.asSendSet(); |
| 75 var value; | 442 var value; |
| 76 if (assignment === null) { | 443 if (assignment === null) { |
| 77 // No initial value. | 444 // No initial value. |
| 78 value = null; | 445 value = const NullConstant(); |
| 79 } else { | 446 } else { |
| 80 Node right = assignment.arguments.head; | 447 Node right = assignment.arguments.head; |
| 81 CompileTimeConstantEvaluator evaluator = | 448 CompileTimeConstantEvaluator evaluator = |
| 82 new CompileTimeConstantEvaluator(this, definitions, compiler); | 449 new CompileTimeConstantEvaluator(this, definitions, compiler); |
| 83 value = evaluator.evaluate(right); | 450 value = evaluator.evaluate(right); |
| 84 } | 451 } |
| 85 initialVariableValues[element] = value; | 452 initialVariableValues[element] = value; |
| 86 return value; | 453 return value; |
| 87 }); | 454 }); |
| 88 } | 455 } |
| 89 | 456 |
| 90 compileObjectCreation(Node node, Element constructor, List arguments) { | 457 ConstructedConstant compileObjectConstruction(Node node, |
| 458 Type type, |
| 459 List arguments) { |
| 91 if (!arguments.isEmpty()) { | 460 if (!arguments.isEmpty()) { |
| 92 compiler.unimplemented("CompileTimeConstantHandler with arguments", | 461 compiler.unimplemented("CompileTimeConstantHandler with arguments", |
| 93 node: node); | 462 node: node); |
| 94 } | 463 } |
| 95 ClassElement classElement = constructor.enclosingElement; | 464 ClassElement classElement = type.element; |
| 96 for (Element member in classElement.members) { | 465 for (Element member in classElement.members) { |
| 97 if (Elements.isInstanceField(member)) { | 466 if (Elements.isInstanceField(member)) { |
| 98 compiler.unimplemented("CompileTimeConstantHandler with fields", | 467 compiler.unimplemented("CompileTimeConstantHandler with fields", |
| 99 node: node); | 468 node: node); |
| 100 } | 469 } |
| 101 } | 470 } |
| 102 if (classElement.superclass != compiler.coreLibrary.find(Types.OBJECT)) { | 471 if (classElement.superclass != compiler.coreLibrary.find(Types.OBJECT)) { |
| 103 compiler.unimplemented("CompileTimeConstantHandler with super", | 472 compiler.unimplemented("CompileTimeConstantHandler with super", |
| 104 node: node); | 473 node: node); |
| 105 } | 474 } |
| 106 compiler.registerInstantiatedClass(classElement); | 475 compiler.registerInstantiatedClass(classElement); |
| 107 Namer namer = compiler.namer; | 476 Constant constant = new ConstructedConstant(type, arguments); |
| 108 String instantiation = "new ${namer.isolatePropertyAccess(classElement)}()"; | |
| 109 Constant constant = new Constant(instantiation); | |
| 110 registerCompileTimeConstant(constant); | 477 registerCompileTimeConstant(constant); |
| 111 return constant; | 478 return constant; |
| 112 } | 479 } |
| 113 | 480 |
| 114 compileListLiteral(Node node, List arguments) { | 481 ListConstant compileListLiteral(Node node, |
| 115 StringBuffer buffer = new StringBuffer(); | 482 Type type, |
| 116 buffer.add(compiler.namer.ISOLATE); | 483 List<Constant> arguments) { |
| 117 buffer.add(".prototype.makeConstantList"); | 484 Constant constant = new ListConstant(type, arguments); |
| 118 buffer.add("(["); | |
| 119 for (int i = 0; i < arguments.length; i++) { | |
| 120 if (i != 0) buffer.add(", "); | |
| 121 // TODO(floitsch): canonicalize if the constant is in the | |
| 122 // [compiledConstant] set. | |
| 123 writeJsCode(buffer, arguments[i]); | |
| 124 } | |
| 125 buffer.add("])"); | |
| 126 // TODO(floitsch): do we have to register 'List' as instantiated class? | |
| 127 String array = buffer.toString(); | |
| 128 Constant constant = new Constant(array); | |
| 129 registerCompileTimeConstant(constant); | 485 registerCompileTimeConstant(constant); |
| 130 return constant; | 486 return constant; |
| 131 } | 487 } |
| 132 | 488 |
| 133 /** | 489 /** |
| 134 * Returns a [List] of static non final fields that need to be initialized. | 490 * Returns a [List] of static non final fields that need to be initialized. |
| 135 * The list must be evaluated in order since the fields might depend on each | 491 * The list must be evaluated in order since the fields might depend on each |
| 136 * other. | 492 * other. |
| 137 */ | 493 */ |
| 138 List<VariableElement> getStaticNonFinalFieldsForEmission() { | 494 List<VariableElement> getStaticNonFinalFieldsForEmission() { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 157 } | 513 } |
| 158 | 514 |
| 159 List<Constant> getConstantsForEmission() { | 515 List<Constant> getConstantsForEmission() { |
| 160 return compiledConstants.getKeys(); | 516 return compiledConstants.getKeys(); |
| 161 } | 517 } |
| 162 | 518 |
| 163 String getNameForConstant(Constant constant) { | 519 String getNameForConstant(Constant constant) { |
| 164 return compiledConstants[constant]; | 520 return compiledConstants[constant]; |
| 165 } | 521 } |
| 166 | 522 |
| 167 StringBuffer writeJsCode(StringBuffer buffer, var value) { | 523 StringBuffer writeJsCode(StringBuffer buffer, Constant value) { |
| 168 if (value === null) { | 524 value.writeJsCode(buffer, this); |
| 169 buffer.add("(void 0)"); | |
| 170 } else if (value is num) { | |
| 171 if (value.isNaN()) { | |
| 172 buffer.add("(0/0)"); | |
| 173 } else if (value == double.INFINITY) { | |
| 174 buffer.add("(1/0)"); | |
| 175 } else if (value == -double.INFINITY) { | |
| 176 buffer.add("(-1/0)"); | |
| 177 } else { | |
| 178 buffer.add("($value)"); | |
| 179 } | |
| 180 } else if (value === true) { | |
| 181 buffer.add("true"); | |
| 182 } else if (value === false) { | |
| 183 buffer.add("false"); | |
| 184 } else if (value is DartString) { | |
| 185 buffer.add("'"); | |
| 186 writeEscapedString(value, buffer, (reason) { | |
| 187 compiler.cancel("failed to write escaped string: $value"); | |
| 188 }); | |
| 189 buffer.add("'"); | |
| 190 } else if (value is Constant) { | |
| 191 Constant constant = value; | |
| 192 buffer.add(constant.jsCode); | |
| 193 } else { | |
| 194 // TODO(floitsch): support more values. | |
| 195 compiler.unimplemented("CompileTimeConstantHandler writeJsCode", | |
| 196 element: element); | |
| 197 } | |
| 198 return buffer; | 525 return buffer; |
| 199 } | 526 } |
| 200 | 527 |
| 201 StringBuffer writeJsCodeForVariable(StringBuffer buffer, | 528 StringBuffer writeJsCodeForVariable(StringBuffer buffer, |
| 202 VariableElement element) { | 529 VariableElement element) { |
| 203 var value = initialVariableValues[element]; | 530 if (!initialVariableValues.containsKey(element)) { |
| 204 if (value is Constant) { | 531 buffer.add("(void 0)"); |
| 205 String name = compiledConstants[value]; | 532 return buffer; |
| 533 // TODO(floitsch): reenable the following lines, once we fixed the rest |
| 534 // of the compiler. |
| 535 /* |
| 536 compiler.internalError("No initial value for given element", |
| 537 element: element); |
| 538 */ |
| 539 } |
| 540 Constant constant = initialVariableValues[element]; |
| 541 if (constant.isObject()) { |
| 542 String name = compiledConstants[constant]; |
| 206 buffer.add("${compiler.namer.ISOLATE}.prototype.$name"); | 543 buffer.add("${compiler.namer.ISOLATE}.prototype.$name"); |
| 207 } else { | 544 } else { |
| 208 return writeJsCode(buffer, initialVariableValues[element]); | 545 writeJsCode(buffer, constant); |
| 209 } | 546 } |
| 547 return buffer; |
| 210 } | 548 } |
| 211 | 549 |
| 212 /** | 550 /** |
| 213 * Write the contents of the quoted string to a [StringBuffer] in | 551 * Write the contents of the quoted string to a [StringBuffer] in |
| 214 * a form that is valid as JavaScript string literal content. | 552 * a form that is valid as JavaScript string literal content. |
| 215 * The string is assumed quoted by single quote characters. | 553 * The string is assumed quoted by single quote characters. |
| 216 */ | 554 */ |
| 217 static void writeEscapedString(DartString string, | 555 static void writeEscapedString(DartString string, |
| 218 StringBuffer buffer, | 556 StringBuffer buffer, |
| 219 void cancel(String reason)) { | 557 void cancel(String reason)) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 255 buffer.add('0'); | 593 buffer.add('0'); |
| 256 } | 594 } |
| 257 buffer.add(code.toRadixString(16)); | 595 buffer.add(code.toRadixString(16)); |
| 258 } | 596 } |
| 259 } else { | 597 } else { |
| 260 buffer.add(new String.fromCharCodes(<int>[code])); | 598 buffer.add(new String.fromCharCodes(<int>[code])); |
| 261 } | 599 } |
| 262 } | 600 } |
| 263 } | 601 } |
| 264 } | 602 } |
| 603 |
| 604 String getJsConstructor(ClassElement element) { |
| 605 return compiler.namer.isolatePropertyAccess(element); |
| 606 } |
| 265 } | 607 } |
| 266 | 608 |
| 267 class CompileTimeConstantEvaluator extends AbstractVisitor { | 609 class CompileTimeConstantEvaluator extends AbstractVisitor { |
| 268 final CompileTimeConstantHandler constantHandler; | 610 final CompileTimeConstantHandler constantHandler; |
| 269 final TreeElements definitions; | 611 final TreeElements definitions; |
| 270 final Compiler compiler; | 612 final Compiler compiler; |
| 271 | 613 |
| 272 CompileTimeConstantEvaluator(this.constantHandler, | 614 CompileTimeConstantEvaluator(this.constantHandler, |
| 273 this.definitions, | 615 this.definitions, |
| 274 this.compiler); | 616 this.compiler); |
| 275 | 617 |
| 276 evaluate(Node node) { | 618 Constant evaluate(Node node) { |
| 277 return node.accept(this); | 619 return node.accept(this); |
| 278 } | 620 } |
| 279 | 621 |
| 280 visitNode(Node node) { | 622 visitNode(Node node) { |
| 281 compiler.unimplemented("CompileTimeConstantEvaluator", node: node); | 623 compiler.unimplemented("CompileTimeConstantEvaluator", node: node); |
| 282 } | 624 } |
| 283 | 625 |
| 284 visitLiteral(Literal literal) { | 626 Constant visitLiteralBool(LiteralBool node) { |
| 285 if (literal is LiteralString) { | 627 // TODO(floitsch): make BoolConstant a factory and cache the two values |
| 286 assert(literal.asLiteralString().isValidated()); | 628 // there. |
| 287 return literal.asLiteralString().dartString; | 629 return node.value ? const BoolConstant(true) : const BoolConstant(false); |
| 630 } |
| 631 |
| 632 Constant visitLiteralDouble(LiteralDouble node) { |
| 633 return new DoubleConstant(node.value); |
| 634 } |
| 635 |
| 636 Constant visitLiteralInt(LiteralInt node) { |
| 637 return new IntConstant(node.value); |
| 638 } |
| 639 |
| 640 Constant visitLiteralList(LiteralList node) { |
| 641 if (!node.isConst()) error(node); |
| 642 List arguments = []; |
| 643 for (Link<Node> link = node.elements.nodes; |
| 644 !link.isEmpty(); |
| 645 link = link.tail) { |
| 646 arguments.add(evaluate(link.head)); |
| 288 } | 647 } |
| 289 return literal.value; | 648 // TODO(floitsch): get type from somewhere. |
| 649 Type type = null; |
| 650 return constantHandler.compileListLiteral(node, type, arguments); |
| 651 } |
| 652 |
| 653 Constant visitLiteralMap(LiteralMap node) { |
| 654 compiler.unimplemented("CompileTimeConstantEvaluator map", node: node); |
| 655 } |
| 656 |
| 657 Constant visitLiteralNull(LiteralNull node) { |
| 658 return const NullConstant(); |
| 659 } |
| 660 |
| 661 Constant visitLiteralString(LiteralString node) { |
| 662 return new StringConstant(node.dartString); |
| 290 } | 663 } |
| 291 | 664 |
| 292 // TODO(floitsch): provide better error-messages. | 665 // TODO(floitsch): provide better error-messages. |
| 293 visitSend(Send send) { | 666 visitSend(Send send) { |
| 294 Element element = definitions[send]; | 667 Element element = definitions[send]; |
| 295 if (Elements.isStaticOrTopLevelField(element)) { | 668 if (Elements.isStaticOrTopLevelField(element)) { |
| 296 if (element.modifiers === null || | 669 if (element.modifiers === null || |
| 297 !element.modifiers.isFinal()) { | 670 !element.modifiers.isFinal()) { |
| 298 error(send); | 671 error(send); |
| 299 } | 672 } |
| 300 return constantHandler.compileVariable(element); | 673 // TODO(floitsch): compileVariable temporarily returns primitives, so |
| 674 // that the rest of the compiler can be adapted incrementally. Therefore |
| 675 // we have to get the constant from the hashtable instead of using the |
| 676 // returned result directly. |
| 677 constantHandler.compileVariable(element); |
| 678 return constantHandler.initialVariableValues[element]; |
| 301 } else if (send.isPrefix) { | 679 } else if (send.isPrefix) { |
| 302 assert(send.isOperator); | 680 assert(send.isOperator); |
| 303 var receiverValue = evaluate(send.receiver); | 681 Constant receiverConstant = evaluate(send.receiver); |
| 304 Operator op = send.selector; | 682 Operator op = send.selector; |
| 305 switch (op.source.stringValue) { | 683 Constant folded = receiverConstant.unaryFold(op.source.stringValue); |
| 306 case "-": | 684 if (folded === null) error(send); |
| 307 if (receiverValue is !num) error(send); | 685 return folded; |
| 308 return -receiverValue; | |
| 309 case "~": | |
| 310 if (receiverValue is !int) error(send); | |
| 311 return ~receiverValue; | |
| 312 case "!": | |
| 313 if (receiverValue is !bool) error(send); | |
| 314 return !receiverValue; | |
| 315 default: | |
| 316 error(send); | |
| 317 } | |
| 318 } else if (send.isOperator && !send.isPostfix) { | 686 } else if (send.isOperator && !send.isPostfix) { |
| 319 assert(send.argumentCount() == 1); | 687 assert(send.argumentCount() == 1); |
| 320 var left = evaluate(send.receiver); | 688 Constant left = evaluate(send.receiver); |
| 321 var right = evaluate(send.argumentsNode.nodes.head); | 689 Constant right = evaluate(send.argumentsNode.nodes.head); |
| 322 String op = send.selector.asOperator().source.stringValue; | 690 String op = send.selector.asOperator().source.stringValue; |
| 323 | 691 Constant folded = left.binaryFold(op, right); |
| 324 if (op == "==" || op == "===") { | 692 if (folded === null) error(send); |
| 325 // We use == instead of === so that non-canonicalized DartStrings can | 693 return folded; |
| 326 // use their equality operator. | |
| 327 return left == right; | |
| 328 } else if (op == "!=" || op == "!==") { | |
| 329 return left != right; | |
| 330 } | |
| 331 if (left is num && right is num) { | |
| 332 switch (op) { | |
| 333 case "+": return left + right; | |
| 334 case "-": return left - right; | |
| 335 case "*": return left * right; | |
| 336 case "/": return left / right; | |
| 337 case "~/": | |
| 338 case "%": | |
| 339 if (left is int && right is int && right == 0) { | |
| 340 error(send); | |
| 341 } | |
| 342 return op == "~/" ? left ~/ right : left % right; | |
| 343 case "<": return left < right; | |
| 344 case "<=": return left <= right; | |
| 345 case ">": return left > right; | |
| 346 case ">=": return left >= right; | |
| 347 } | |
| 348 } | |
| 349 if (left is int && right is int) { | |
| 350 switch (op) { | |
| 351 case "|": return left | right; | |
| 352 case "&": return left & right; | |
| 353 case "<<": | |
| 354 // TODO(floitsch): find a better way to guard against shifts to the | |
| 355 // left. | |
| 356 if (right > 100) error(send); | |
| 357 if (right < 0) error(send); | |
| 358 return left << right; | |
| 359 case ">>": | |
| 360 if (right < 0) error(send); | |
| 361 return left >> right; | |
| 362 case "^": return left ^ right; | |
| 363 } | |
| 364 } | |
| 365 if (left is DartString && right is DartString && op == "+") { | |
| 366 return new ConsDartString(left, right); | |
| 367 } | |
| 368 } | 694 } |
| 369 return super.visitSend(send); | 695 return super.visitSend(send); |
| 370 } | 696 } |
| 371 | 697 |
| 372 visitSendSet(SendSet node) { | 698 visitSendSet(SendSet node) { |
| 373 error(node); | 699 error(node); |
| 374 } | 700 } |
| 375 | 701 |
| 376 visitNewExpression(NewExpression node) { | 702 visitNewExpression(NewExpression node) { |
| 377 if (!node.isConst()) error(node); | 703 if (!node.isConst()) error(node); |
| 378 Send send = node.send; | 704 Send send = node.send; |
| 379 List arguments; | 705 List arguments; |
| 380 if (send.arguments.isEmpty()) { | 706 if (send.arguments.isEmpty()) { |
| 381 arguments = const []; | 707 arguments = const []; |
| 382 } else { | 708 } else { |
| 383 arguments = []; | 709 arguments = []; |
| 384 for (Link<Node> link = send.arguments; | 710 for (Link<Node> link = send.arguments; |
| 385 !link.isEmpty(); | 711 !link.isEmpty(); |
| 386 link = link.tail) { | 712 link = link.tail) { |
| 387 arguments.add(evaluate(link.head)); | 713 arguments.add(evaluate(link.head)); |
| 388 } | 714 } |
| 389 } | 715 } |
| 390 return constantHandler.compileObjectCreation(node, definitions[node.send], | 716 // TODO(floitsch): get the type from somewhere. |
| 391 arguments); | 717 Element constructorElement = definitions[node.send]; |
| 392 } | 718 ClassElement classElement = constructorElement.enclosingElement; |
| 393 | 719 Type type = new SimpleType(classElement.name, classElement); |
| 394 visitLiteralList(LiteralList node) { | 720 return constantHandler.compileObjectConstruction(node, |
| 395 if (!node.isConst()) error(node); | 721 type, |
| 396 List arguments = []; | 722 arguments); |
| 397 for (Link<Node> link = node.elements.nodes; | |
| 398 !link.isEmpty(); | |
| 399 link = link.tail) { | |
| 400 arguments.add(evaluate(link.head)); | |
| 401 } | |
| 402 return constantHandler.compileListLiteral(node, arguments); | |
| 403 } | 723 } |
| 404 | 724 |
| 405 error(Node node) { | 725 error(Node node) { |
| 406 // TODO(floitsch): get the list of constants that are currently compiled | 726 // TODO(floitsch): get the list of constants that are currently compiled |
| 407 // and present some kind of stack-trace. | 727 // and present some kind of stack-trace. |
| 408 MessageKind kind = MessageKind.NOT_A_COMPILE_TIME_CONSTANT; | 728 MessageKind kind = MessageKind.NOT_A_COMPILE_TIME_CONSTANT; |
| 409 compiler.reportError(node, new CompileTimeConstantError(kind, const [])); | 729 compiler.reportError(node, new CompileTimeConstantError(kind, const [])); |
| 410 } | 730 } |
| 411 } | 731 } |
| OLD | NEW |