| 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 class Constant implements Hashable { | |
| 6 const Constant(); | |
| 7 | |
| 8 bool isNull() => false; | |
| 9 bool isBool() => false; | |
| 10 bool isTrue() => false; | |
| 11 bool isFalse() => false; | |
| 12 bool isInt() => false; | |
| 13 bool isDouble() => false; | |
| 14 bool isNum() => false; | |
| 15 bool isString() => false; | |
| 16 bool isList() => false; | |
| 17 bool isMap() => false; | |
| 18 bool isConstructedObject() => false; | |
| 19 bool isFunction() => false; | |
| 20 /** Returns true if the constant is null, a bool, a number or a string. */ | |
| 21 bool isPrimitive() => false; | |
| 22 /** Returns true if the constant is a list, a map or a constructed object. */ | |
| 23 bool isObject() => false; | |
| 24 bool isSentinel() => false; | |
| 25 | |
| 26 bool isNaN() => false; | |
| 27 bool isMinusZero() => false; | |
| 28 | |
| 29 abstract void _writeJsCode(CodeBuffer buffer, ConstantHandler handler); | |
| 30 /** | |
| 31 * Unless the constant can be emitted multiple times (as for numbers and | |
| 32 * strings) adds its canonical name to the buffer. | |
| 33 */ | |
| 34 abstract void _writeCanonicalizedJsCode(CodeBuffer buffer, | |
| 35 ConstantHandler handler); | |
| 36 abstract List<Constant> getDependencies(); | |
| 37 } | |
| 38 | |
| 39 class SentinelConstant extends Constant { | |
| 40 const SentinelConstant(); | |
| 41 static final SENTINEL = const SentinelConstant(); | |
| 42 | |
| 43 void _writeJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 44 handler.compiler.internalError( | |
| 45 "The parameter sentinel constant does not need specific JS code"); | |
| 46 } | |
| 47 | |
| 48 void _writeCanonicalizedJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 49 buffer.add(handler.compiler.namer.CURRENT_ISOLATE); | |
| 50 } | |
| 51 | |
| 52 List<Constant> getDependencies() => const <Constant>[]; | |
| 53 | |
| 54 // Just use a randome value. | |
| 55 int hashCode() => 926429784158; | |
| 56 | |
| 57 bool isSentinel() => true; | |
| 58 } | |
| 59 | |
| 60 class FunctionConstant extends Constant { | |
| 61 Element element; | |
| 62 | |
| 63 FunctionConstant(this.element); | |
| 64 | |
| 65 bool isFunction() => true; | |
| 66 | |
| 67 bool operator ==(var other) { | |
| 68 if (other is !FunctionConstant) return false; | |
| 69 return other.element === element; | |
| 70 } | |
| 71 | |
| 72 String toString() => element.toString(); | |
| 73 List<Constant> getDependencies() => const <Constant>[]; | |
| 74 DartString toDartString() { | |
| 75 return new DartString.literal(element.name.slowToString()); | |
| 76 } | |
| 77 | |
| 78 void _writeJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 79 handler.compiler.internalError( | |
| 80 "A constant function does not need specific JS code"); | |
| 81 } | |
| 82 | |
| 83 void _writeCanonicalizedJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 84 buffer.add(handler.compiler.namer.isolatePropertiesAccess(element)); | |
| 85 } | |
| 86 | |
| 87 int hashCode() => (17 * element.hashCode()) & 0x7fffffff; | |
| 88 } | |
| 89 | |
| 90 class PrimitiveConstant extends Constant { | |
| 91 abstract get value; | |
| 92 const PrimitiveConstant(); | |
| 93 bool isPrimitive() => true; | |
| 94 | |
| 95 bool operator ==(var other) { | |
| 96 if (other is !PrimitiveConstant) return false; | |
| 97 PrimitiveConstant otherPrimitive = other; | |
| 98 // We use == instead of === so that DartStrings compare correctly. | |
| 99 return value == otherPrimitive.value; | |
| 100 } | |
| 101 | |
| 102 String toString() => value.toString(); | |
| 103 // Primitive constants don't have dependencies. | |
| 104 List<Constant> getDependencies() => const <Constant>[]; | |
| 105 abstract DartString toDartString(); | |
| 106 | |
| 107 void _writeCanonicalizedJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 108 _writeJsCode(buffer, handler); | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 class NullConstant extends PrimitiveConstant { | |
| 113 /** The value a Dart null is compiled to in JavaScript. */ | |
| 114 static const String JsNull = "null"; | |
| 115 | |
| 116 factory NullConstant() => const NullConstant._internal(); | |
| 117 const NullConstant._internal(); | |
| 118 bool isNull() => true; | |
| 119 get value => null; | |
| 120 | |
| 121 void _writeJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 122 buffer.add(JsNull); | |
| 123 } | |
| 124 | |
| 125 // The magic constant has no meaning. It is just a random value. | |
| 126 int hashCode() => 785965825; | |
| 127 DartString toDartString() => const LiteralDartString("null"); | |
| 128 } | |
| 129 | |
| 130 class NumConstant extends PrimitiveConstant { | |
| 131 abstract num get value; | |
| 132 const NumConstant(); | |
| 133 bool isNum() => true; | |
| 134 } | |
| 135 | |
| 136 class IntConstant extends NumConstant { | |
| 137 final int value; | |
| 138 factory IntConstant(int value) { | |
| 139 switch (value) { | |
| 140 case 0: return const IntConstant._internal(0); | |
| 141 case 1: return const IntConstant._internal(1); | |
| 142 case 2: return const IntConstant._internal(2); | |
| 143 case 3: return const IntConstant._internal(3); | |
| 144 case 4: return const IntConstant._internal(4); | |
| 145 case 5: return const IntConstant._internal(5); | |
| 146 case 6: return const IntConstant._internal(6); | |
| 147 case 7: return const IntConstant._internal(7); | |
| 148 case 8: return const IntConstant._internal(8); | |
| 149 case 9: return const IntConstant._internal(9); | |
| 150 case 10: return const IntConstant._internal(10); | |
| 151 case -1: return const IntConstant._internal(-1); | |
| 152 case -2: return const IntConstant._internal(-2); | |
| 153 default: return new IntConstant._internal(value); | |
| 154 } | |
| 155 } | |
| 156 const IntConstant._internal(this.value); | |
| 157 bool isInt() => true; | |
| 158 | |
| 159 void _writeJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 160 buffer.add("$value"); | |
| 161 } | |
| 162 | |
| 163 // We have to override the equality operator so that ints and doubles are | |
| 164 // treated as separate constants. | |
| 165 // The is [:!IntConstant:] check at the beginning of the function makes sure | |
| 166 // that we compare only equal to integer constants. | |
| 167 bool operator ==(var other) { | |
| 168 if (other is !IntConstant) return false; | |
| 169 IntConstant otherInt = other; | |
| 170 return value == otherInt.value; | |
| 171 } | |
| 172 | |
| 173 int hashCode() => value.hashCode(); | |
| 174 DartString toDartString() => new DartString.literal(value.toString()); | |
| 175 } | |
| 176 | |
| 177 class DoubleConstant extends NumConstant { | |
| 178 final double value; | |
| 179 factory DoubleConstant(double value) { | |
| 180 if (value.isNaN()) { | |
| 181 return const DoubleConstant._internal(double.NAN); | |
| 182 } else if (value == double.INFINITY) { | |
| 183 return const DoubleConstant._internal(double.INFINITY); | |
| 184 } else if (value == -double.INFINITY) { | |
| 185 return const DoubleConstant._internal(-double.INFINITY); | |
| 186 } else if (value == 0.0 && !value.isNegative()) { | |
| 187 return const DoubleConstant._internal(0.0); | |
| 188 } else if (value == 1.0) { | |
| 189 return const DoubleConstant._internal(1.0); | |
| 190 } else { | |
| 191 return new DoubleConstant._internal(value); | |
| 192 } | |
| 193 } | |
| 194 const DoubleConstant._internal(this.value); | |
| 195 bool isDouble() => true; | |
| 196 bool isNaN() => value.isNaN(); | |
| 197 // We need to check for the negative sign since -0.0 == 0.0. | |
| 198 bool isMinusZero() => value == 0.0 && value.isNegative(); | |
| 199 | |
| 200 void _writeJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 201 if (value.isNaN()) { | |
| 202 buffer.add("(0/0)"); | |
| 203 } else if (value == double.INFINITY) { | |
| 204 buffer.add("(1/0)"); | |
| 205 } else if (value == -double.INFINITY) { | |
| 206 buffer.add("(-1/0)"); | |
| 207 } else { | |
| 208 buffer.add("$value"); | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 bool operator ==(var other) { | |
| 213 if (other is !DoubleConstant) return false; | |
| 214 DoubleConstant otherDouble = other; | |
| 215 double otherValue = otherDouble.value; | |
| 216 if (value == 0.0 && otherValue == 0.0) { | |
| 217 return value.isNegative() == otherValue.isNegative(); | |
| 218 } else if (value.isNaN()) { | |
| 219 return otherValue.isNaN(); | |
| 220 } else { | |
| 221 return value == otherValue; | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 int hashCode() => value.hashCode(); | |
| 226 DartString toDartString() => new DartString.literal(value.toString()); | |
| 227 } | |
| 228 | |
| 229 class BoolConstant extends PrimitiveConstant { | |
| 230 factory BoolConstant(value) { | |
| 231 return value ? new TrueConstant() : new FalseConstant(); | |
| 232 } | |
| 233 const BoolConstant._internal(); | |
| 234 bool isBool() => true; | |
| 235 | |
| 236 abstract BoolConstant negate(); | |
| 237 } | |
| 238 | |
| 239 class TrueConstant extends BoolConstant { | |
| 240 final bool value = true; | |
| 241 | |
| 242 factory TrueConstant() => const TrueConstant._internal(); | |
| 243 const TrueConstant._internal() : super._internal(); | |
| 244 bool isTrue() => true; | |
| 245 | |
| 246 void _writeJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 247 buffer.add("true"); | |
| 248 } | |
| 249 | |
| 250 FalseConstant negate() => new FalseConstant(); | |
| 251 | |
| 252 bool operator ==(var other) => this === other; | |
| 253 // The magic constant is just a random value. It does not have any | |
| 254 // significance. | |
| 255 int hashCode() => 499; | |
| 256 DartString toDartString() => const LiteralDartString("true"); | |
| 257 } | |
| 258 | |
| 259 class FalseConstant extends BoolConstant { | |
| 260 final bool value = false; | |
| 261 | |
| 262 factory FalseConstant() => const FalseConstant._internal(); | |
| 263 const FalseConstant._internal() : super._internal(); | |
| 264 bool isFalse() => true; | |
| 265 | |
| 266 void _writeJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 267 buffer.add("false"); | |
| 268 } | |
| 269 | |
| 270 TrueConstant negate() => new TrueConstant(); | |
| 271 | |
| 272 bool operator ==(var other) => this === other; | |
| 273 // The magic constant is just a random value. It does not have any | |
| 274 // significance. | |
| 275 int hashCode() => 536555975; | |
| 276 DartString toDartString() => const LiteralDartString("false"); | |
| 277 } | |
| 278 | |
| 279 class StringConstant extends PrimitiveConstant { | |
| 280 final DartString value; | |
| 281 int _hashCode; | |
| 282 final Node node; | |
| 283 | |
| 284 StringConstant(this.value, this.node) { | |
| 285 // TODO(floitsch): cache StringConstants. | |
| 286 // TODO(floitsch): compute hashcode without calling toString() on the | |
| 287 // DartString. | |
| 288 _hashCode = value.slowToString().hashCode(); | |
| 289 } | |
| 290 bool isString() => true; | |
| 291 | |
| 292 void _writeJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 293 buffer.add("'"); | |
| 294 ConstantHandler.writeEscapedString(value, buffer, (reason) { | |
| 295 handler.compiler.reportError(node, reason); | |
| 296 }); | |
| 297 buffer.add("'"); | |
| 298 } | |
| 299 | |
| 300 bool operator ==(var other) { | |
| 301 if (other is !StringConstant) return false; | |
| 302 StringConstant otherString = other; | |
| 303 return (_hashCode == otherString._hashCode) && (value == otherString.value); | |
| 304 } | |
| 305 | |
| 306 int hashCode() => _hashCode; | |
| 307 DartString toDartString() => value; | |
| 308 int get length => value.length; | |
| 309 } | |
| 310 | |
| 311 class ObjectConstant extends Constant { | |
| 312 final DartType type; | |
| 313 | |
| 314 ObjectConstant(this.type); | |
| 315 bool isObject() => true; | |
| 316 | |
| 317 // TODO(1603): The class should be marked as abstract, but the VM doesn't | |
| 318 // currently allow this. | |
| 319 abstract int hashCode(); | |
| 320 | |
| 321 void _writeCanonicalizedJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 322 String name = handler.getNameForConstant(this); | |
| 323 buffer.add(handler.compiler.namer.isolatePropertiesAccessForConstant(name)); | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 class ListConstant extends ObjectConstant { | |
| 328 final List<Constant> entries; | |
| 329 int _hashCode; | |
| 330 | |
| 331 ListConstant(DartType type, this.entries) : super(type) { | |
| 332 // TODO(floitsch): create a better hash. | |
| 333 int hash = 0; | |
| 334 for (Constant input in entries) hash ^= input.hashCode(); | |
| 335 _hashCode = hash; | |
| 336 } | |
| 337 bool isList() => true; | |
| 338 | |
| 339 void _writeJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 340 // TODO(floitsch): we should not need to go through the compiler to make | |
| 341 // the list constant. | |
| 342 buffer.add("${handler.compiler.namer.ISOLATE}.makeConstantList"); | |
| 343 buffer.add("(["); | |
| 344 for (int i = 0; i < entries.length; i++) { | |
| 345 if (i != 0) buffer.add(", "); | |
| 346 Constant entry = entries[i]; | |
| 347 handler.writeConstant(buffer, entry); | |
| 348 } | |
| 349 buffer.add("])"); | |
| 350 } | |
| 351 | |
| 352 bool operator ==(var other) { | |
| 353 if (other is !ListConstant) return false; | |
| 354 ListConstant otherList = other; | |
| 355 if (hashCode() != otherList.hashCode()) return false; | |
| 356 // TODO(floitsch): verify that the generic types are the same. | |
| 357 if (entries.length != otherList.entries.length) return false; | |
| 358 for (int i = 0; i < entries.length; i++) { | |
| 359 if (entries[i] != otherList.entries[i]) return false; | |
| 360 } | |
| 361 return true; | |
| 362 } | |
| 363 | |
| 364 int hashCode() => _hashCode; | |
| 365 | |
| 366 List<Constant> getDependencies() => entries; | |
| 367 | |
| 368 int get length => entries.length; | |
| 369 } | |
| 370 | |
| 371 class MapConstant extends ObjectConstant { | |
| 372 /** | |
| 373 * The [PROTO_PROPERTY] must not be used as normal property in any JavaScript | |
| 374 * object. It would change the prototype chain. | |
| 375 */ | |
| 376 static const String PROTO_PROPERTY = "__proto__"; | |
| 377 | |
| 378 /** The dart class implementing constant map literals. */ | |
| 379 static const SourceString DART_CLASS = const SourceString("ConstantMap"); | |
| 380 static const SourceString DART_PROTO_CLASS = | |
| 381 const SourceString("ConstantProtoMap"); | |
| 382 static const SourceString LENGTH_NAME = const SourceString("length"); | |
| 383 static const SourceString JS_OBJECT_NAME = const SourceString("_jsObject"); | |
| 384 static const SourceString KEYS_NAME = const SourceString("_keys"); | |
| 385 static const SourceString PROTO_VALUE = const SourceString("_protoValue"); | |
| 386 | |
| 387 final ListConstant keys; | |
| 388 final List<Constant> values; | |
| 389 final Constant protoValue; | |
| 390 int _hashCode; | |
| 391 | |
| 392 MapConstant(DartType type, this.keys, this.values, this.protoValue) | |
| 393 : super(type) { | |
| 394 // TODO(floitsch): create a better hash. | |
| 395 int hash = 0; | |
| 396 for (Constant value in values) hash ^= value.hashCode(); | |
| 397 _hashCode = hash; | |
| 398 } | |
| 399 bool isMap() => true; | |
| 400 | |
| 401 void _writeJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 402 | |
| 403 void writeJsMap() { | |
| 404 buffer.add("{"); | |
| 405 int valueIndex = 0; | |
| 406 for (int i = 0; i < keys.entries.length; i++) { | |
| 407 StringConstant key = keys.entries[i]; | |
| 408 if (key.value == const LiteralDartString(PROTO_PROPERTY)) continue; | |
| 409 | |
| 410 if (valueIndex != 0) buffer.add(", "); | |
| 411 | |
| 412 key._writeJsCode(buffer, handler); | |
| 413 buffer.add(": "); | |
| 414 Constant value = values[valueIndex++]; | |
| 415 handler.writeConstant(buffer, value); | |
| 416 } | |
| 417 buffer.add("}"); | |
| 418 if (valueIndex != values.length) { | |
| 419 handler.compiler.internalError("Bad value count."); | |
| 420 } | |
| 421 } | |
| 422 | |
| 423 void badFieldCountError() { | |
| 424 handler.compiler.internalError( | |
| 425 "Compiler and ConstantMap disagree on number of fields."); | |
| 426 } | |
| 427 | |
| 428 ClassElement classElement = type.element; | |
| 429 buffer.add("new "); | |
| 430 buffer.add(handler.getJsConstructor(classElement)); | |
| 431 buffer.add("("); | |
| 432 // The arguments of the JavaScript constructor for any given Dart class | |
| 433 // are in the same order as the members of the class element. | |
| 434 int emittedArgumentCount = 0; | |
| 435 classElement.forEachInstanceField( | |
| 436 includeBackendMembers: true, | |
| 437 includeSuperMembers: true, | |
| 438 f: (ClassElement enclosing, Element field) { | |
| 439 if (emittedArgumentCount != 0) buffer.add(", "); | |
| 440 if (field.name == LENGTH_NAME) { | |
| 441 buffer.add(keys.entries.length); | |
| 442 } else if (field.name == JS_OBJECT_NAME) { | |
| 443 writeJsMap(); | |
| 444 } else if (field.name == KEYS_NAME) { | |
| 445 handler.writeConstant(buffer, keys); | |
| 446 } else if (field.name == PROTO_VALUE) { | |
| 447 assert(protoValue !== null); | |
| 448 handler.writeConstant(buffer, protoValue); | |
| 449 } else { | |
| 450 badFieldCountError(); | |
| 451 } | |
| 452 emittedArgumentCount++; | |
| 453 }); | |
| 454 if ((protoValue === null && emittedArgumentCount != 3) || | |
| 455 (protoValue !== null && emittedArgumentCount != 4)) { | |
| 456 badFieldCountError(); | |
| 457 } | |
| 458 buffer.add(")"); | |
| 459 } | |
| 460 | |
| 461 bool operator ==(var other) { | |
| 462 if (other is !MapConstant) return false; | |
| 463 MapConstant otherMap = other; | |
| 464 if (hashCode() != otherMap.hashCode()) return false; | |
| 465 // TODO(floitsch): verify that the generic types are the same. | |
| 466 if (keys != otherMap.keys) return false; | |
| 467 for (int i = 0; i < values.length; i++) { | |
| 468 if (values[i] != otherMap.values[i]) return false; | |
| 469 } | |
| 470 return true; | |
| 471 } | |
| 472 | |
| 473 int hashCode() => _hashCode; | |
| 474 | |
| 475 List<Constant> getDependencies() { | |
| 476 List<Constant> result = <Constant>[keys]; | |
| 477 result.addAll(values); | |
| 478 return result; | |
| 479 } | |
| 480 | |
| 481 int get length => keys.length; | |
| 482 } | |
| 483 | |
| 484 class ConstructedConstant extends ObjectConstant { | |
| 485 final List<Constant> fields; | |
| 486 int _hashCode; | |
| 487 | |
| 488 ConstructedConstant(DartType type, this.fields) : super(type) { | |
| 489 assert(type !== null); | |
| 490 // TODO(floitsch): create a better hash. | |
| 491 int hash = 0; | |
| 492 for (Constant field in fields) { | |
| 493 hash ^= field.hashCode(); | |
| 494 } | |
| 495 hash ^= type.element.hashCode(); | |
| 496 _hashCode = hash; | |
| 497 } | |
| 498 bool isConstructedObject() => true; | |
| 499 | |
| 500 void _writeJsCode(CodeBuffer buffer, ConstantHandler handler) { | |
| 501 buffer.add("new "); | |
| 502 buffer.add(handler.getJsConstructor(type.element)); | |
| 503 buffer.add("("); | |
| 504 for (int i = 0; i < fields.length; i++) { | |
| 505 if (i != 0) buffer.add(", "); | |
| 506 Constant field = fields[i]; | |
| 507 handler.writeConstant(buffer, field); | |
| 508 } | |
| 509 buffer.add(")"); | |
| 510 } | |
| 511 | |
| 512 bool operator ==(var otherVar) { | |
| 513 if (otherVar is !ConstructedConstant) return false; | |
| 514 ConstructedConstant other = otherVar; | |
| 515 if (hashCode() != other.hashCode()) return false; | |
| 516 // TODO(floitsch): verify that the (generic) types are the same. | |
| 517 if (type.element != other.type.element) return false; | |
| 518 if (fields.length != other.fields.length) return false; | |
| 519 for (int i = 0; i < fields.length; i++) { | |
| 520 if (fields[i] != other.fields[i]) return false; | |
| 521 } | |
| 522 return true; | |
| 523 } | |
| 524 | |
| 525 int hashCode() => _hashCode; | |
| 526 List<Constant> getDependencies() => fields; | |
| 527 } | |
| 528 | |
| 529 /** | 5 /** |
| 530 * The [ConstantHandler] keeps track of compile-time constants, | 6 * The [ConstantHandler] keeps track of compile-time constants, |
| 531 * initializations of global and static fields, and default values of | 7 * initializations of global and static fields, and default values of |
| 532 * optional parameters. | 8 * optional parameters. |
| 533 */ | 9 */ |
| 534 class ConstantHandler extends CompilerTask { | 10 class ConstantHandler extends CompilerTask { |
| 535 final ConstantSystem constantSystem; | 11 final ConstantSystem constantSystem; |
| 536 | 12 |
| 537 /** | 13 /** |
| 538 * Contains the initial value of fields. Must contain all static and global | 14 * Contains the initial value of fields. Must contain all static and global |
| (...skipping 800 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1339 Constant fieldValue = fieldValues[field]; | 815 Constant fieldValue = fieldValues[field]; |
| 1340 if (fieldValue === null) { | 816 if (fieldValue === null) { |
| 1341 // Use the default value. | 817 // Use the default value. |
| 1342 fieldValue = compiler.compileConstant(field); | 818 fieldValue = compiler.compileConstant(field); |
| 1343 } | 819 } |
| 1344 jsNewArguments.add(fieldValue); | 820 jsNewArguments.add(fieldValue); |
| 1345 }); | 821 }); |
| 1346 return jsNewArguments; | 822 return jsNewArguments; |
| 1347 } | 823 } |
| 1348 } | 824 } |
| OLD | NEW |