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