| 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 part of js_backend; | 5 part of js_backend; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * A function element that represents a closure call. The signature is copied | 8 * A function element that represents a closure call. The signature is copied |
| 9 * from the given element. | 9 * from the given element. |
| 10 */ | 10 */ |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 */ | 23 */ |
| 24 Element methodElement; | 24 Element methodElement; |
| 25 } | 25 } |
| 26 | 26 |
| 27 /** | 27 /** |
| 28 * A convenient type alias for some functions that emit keyed values. | 28 * A convenient type alias for some functions that emit keyed values. |
| 29 */ | 29 */ |
| 30 typedef void DefineStubFunction(String invocationName, jsAst.Expression value); | 30 typedef void DefineStubFunction(String invocationName, jsAst.Expression value); |
| 31 | 31 |
| 32 /** | 32 /** |
| 33 * [member] is a field (instance, static, or top level). |
| 34 * |
| 35 * [name] is the field name that the [Namer] has picked for this field's |
| 36 * storage, that is, the JavaScript property name. |
| 37 * |
| 38 * [accessorName] is the name of the accessor. For instance fields this is |
| 39 * mostly the same as [name] except when [member] is shadowing a field in its |
| 40 * superclass. For other fields, they are rarely the same. |
| 41 * |
| 42 * [needsGetter] and [needsSetter] represent if a getter or a setter |
| 43 * respectively is needed. There are many factors in this, for example, if the |
| 44 * accessor can be inlined. |
| 45 * |
| 46 * [needsCheckedSetter] indicates that a checked getter is needed, and in this |
| 47 * case, [needsSetter] is always false. [needsCheckedSetter] is only true when |
| 48 * type assertions are enabled (checked mode). |
| 49 */ |
| 50 typedef void AcceptField(VariableElement member, |
| 51 String name, |
| 52 String accessorName, |
| 53 bool needsGetter, |
| 54 bool needsSetter, |
| 55 bool needsCheckedSetter); |
| 56 |
| 57 /** |
| 33 * A data structure for collecting fragments of a class definition. | 58 * A data structure for collecting fragments of a class definition. |
| 34 */ | 59 */ |
| 35 class ClassBuilder { | 60 class ClassBuilder { |
| 36 final List<jsAst.Property> properties = <jsAst.Property>[]; | 61 final List<jsAst.Property> properties = <jsAst.Property>[]; |
| 37 | 62 |
| 38 /// Set to true by user if class is indistinguishable from its superclass. | 63 /// Set to true by user if class is indistinguishable from its superclass. |
| 39 bool isTrivial = false; | 64 bool isTrivial = false; |
| 40 | 65 |
| 41 // Has the same signature as [DefineStubFunction]. | 66 // Has the same signature as [DefineStubFunction]. |
| 42 void addProperty(String name, jsAst.Expression value) { | 67 void addProperty(String name, jsAst.Expression value) { |
| (...skipping 1142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1185 for (int i = 1; i <= signature.optionalParameterCount; i++) { | 1210 for (int i = 1; i <= signature.optionalParameterCount; i++) { |
| 1186 selectors.add(new Selector(SelectorKind.CALL, | 1211 selectors.add(new Selector(SelectorKind.CALL, |
| 1187 element.name, | 1212 element.name, |
| 1188 element.getLibrary(), | 1213 element.getLibrary(), |
| 1189 signature.requiredParameterCount + i, | 1214 signature.requiredParameterCount + i, |
| 1190 <SourceString>[])); | 1215 <SourceString>[])); |
| 1191 } | 1216 } |
| 1192 return selectors; | 1217 return selectors; |
| 1193 } | 1218 } |
| 1194 | 1219 |
| 1195 bool instanceFieldNeedsGetter(Element member) { | 1220 bool fieldNeedsGetter(VariableElement field) { |
| 1196 assert(member.isField()); | 1221 assert(field.isField()); |
| 1197 if (fieldAccessNeverThrows(member)) return false; | 1222 if (fieldAccessNeverThrows(field)) return false; |
| 1198 return backend.retainGetter(member) | 1223 return backend.retainGetter(field) |
| 1199 || compiler.codegenWorld.hasInvokedGetter(member, compiler); | 1224 || compiler.codegenWorld.hasInvokedGetter(field, compiler); |
| 1200 } | 1225 } |
| 1201 | 1226 |
| 1202 bool instanceFieldNeedsSetter(Element member) { | 1227 bool fieldNeedsSetter(VariableElement field) { |
| 1203 assert(member.isField()); | 1228 assert(field.isField()); |
| 1204 if (fieldAccessNeverThrows(member)) return false; | 1229 if (fieldAccessNeverThrows(field)) return false; |
| 1205 return (!member.modifiers.isFinalOrConst()) | 1230 return (!field.modifiers.isFinalOrConst()) |
| 1206 && (backend.retainSetter(member) | 1231 && (backend.retainSetter(field) |
| 1207 || compiler.codegenWorld.hasInvokedSetter(member, compiler)); | 1232 || compiler.codegenWorld.hasInvokedSetter(field, compiler)); |
| 1208 } | 1233 } |
| 1209 | 1234 |
| 1210 // We never access a field in a closure (a captured variable) without knowing | 1235 // We never access a field in a closure (a captured variable) without knowing |
| 1211 // that it is there. Therefore we don't need to use a getter (that will throw | 1236 // that it is there. Therefore we don't need to use a getter (that will throw |
| 1212 // if the getter method is missing), but can always access the field directly. | 1237 // if the getter method is missing), but can always access the field directly. |
| 1213 static bool fieldAccessNeverThrows(Element element) { | 1238 static bool fieldAccessNeverThrows(VariableElement field) { |
| 1214 return element is ClosureFieldElement; | 1239 return field is ClosureFieldElement; |
| 1215 } | 1240 } |
| 1216 | 1241 |
| 1217 String compiledFieldName(Element member) { | 1242 String compiledFieldName(Element member) { |
| 1218 assert(member.isField()); | 1243 assert(member.isField()); |
| 1219 return member.hasFixedBackendName() | 1244 return member.hasFixedBackendName() |
| 1220 ? member.fixedBackendName() | 1245 ? member.fixedBackendName() |
| 1221 : namer.getName(member); | 1246 : namer.getName(member); |
| 1222 } | 1247 } |
| 1223 | 1248 |
| 1224 /** | 1249 /** |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1478 for (ClassElement cls in typeChecks) { | 1503 for (ClassElement cls in typeChecks) { |
| 1479 for (TypeCheck check in typeChecks[cls]) { | 1504 for (TypeCheck check in typeChecks[cls]) { |
| 1480 result.add(backend.getImplementationClass(cls)); | 1505 result.add(backend.getImplementationClass(cls)); |
| 1481 break; | 1506 break; |
| 1482 } | 1507 } |
| 1483 } | 1508 } |
| 1484 return result; | 1509 return result; |
| 1485 } | 1510 } |
| 1486 | 1511 |
| 1487 /** | 1512 /** |
| 1488 * Documentation wanted -- johnniwinther | 1513 * Calls [addField] for each of the fields of [element]. |
| 1489 * | 1514 * |
| 1490 * Invariant: [classElement] must be a declaration element. | 1515 * [element] must be a [ClassElement] or a [LibraryElement]. |
| 1516 * |
| 1517 * If [element] is a [ClassElement], the static fields of the class are |
| 1518 * visited if [visitStatics] is true and the instance fields are visited if |
| 1519 * [visitStatics] is false. |
| 1520 * |
| 1521 * If [element] is a [LibraryElement], [visitStatics] must be true. |
| 1522 * |
| 1523 * When visiting the instance fields of a class, the fields of its superclass |
| 1524 * are also visited if the class is instantiated. |
| 1525 * |
| 1526 * Invariant: [element] must be a declaration element. |
| 1491 */ | 1527 */ |
| 1492 void visitClassFields(ClassElement classElement, | 1528 void visitFields(Element element, bool visitStatics, AcceptField f) { |
| 1493 bool visitStatics, | 1529 assert(invariant(element, element.isDeclaration)); |
| 1494 void addField(Element member, | 1530 |
| 1495 String name, | 1531 bool isClass = false; |
| 1496 String accessorName, | 1532 bool isLibrary = false; |
| 1497 bool needsGetter, | 1533 if (element.isClass()) { |
| 1498 bool needsSetter, | 1534 isClass = true; |
| 1499 bool needsCheckedSetter)) { | 1535 } else if (element.isLibrary()) { |
| 1500 assert(invariant(classElement, classElement.isDeclaration)); | 1536 isLibrary = true; |
| 1537 assert(invariant(element, visitStatics)); |
| 1538 } else { |
| 1539 throw new SpannableAssertionFailure( |
| 1540 element, 'Expected a ClassElement or a LibraryElement.'); |
| 1541 } |
| 1542 |
| 1501 // If the class is never instantiated we still need to set it up for | 1543 // If the class is never instantiated we still need to set it up for |
| 1502 // inheritance purposes, but we can simplify its JavaScript constructor. | 1544 // inheritance purposes, but we can simplify its JavaScript constructor. |
| 1503 bool isInstantiated = | 1545 bool isInstantiated = |
| 1504 compiler.codegenWorld.instantiatedClasses.contains(classElement); | 1546 compiler.codegenWorld.instantiatedClasses.contains(element); |
| 1505 | 1547 |
| 1506 void visitField(ClassElement enclosingClass, Element member) { | 1548 void visitField(Element holder, VariableElement field) { |
| 1507 assert(invariant(classElement, member.isDeclaration)); | 1549 assert(invariant(element, field.isDeclaration)); |
| 1508 LibraryElement library = member.getLibrary(); | 1550 SourceString name = field.name; |
| 1509 SourceString name = member.name; | |
| 1510 bool isPrivate = name.isPrivate(); | |
| 1511 | 1551 |
| 1512 // Keep track of whether or not we're dealing with a field mixin | 1552 // Keep track of whether or not we're dealing with a field mixin |
| 1513 // into a native class. | 1553 // into a native class. |
| 1514 bool isMixinNativeField = | 1554 bool isMixinNativeField = |
| 1515 classElement.isNative() && enclosingClass.isMixinApplication; | 1555 isClass && element.isNative() && holder.isMixinApplication; |
| 1516 | 1556 |
| 1517 // See if we can dynamically create getters and setters. | 1557 // See if we can dynamically create getters and setters. |
| 1518 // We can only generate getters and setters for [classElement] since | 1558 // We can only generate getters and setters for [element] since |
| 1519 // the fields of super classes could be overwritten with getters or | 1559 // the fields of super classes could be overwritten with getters or |
| 1520 // setters. | 1560 // setters. |
| 1521 bool needsGetter = false; | 1561 bool needsGetter = false; |
| 1522 bool needsSetter = false; | 1562 bool needsSetter = false; |
| 1523 // We need to name shadowed fields differently, so they don't clash with | 1563 // We need to name shadowed fields differently, so they don't clash with |
| 1524 // the non-shadowed field. | 1564 // the non-shadowed field. |
| 1525 bool isShadowed = false; | 1565 bool isShadowed = false; |
| 1526 if (isMixinNativeField || identical(enclosingClass, classElement)) { | 1566 if (isLibrary || isMixinNativeField || holder == element) { |
| 1527 needsGetter = instanceFieldNeedsGetter(member); | 1567 needsGetter = fieldNeedsGetter(field); |
| 1528 needsSetter = instanceFieldNeedsSetter(member); | 1568 needsSetter = fieldNeedsSetter(field); |
| 1529 } else { | 1569 } else { |
| 1530 isShadowed = classElement.isShadowedByField(member); | 1570 ClassElement cls = element; |
| 1571 isShadowed = cls.isShadowedByField(field); |
| 1531 } | 1572 } |
| 1532 | 1573 |
| 1533 if ((isInstantiated && !enclosingClass.isNative()) | 1574 if ((isInstantiated && !holder.isNative()) |
| 1534 || needsGetter | 1575 || needsGetter |
| 1535 || needsSetter) { | 1576 || needsSetter) { |
| 1536 String accessorName = isShadowed | 1577 String accessorName = isShadowed |
| 1537 ? namer.shadowedFieldName(member) | 1578 ? namer.shadowedFieldName(field) |
| 1538 : namer.getName(member); | 1579 : namer.getName(field); |
| 1539 String fieldName = member.hasFixedBackendName() | 1580 String fieldName = field.hasFixedBackendName() |
| 1540 ? member.fixedBackendName() | 1581 ? field.fixedBackendName() |
| 1541 : (isMixinNativeField ? member.name.slowToString() : accessorName); | 1582 : (isMixinNativeField ? name.slowToString() : accessorName); |
| 1542 bool needsCheckedSetter = false; | 1583 bool needsCheckedSetter = false; |
| 1543 if (needsSetter) { | 1584 if (compiler.enableTypeAssertions |
| 1544 if (compiler.enableTypeAssertions | 1585 && needsSetter |
| 1545 && canGenerateCheckedSetter(member)) { | 1586 && canGenerateCheckedSetter(field)) { |
| 1546 needsCheckedSetter = true; | 1587 needsCheckedSetter = true; |
| 1547 needsSetter = false; | 1588 needsSetter = false; |
| 1548 } | |
| 1549 } | 1589 } |
| 1550 // Getters and setters with suffixes will be generated dynamically. | 1590 // Getters and setters with suffixes will be generated dynamically. |
| 1551 addField(member, | 1591 f(field, fieldName, accessorName, needsGetter, needsSetter, |
| 1552 fieldName, | 1592 needsCheckedSetter); |
| 1553 accessorName, | |
| 1554 needsGetter, | |
| 1555 needsSetter, | |
| 1556 needsCheckedSetter); | |
| 1557 } | 1593 } |
| 1558 } | 1594 } |
| 1559 | 1595 |
| 1560 if (visitStatics) { | 1596 if (isLibrary) { |
| 1561 classElement.implementation.forEachStaticField(visitField); | 1597 LibraryElement library = element; |
| 1598 library.implementation.forEachLocalMember((Element member) { |
| 1599 if (member.isField()) visitField(library, member); |
| 1600 }); |
| 1601 } else if (visitStatics) { |
| 1602 ClassElement cls = element; |
| 1603 cls.implementation.forEachStaticField(visitField); |
| 1562 } else { | 1604 } else { |
| 1605 ClassElement cls = element; |
| 1563 // TODO(kasperl): We should make sure to only emit one version of | 1606 // TODO(kasperl): We should make sure to only emit one version of |
| 1564 // overridden fields. Right now, we rely on the ordering so the | 1607 // overridden fields. Right now, we rely on the ordering so the |
| 1565 // fields pulled in from mixins are replaced with the fields from | 1608 // fields pulled in from mixins are replaced with the fields from |
| 1566 // the class definition. | 1609 // the class definition. |
| 1567 | 1610 |
| 1568 // If a class is not instantiated then we add the field just so we can | 1611 // If a class is not instantiated then we add the field just so we can |
| 1569 // generate the field getter/setter dynamically. Since this is only | 1612 // generate the field getter/setter dynamically. Since this is only |
| 1570 // allowed on fields that are in [classElement] we don't need to visit | 1613 // allowed on fields that are in [element] we don't need to visit |
| 1571 // superclasses for non-instantiated classes. | 1614 // superclasses for non-instantiated classes. |
| 1572 classElement.implementation.forEachInstanceField( | 1615 cls.implementation.forEachInstanceField( |
| 1573 visitField, | 1616 visitField, includeSuperAndInjectedMembers: isInstantiated); |
| 1574 includeSuperAndInjectedMembers: isInstantiated); | |
| 1575 } | 1617 } |
| 1576 } | 1618 } |
| 1577 | 1619 |
| 1578 void generateGetter(Element member, String fieldName, String accessorName, | 1620 void generateGetter(Element member, String fieldName, String accessorName, |
| 1579 ClassBuilder builder) { | 1621 ClassBuilder builder) { |
| 1580 String getterName = namer.getterNameFromAccessorName(accessorName); | 1622 String getterName = namer.getterNameFromAccessorName(accessorName); |
| 1581 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) | 1623 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) |
| 1582 ? 'receiver' : 'this'; | 1624 ? 'receiver' : 'this'; |
| 1583 List<String> args = backend.isInterceptedMethod(member) | 1625 List<String> args = backend.isInterceptedMethod(member) |
| 1584 ? ['receiver'] | 1626 ? ['receiver'] |
| 1585 : []; | 1627 : []; |
| 1586 builder.addProperty(getterName, | 1628 builder.addProperty(getterName, |
| 1587 js.fun(args, js.return_(js('$receiver.$fieldName')))); | 1629 js.fun(args, js.return_(js('$receiver.$fieldName')))); |
| 1588 } | 1630 } |
| 1589 | 1631 |
| 1590 void generateSetter(Element member, String fieldName, String accessorName, | 1632 void generateSetter(Element member, String fieldName, String accessorName, |
| 1591 ClassBuilder builder) { | 1633 ClassBuilder builder) { |
| 1592 String setterName = namer.setterNameFromAccessorName(accessorName); | 1634 String setterName = namer.setterNameFromAccessorName(accessorName); |
| 1593 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) | 1635 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) |
| 1594 ? 'receiver' : 'this'; | 1636 ? 'receiver' : 'this'; |
| 1595 List<String> args = backend.isInterceptedMethod(member) | 1637 List<String> args = backend.isInterceptedMethod(member) |
| 1596 ? ['receiver', 'v'] | 1638 ? ['receiver', 'v'] |
| 1597 : ['v']; | 1639 : ['v']; |
| 1598 builder.addProperty(setterName, | 1640 builder.addProperty(setterName, |
| 1599 js.fun(args, js('$receiver.$fieldName = v'))); | 1641 js.fun(args, js('$receiver.$fieldName = v'))); |
| 1600 } | 1642 } |
| 1601 | 1643 |
| 1602 bool canGenerateCheckedSetter(Element member) { | 1644 bool canGenerateCheckedSetter(VariableElement field) { |
| 1603 // We never generate accessors for top-level/static fields. | 1645 // We never generate accessors for top-level/static fields. |
| 1604 if (!member.isInstanceMember()) return false; | 1646 if (!field.isInstanceMember()) return false; |
| 1605 DartType type = member.computeType(compiler).unalias(compiler); | 1647 DartType type = field.computeType(compiler).unalias(compiler); |
| 1606 if (type.element.isTypeVariable() || | 1648 if (type.element.isTypeVariable() || |
| 1607 (type is FunctionType && type.containsTypeVariables) || | 1649 (type is FunctionType && type.containsTypeVariables) || |
| 1608 type.element == compiler.dynamicClass || | 1650 type.element == compiler.dynamicClass || |
| 1609 type.element == compiler.objectClass) { | 1651 type.element == compiler.objectClass) { |
| 1610 // TODO(ngeoffray): Support type checks on type parameters. | 1652 // TODO(ngeoffray): Support type checks on type parameters. |
| 1611 return false; | 1653 return false; |
| 1612 } | 1654 } |
| 1613 return true; | 1655 return true; |
| 1614 } | 1656 } |
| 1615 | 1657 |
| 1616 void generateCheckedSetter(Element member, | 1658 void generateCheckedSetter(Element member, |
| 1617 String fieldName, | 1659 String fieldName, |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1668 } else { | 1710 } else { |
| 1669 previousName = mangledGlobalFieldNames.putIfAbsent( | 1711 previousName = mangledGlobalFieldNames.putIfAbsent( |
| 1670 accessorName, | 1712 accessorName, |
| 1671 () => memberName); | 1713 () => memberName); |
| 1672 } | 1714 } |
| 1673 assert(invariant(member, previousName == memberName, | 1715 assert(invariant(member, previousName == memberName, |
| 1674 message: '$previousName != ${memberName}')); | 1716 message: '$previousName != ${memberName}')); |
| 1675 } | 1717 } |
| 1676 | 1718 |
| 1677 /// Returns `true` if fields added. | 1719 /// Returns `true` if fields added. |
| 1678 bool emitClassFields(ClassElement classElement, | 1720 bool emitFields(Element element, |
| 1679 ClassBuilder builder, | 1721 ClassBuilder builder, |
| 1680 String superName, | 1722 String superName, |
| 1681 { bool classIsNative: false, | 1723 { bool classIsNative: false, |
| 1682 bool emitStatics: false, | 1724 bool emitStatics: false, |
| 1683 bool onlyForRti: false }) { | 1725 bool onlyForRti: false }) { |
| 1684 assert(!emitStatics || !onlyForRti); | 1726 assert(!emitStatics || !onlyForRti); |
| 1727 bool isClass = false; |
| 1728 bool isLibrary = false; |
| 1729 if (element.isClass()) { |
| 1730 isClass = true; |
| 1731 } else if (element.isLibrary()) { |
| 1732 isLibrary = false; |
| 1733 assert(invariant(element, emitStatics)); |
| 1734 } else { |
| 1735 throw new SpannableAssertionFailure( |
| 1736 element, 'Must be a ClassElement or a LibraryElement'); |
| 1737 } |
| 1685 StringBuffer buffer = new StringBuffer(); | 1738 StringBuffer buffer = new StringBuffer(); |
| 1686 if (emitStatics) { | 1739 if (emitStatics) { |
| 1687 assert(invariant(classElement, superName == null, message: superName)); | 1740 assert(invariant(element, superName == null, message: superName)); |
| 1688 } else { | 1741 } else { |
| 1689 assert(invariant(classElement, superName != null)); | 1742 assert(invariant(element, superName != null)); |
| 1690 String nativeName = | 1743 String nativeName = |
| 1691 namer.getPrimitiveInterceptorRuntimeName(classElement); | 1744 namer.getPrimitiveInterceptorRuntimeName(element); |
| 1692 if (nativeName != null) { | 1745 if (nativeName != null) { |
| 1693 buffer.write('$nativeName/'); | 1746 buffer.write('$nativeName/'); |
| 1694 } | 1747 } |
| 1695 buffer.write('$superName;'); | 1748 buffer.write('$superName;'); |
| 1696 } | 1749 } |
| 1697 int bufferClassLength = buffer.length; | 1750 int bufferClassLength = buffer.length; |
| 1698 | 1751 |
| 1699 String separator = ''; | 1752 String separator = ''; |
| 1700 | 1753 |
| 1701 var fieldMetadata = []; | 1754 var fieldMetadata = []; |
| 1702 bool hasMetadata = false; | 1755 bool hasMetadata = false; |
| 1703 | 1756 |
| 1704 if (!onlyForRti) { | 1757 if (!onlyForRti) { |
| 1705 visitClassFields(classElement, emitStatics, | 1758 visitFields(element, emitStatics, |
| 1706 (Element member, | 1759 (VariableElement field, |
| 1707 String name, | 1760 String name, |
| 1708 String accessorName, | 1761 String accessorName, |
| 1709 bool needsGetter, | 1762 bool needsGetter, |
| 1710 bool needsSetter, | 1763 bool needsSetter, |
| 1711 bool needsCheckedSetter) { | 1764 bool needsCheckedSetter) { |
| 1712 // Ignore needsCheckedSetter - that is handled below. | 1765 // Ignore needsCheckedSetter - that is handled below. |
| 1713 bool needsAccessor = (needsGetter || needsSetter); | 1766 bool needsAccessor = (needsGetter || needsSetter); |
| 1714 // We need to output the fields for non-native classes so we can auto- | 1767 // We need to output the fields for non-native classes so we can auto- |
| 1715 // generate the constructor. For native classes there are no | 1768 // generate the constructor. For native classes there are no |
| 1716 // constructors, so we don't need the fields unless we are generating | 1769 // constructors, so we don't need the fields unless we are generating |
| 1717 // accessors at runtime. | 1770 // accessors at runtime. |
| 1718 if (!classIsNative || needsAccessor) { | 1771 if (!classIsNative || needsAccessor) { |
| 1719 buffer.write(separator); | 1772 buffer.write(separator); |
| 1720 separator = ','; | 1773 separator = ','; |
| 1721 var metadata = buildMetadataFunction(member); | 1774 var metadata = buildMetadataFunction(field); |
| 1722 if (metadata != null) { | 1775 if (metadata != null) { |
| 1723 hasMetadata = true; | 1776 hasMetadata = true; |
| 1724 } else { | 1777 } else { |
| 1725 metadata = new jsAst.LiteralNull(); | 1778 metadata = new jsAst.LiteralNull(); |
| 1726 } | 1779 } |
| 1727 fieldMetadata.add(metadata); | 1780 fieldMetadata.add(metadata); |
| 1728 recordMangledField(member, accessorName, member.name.slowToString()); | 1781 recordMangledField(field, accessorName, field.name.slowToString()); |
| 1729 if (!needsAccessor) { | 1782 if (!needsAccessor) { |
| 1730 // Emit field for constructor generation. | 1783 // Emit field for constructor generation. |
| 1731 assert(!classIsNative); | 1784 assert(!classIsNative); |
| 1732 buffer.write(name); | 1785 buffer.write(name); |
| 1733 } else { | 1786 } else { |
| 1734 // Emit (possibly renaming) field name so we can add accessors at | 1787 // Emit (possibly renaming) field name so we can add accessors at |
| 1735 // runtime. | 1788 // runtime. |
| 1736 buffer.write(accessorName); | 1789 buffer.write(accessorName); |
| 1737 if (name != accessorName) { | 1790 if (name != accessorName) { |
| 1738 buffer.write(':$name'); | 1791 buffer.write(':$name'); |
| 1739 // Only the native classes can have renaming accessors. | 1792 // Only the native classes can have renaming accessors. |
| 1740 assert(classIsNative); | 1793 assert(classIsNative); |
| 1741 } | 1794 } |
| 1742 | 1795 |
| 1743 int getterCode = 0; | 1796 int getterCode = 0; |
| 1744 if (needsGetter) { | 1797 if (needsGetter) { |
| 1745 // 01: function() { return this.field; } | 1798 if (field.isInstanceMember()) { |
| 1746 // 10: function(receiver) { return receiver.field; } | 1799 // 01: function() { return this.field; } |
| 1747 // 11: function(receiver) { return this.field; } | 1800 // 10: function(receiver) { return receiver.field; } |
| 1748 if (member.isInstanceMember()) { | 1801 // 11: function(receiver) { return this.field; } |
| 1749 getterCode += backend.fieldHasInterceptedGetter(member) ? 2 : 0; | 1802 getterCode += backend.fieldHasInterceptedGetter(field) ? 2 : 0; |
| 1750 getterCode += backend.isInterceptorClass(classElement) ? 0 : 1; | 1803 getterCode += backend.isInterceptorClass(element) ? 0 : 1; |
| 1751 // TODO(sra): 'isInterceptorClass' might not be the correct | 1804 // TODO(sra): 'isInterceptorClass' might not be the correct test |
| 1752 // test for methods forced to use the interceptor convention | 1805 // for methods forced to use the interceptor convention because |
| 1753 // because the method's class was elsewhere mixed-in to an | 1806 // the method's class was elsewhere mixed-in to an interceptor. |
| 1754 // interceptor. | 1807 assert(!field.isInstanceMember() || getterCode != 0); |
| 1755 assert(getterCode != 0); | |
| 1756 } else { | 1808 } else { |
| 1757 getterCode = 1; | 1809 getterCode = 1; |
| 1758 } | 1810 } |
| 1759 } | 1811 } |
| 1760 int setterCode = 0; | 1812 int setterCode = 0; |
| 1761 if (needsSetter) { | 1813 if (needsSetter) { |
| 1762 // 01: function(value) { this.field = value; } | 1814 if (field.isInstanceMember()) { |
| 1763 // 10: function(receiver, value) { receiver.field = value; } | 1815 // 01: function(value) { this.field = value; } |
| 1764 // 11: function(receiver, value) { this.field = value; } | 1816 // 10: function(receiver, value) { receiver.field = value; } |
| 1765 if (member.isInstanceMember()) { | 1817 // 11: function(receiver, value) { this.field = value; } |
| 1766 setterCode += backend.fieldHasInterceptedSetter(member) ? 2 : 0; | 1818 setterCode += backend.fieldHasInterceptedSetter(field) ? 2 : 0; |
| 1767 setterCode += backend.isInterceptorClass(classElement) ? 0 : 1; | 1819 setterCode += backend.isInterceptorClass(element) ? 0 : 1; |
| 1768 assert(setterCode != 0); | 1820 assert(!field.isInstanceMember() || setterCode != 0); |
| 1769 } else { | 1821 } else { |
| 1770 setterCode = 1; | 1822 setterCode = 1; |
| 1771 } | 1823 } |
| 1772 } | 1824 } |
| 1773 int code = getterCode + (setterCode << 2); | 1825 int code = getterCode + (setterCode << 2); |
| 1774 if (code == 0) { | 1826 if (code == 0) { |
| 1775 compiler.reportInternalError( | 1827 compiler.reportInternalError( |
| 1776 member, 'Internal error: code is 0 ($classElement/$member)'); | 1828 field, 'Internal error: code is 0 ($element/$field)'); |
| 1777 } else { | 1829 } else { |
| 1778 buffer.write(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]); | 1830 buffer.write(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]); |
| 1779 } | 1831 } |
| 1780 } | 1832 } |
| 1781 } | 1833 } |
| 1782 }); | 1834 }); |
| 1783 } | 1835 } |
| 1784 | 1836 |
| 1785 bool fieldsAdded = buffer.length > bufferClassLength; | 1837 bool fieldsAdded = buffer.length > bufferClassLength; |
| 1786 String compactClassData = buffer.toString(); | 1838 String compactClassData = buffer.toString(); |
| 1787 jsAst.Expression classDataNode = js.string(compactClassData); | 1839 jsAst.Expression classDataNode = js.string(compactClassData); |
| 1788 if (hasMetadata) { | 1840 if (hasMetadata) { |
| 1789 fieldMetadata.insert(0, classDataNode); | 1841 fieldMetadata.insert(0, classDataNode); |
| 1790 classDataNode = new jsAst.ArrayInitializer.from(fieldMetadata); | 1842 classDataNode = new jsAst.ArrayInitializer.from(fieldMetadata); |
| 1791 } | 1843 } |
| 1792 builder.addProperty('', classDataNode); | 1844 builder.addProperty('', classDataNode); |
| 1793 return fieldsAdded; | 1845 return fieldsAdded; |
| 1794 } | 1846 } |
| 1795 | 1847 |
| 1796 void emitClassGettersSetters(ClassElement classElement, | 1848 void emitClassGettersSetters(ClassElement classElement, |
| 1797 ClassBuilder builder) { | 1849 ClassBuilder builder) { |
| 1798 | 1850 |
| 1799 visitClassFields(classElement, false, | 1851 visitFields(classElement, false, |
| 1800 (Element member, | 1852 (VariableElement member, |
| 1801 String name, | 1853 String name, |
| 1802 String accessorName, | 1854 String accessorName, |
| 1803 bool needsGetter, | 1855 bool needsGetter, |
| 1804 bool needsSetter, | 1856 bool needsSetter, |
| 1805 bool needsCheckedSetter) { | 1857 bool needsCheckedSetter) { |
| 1806 compiler.withCurrentElement(member, () { | 1858 compiler.withCurrentElement(member, () { |
| 1807 if (needsCheckedSetter) { | 1859 if (needsCheckedSetter) { |
| 1808 assert(!needsSetter); | 1860 assert(!needsSetter); |
| 1809 generateCheckedSetter(member, name, accessorName, builder); | 1861 generateCheckedSetter(member, name, accessorName, builder); |
| 1810 } | 1862 } |
| 1811 if (!getterAndSetterCanBeImplementedByFieldSpec) { | 1863 if (!getterAndSetterCanBeImplementedByFieldSpec) { |
| 1812 if (needsGetter) { | 1864 if (needsGetter) { |
| 1813 generateGetter(member, name, accessorName, builder); | 1865 generateGetter(member, name, accessorName, builder); |
| 1814 } | 1866 } |
| 1815 if (needsSetter) { | 1867 if (needsSetter) { |
| (...skipping 29 matching lines...) Expand all Loading... |
| 1845 if (classElement.isMixinApplication) { | 1897 if (classElement.isMixinApplication) { |
| 1846 String mixinName = namer.getName(computeMixinClass(classElement)); | 1898 String mixinName = namer.getName(computeMixinClass(classElement)); |
| 1847 superName = '$superName+$mixinName'; | 1899 superName = '$superName+$mixinName'; |
| 1848 needsMixinSupport = true; | 1900 needsMixinSupport = true; |
| 1849 } | 1901 } |
| 1850 | 1902 |
| 1851 ClassBuilder builder = new ClassBuilder(); | 1903 ClassBuilder builder = new ClassBuilder(); |
| 1852 emitClassConstructor(classElement, builder); | 1904 emitClassConstructor(classElement, builder); |
| 1853 emitSuper(superName, builder); | 1905 emitSuper(superName, builder); |
| 1854 emitRuntimeName(runtimeName, builder); | 1906 emitRuntimeName(runtimeName, builder); |
| 1855 emitClassFields(classElement, builder, superName, onlyForRti: onlyForRti); | 1907 emitFields(classElement, builder, superName, onlyForRti: onlyForRti); |
| 1856 emitClassGettersSetters(classElement, builder); | 1908 emitClassGettersSetters(classElement, builder); |
| 1857 if (!classElement.isMixinApplication) { | 1909 if (!classElement.isMixinApplication) { |
| 1858 emitInstanceMembers(classElement, builder); | 1910 emitInstanceMembers(classElement, builder); |
| 1859 } | 1911 } |
| 1860 emitIsTests(classElement, builder); | 1912 emitIsTests(classElement, builder); |
| 1861 | 1913 |
| 1862 emitClassBuilderWithReflectionData( | 1914 emitClassBuilderWithReflectionData( |
| 1863 className, classElement, builder, buffer); | 1915 className, classElement, builder, buffer); |
| 1864 } | 1916 } |
| 1865 | 1917 |
| 1866 void emitClassBuilderWithReflectionData(String className, | 1918 void emitClassBuilderWithReflectionData(String className, |
| 1867 ClassElement classElement, | 1919 ClassElement classElement, |
| 1868 ClassBuilder builder, | 1920 ClassBuilder builder, |
| 1869 CodeBuffer buffer) { | 1921 CodeBuffer buffer) { |
| 1870 var metadata = buildMetadataFunction(classElement); | 1922 var metadata = buildMetadataFunction(classElement); |
| 1871 if (metadata != null) { | 1923 if (metadata != null) { |
| 1872 builder.addProperty("@", metadata); | 1924 builder.addProperty("@", metadata); |
| 1873 } | 1925 } |
| 1874 | 1926 |
| 1875 List<CodeBuffer> classBuffers = elementBuffers[classElement]; | 1927 List<CodeBuffer> classBuffers = elementBuffers[classElement]; |
| 1876 if (classBuffers == null) { | 1928 if (classBuffers == null) { |
| 1877 classBuffers = []; | 1929 classBuffers = []; |
| 1878 } else { | 1930 } else { |
| 1879 elementBuffers.remove(classElement); | 1931 elementBuffers.remove(classElement); |
| 1880 } | 1932 } |
| 1881 CodeBuffer statics = new CodeBuffer(); | 1933 CodeBuffer statics = new CodeBuffer(); |
| 1882 statics.write('{$n'); | 1934 statics.write('{$n'); |
| 1883 bool hasStatics = false; | 1935 bool hasStatics = false; |
| 1884 ClassBuilder staticsBuilder = new ClassBuilder(); | 1936 ClassBuilder staticsBuilder = new ClassBuilder(); |
| 1885 if (emitClassFields( | 1937 if (emitFields(classElement, staticsBuilder, null, emitStatics: true)) { |
| 1886 classElement, staticsBuilder, null, emitStatics: true)) { | |
| 1887 hasStatics = true; | 1938 hasStatics = true; |
| 1888 statics.write('"":$_'); | 1939 statics.write('"":$_'); |
| 1889 statics.write( | 1940 statics.write( |
| 1890 jsAst.prettyPrint(staticsBuilder.properties.single.value, compiler)); | 1941 jsAst.prettyPrint(staticsBuilder.properties.single.value, compiler)); |
| 1891 statics.write(',$n'); | 1942 statics.write(',$n'); |
| 1892 } | 1943 } |
| 1893 for (CodeBuffer classBuffer in classBuffers) { | 1944 for (CodeBuffer classBuffer in classBuffers) { |
| 1894 // TODO(ahe): What about deferred? | 1945 // TODO(ahe): What about deferred? |
| 1895 if (classBuffer != null) { | 1946 if (classBuffer != null) { |
| 1896 hasStatics = true; | 1947 hasStatics = true; |
| (...skipping 1707 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3604 // After this assignment we will produce invalid JavaScript code if we use | 3655 // After this assignment we will produce invalid JavaScript code if we use |
| 3605 // the classesCollector variable. | 3656 // the classesCollector variable. |
| 3606 classesCollector = 'classesCollector should not be used from now on'; | 3657 classesCollector = 'classesCollector should not be used from now on'; |
| 3607 | 3658 |
| 3608 if (!elementBuffers.isEmpty) { | 3659 if (!elementBuffers.isEmpty) { |
| 3609 var oldClassesCollector = classesCollector; | 3660 var oldClassesCollector = classesCollector; |
| 3610 classesCollector = r"$$"; | 3661 classesCollector = r"$$"; |
| 3611 if (compiler.enableMinification) { | 3662 if (compiler.enableMinification) { |
| 3612 mainBuffer.write(';'); | 3663 mainBuffer.write(';'); |
| 3613 } | 3664 } |
| 3665 |
| 3666 for (Element element in elementBuffers.keys) { |
| 3667 // TODO(ahe): Should iterate over all libraries. Otherwise, we will |
| 3668 // not see libraries that only have fields. |
| 3669 if (element.isLibrary()) { |
| 3670 LibraryElement library = element; |
| 3671 ClassBuilder builder = new ClassBuilder(); |
| 3672 if (emitFields(library, builder, null, emitStatics: true)) { |
| 3673 List<CodeBuffer> buffers = elementBuffers[library]; |
| 3674 var buffer = buffers[0]; |
| 3675 if (buffer == null) { |
| 3676 buffers[0] = buffer = new CodeBuffer(); |
| 3677 } |
| 3678 for (jsAst.Property property in builder.properties) { |
| 3679 if (!buffer.isEmpty) buffer.write(',$n'); |
| 3680 buffer.addBuffer(jsAst.prettyPrint(property, compiler)); |
| 3681 } |
| 3682 } |
| 3683 } |
| 3684 } |
| 3685 |
| 3614 if (!mangledFieldNames.isEmpty) { | 3686 if (!mangledFieldNames.isEmpty) { |
| 3615 var keys = mangledFieldNames.keys.toList(); | 3687 var keys = mangledFieldNames.keys.toList(); |
| 3616 keys.sort(); | 3688 keys.sort(); |
| 3617 var properties = []; | 3689 var properties = []; |
| 3618 for (String key in keys) { | 3690 for (String key in keys) { |
| 3619 var value = js.string('${mangledFieldNames[key]}'); | 3691 var value = js.string('${mangledFieldNames[key]}'); |
| 3620 properties.add(new jsAst.Property(js.string(key), value)); | 3692 properties.add(new jsAst.Property(js.string(key), value)); |
| 3621 } | 3693 } |
| 3622 var map = new jsAst.ObjectInitializer(properties); | 3694 var map = new jsAst.ObjectInitializer(properties); |
| 3623 mainBuffer.write( | 3695 mainBuffer.write( |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3900 var mangledNames = init.mangledNames; | 3972 var mangledNames = init.mangledNames; |
| 3901 var mangledGlobalNames = init.mangledGlobalNames; | 3973 var mangledGlobalNames = init.mangledGlobalNames; |
| 3902 var hasOwnProperty = Object.prototype.hasOwnProperty; | 3974 var hasOwnProperty = Object.prototype.hasOwnProperty; |
| 3903 var length = reflectionData.length; | 3975 var length = reflectionData.length; |
| 3904 for (var i = 0; i < length; i++) { | 3976 for (var i = 0; i < length; i++) { |
| 3905 var data = reflectionData[i]; | 3977 var data = reflectionData[i]; |
| 3906 var name = data[0]; | 3978 var name = data[0]; |
| 3907 var uri = data[1]; | 3979 var uri = data[1]; |
| 3908 var metadata = data[2]; | 3980 var metadata = data[2]; |
| 3909 var descriptor = data[3]; | 3981 var descriptor = data[3]; |
| 3982 var fields = descriptor && descriptor[""]; |
| 3910 var classes = []; | 3983 var classes = []; |
| 3911 var functions = []; | 3984 var functions = []; |
| 3912 function processStatics(descriptor) { | 3985 function processStatics(descriptor) { |
| 3913 for (var property in descriptor) { | 3986 for (var property in descriptor) { |
| 3914 if (!hasOwnProperty.call(descriptor, property)) continue; | 3987 if (!hasOwnProperty.call(descriptor, property)) continue; |
| 3915 if (property === "") continue; | 3988 if (property === "") continue; |
| 3916 var element = descriptor[property]; | 3989 var element = descriptor[property]; |
| 3917 var firstChar = property.substring(0, 1); | 3990 var firstChar = property.substring(0, 1); |
| 3918 var previousProperty; | 3991 var previousProperty; |
| 3919 if (firstChar === "+") { | 3992 if (firstChar === "+") { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 3940 } else { | 4013 } else { |
| 3941 newDesc[previousProp = prop] = element[prop]; | 4014 newDesc[previousProp = prop] = element[prop]; |
| 3942 } | 4015 } |
| 3943 } | 4016 } |
| 3944 $classesCollector[property] = newDesc; | 4017 $classesCollector[property] = newDesc; |
| 3945 classes.push(property); | 4018 classes.push(property); |
| 3946 } | 4019 } |
| 3947 } | 4020 } |
| 3948 } | 4021 } |
| 3949 processStatics(descriptor); | 4022 processStatics(descriptor); |
| 3950 libraries.push([name, uri, classes, functions, metadata]); | 4023 libraries.push([name, uri, classes, functions, metadata, fields]); |
| 3951 } | 4024 } |
| 3952 })'''; | 4025 })'''; |
| 3953 } | 4026 } |
| 3954 } | 4027 } |
| 3955 | 4028 |
| 3956 const String GENERATED_BY = """ | 4029 const String GENERATED_BY = """ |
| 3957 // Generated by dart2js, the Dart to JavaScript compiler. | 4030 // Generated by dart2js, the Dart to JavaScript compiler. |
| 3958 """; | 4031 """; |
| 3959 | 4032 |
| 3960 const String HOOKS_API_USAGE = """ | 4033 const String HOOKS_API_USAGE = """ |
| 3961 // The code supports the following hooks: | 4034 // The code supports the following hooks: |
| 3962 // dartPrint(message) - if this function is defined it is called | 4035 // dartPrint(message) - if this function is defined it is called |
| 3963 // instead of the Dart [print] method. | 4036 // instead of the Dart [print] method. |
| 3964 // dartMainRunner(main) - if this function is defined, the Dart [main] | 4037 // dartMainRunner(main) - if this function is defined, the Dart [main] |
| 3965 // method will not be invoked directly. | 4038 // method will not be invoked directly. |
| 3966 // Instead, a closure that will invoke [main] is | 4039 // Instead, a closure that will invoke [main] is |
| 3967 // passed to [dartMainRunner]. | 4040 // passed to [dartMainRunner]. |
| 3968 """; | 4041 """; |
| OLD | NEW |