| 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 1141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1184 for (int i = 1; i <= signature.optionalParameterCount; i++) { | 1209 for (int i = 1; i <= signature.optionalParameterCount; i++) { |
| 1185 selectors.add(new Selector(SelectorKind.CALL, | 1210 selectors.add(new Selector(SelectorKind.CALL, |
| 1186 element.name, | 1211 element.name, |
| 1187 element.getLibrary(), | 1212 element.getLibrary(), |
| 1188 signature.requiredParameterCount + i, | 1213 signature.requiredParameterCount + i, |
| 1189 <SourceString>[])); | 1214 <SourceString>[])); |
| 1190 } | 1215 } |
| 1191 return selectors; | 1216 return selectors; |
| 1192 } | 1217 } |
| 1193 | 1218 |
| 1194 bool instanceFieldNeedsGetter(Element member) { | 1219 bool fieldNeedsGetter(VariableElement field) { |
| 1195 assert(member.isField()); | 1220 assert(field.isField()); |
| 1196 if (fieldAccessNeverThrows(member)) return false; | 1221 if (fieldAccessNeverThrows(field)) return false; |
| 1197 return backend.retainGetter(member) | 1222 return backend.retainGetter(field) |
| 1198 || compiler.codegenWorld.hasInvokedGetter(member, compiler); | 1223 || compiler.codegenWorld.hasInvokedGetter(field, compiler); |
| 1199 } | 1224 } |
| 1200 | 1225 |
| 1201 bool instanceFieldNeedsSetter(Element member) { | 1226 bool fieldNeedsSetter(VariableElement field) { |
| 1202 assert(member.isField()); | 1227 assert(field.isField()); |
| 1203 if (fieldAccessNeverThrows(member)) return false; | 1228 if (fieldAccessNeverThrows(field)) return false; |
| 1204 return (!member.modifiers.isFinalOrConst()) | 1229 return (!field.modifiers.isFinalOrConst()) |
| 1205 && (backend.retainSetter(member) | 1230 && (backend.retainSetter(field) |
| 1206 || compiler.codegenWorld.hasInvokedSetter(member, compiler)); | 1231 || compiler.codegenWorld.hasInvokedSetter(field, compiler)); |
| 1207 } | 1232 } |
| 1208 | 1233 |
| 1209 // We never access a field in a closure (a captured variable) without knowing | 1234 // We never access a field in a closure (a captured variable) without knowing |
| 1210 // that it is there. Therefore we don't need to use a getter (that will throw | 1235 // that it is there. Therefore we don't need to use a getter (that will throw |
| 1211 // if the getter method is missing), but can always access the field directly. | 1236 // if the getter method is missing), but can always access the field directly. |
| 1212 static bool fieldAccessNeverThrows(Element element) { | 1237 static bool fieldAccessNeverThrows(VariableElement field) { |
| 1213 return element is ClosureFieldElement; | 1238 return field is ClosureFieldElement; |
| 1214 } | 1239 } |
| 1215 | 1240 |
| 1216 String compiledFieldName(Element member) { | 1241 String compiledFieldName(Element member) { |
| 1217 assert(member.isField()); | 1242 assert(member.isField()); |
| 1218 return member.hasFixedBackendName() | 1243 return member.hasFixedBackendName() |
| 1219 ? member.fixedBackendName() | 1244 ? member.fixedBackendName() |
| 1220 : namer.getName(member); | 1245 : namer.getName(member); |
| 1221 } | 1246 } |
| 1222 | 1247 |
| 1223 /** | 1248 /** |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1477 for (ClassElement cls in typeChecks) { | 1502 for (ClassElement cls in typeChecks) { |
| 1478 for (TypeCheck check in typeChecks[cls]) { | 1503 for (TypeCheck check in typeChecks[cls]) { |
| 1479 result.add(backend.getImplementationClass(cls)); | 1504 result.add(backend.getImplementationClass(cls)); |
| 1480 break; | 1505 break; |
| 1481 } | 1506 } |
| 1482 } | 1507 } |
| 1483 return result; | 1508 return result; |
| 1484 } | 1509 } |
| 1485 | 1510 |
| 1486 /** | 1511 /** |
| 1487 * Documentation wanted -- johnniwinther | 1512 * Calls [addField] for each of the fields of [element]. |
| 1488 * | 1513 * |
| 1489 * Invariant: [classElement] must be a declaration element. | 1514 * [element] must be a [ClassElement] or a [LibraryElement]. |
| 1515 * |
| 1516 * If [element] is a [ClassElement], the static fields of the class are |
| 1517 * visited if [visitStatics] is true and the instance fields are visited if |
| 1518 * [visitStatics] is false. |
| 1519 * |
| 1520 * If [element] is a [LibraryElement], [visitStatics] must be true. |
| 1521 * |
| 1522 * When visiting the instance fields of a class, the fields of its superclass |
| 1523 * are also visited if the class is instantiated. |
| 1524 * |
| 1525 * Invariant: [element] must be a declaration element. |
| 1490 */ | 1526 */ |
| 1491 void visitClassFields(ClassElement classElement, | 1527 void visitFields(Element element, bool visitStatics, AcceptField f) { |
| 1492 bool visitStatics, | 1528 assert(invariant(element, element.isDeclaration)); |
| 1493 void addField(Element member, | 1529 |
| 1494 String name, | 1530 bool isClass = false; |
| 1495 String accessorName, | 1531 bool isLibrary = false; |
| 1496 bool needsGetter, | 1532 if (element is ClassElement) { |
| 1497 bool needsSetter, | 1533 isClass = true; |
| 1498 bool needsCheckedSetter)) { | 1534 } else if (element is LibraryElement) { |
| 1499 assert(invariant(classElement, classElement.isDeclaration)); | 1535 isLibrary = true; |
| 1536 assert(invariant(element, visitStatics)); |
| 1537 } else { |
| 1538 throw new SpannableAssertionFailure( |
| 1539 element, 'Expected a ClassElement or a LibraryElement.'); |
| 1540 } |
| 1541 |
| 1500 // If the class is never instantiated we still need to set it up for | 1542 // If the class is never instantiated we still need to set it up for |
| 1501 // inheritance purposes, but we can simplify its JavaScript constructor. | 1543 // inheritance purposes, but we can simplify its JavaScript constructor. |
| 1502 bool isInstantiated = | 1544 bool isInstantiated = |
| 1503 compiler.codegenWorld.instantiatedClasses.contains(classElement); | 1545 compiler.codegenWorld.instantiatedClasses.contains(element); |
| 1504 | 1546 |
| 1505 void visitField(ClassElement enclosingClass, Element member) { | 1547 void visitField(Element holder, VariableElement field) { |
| 1506 assert(invariant(classElement, member.isDeclaration)); | 1548 assert(invariant(element, field.isDeclaration)); |
| 1507 LibraryElement library = member.getLibrary(); | 1549 SourceString name = field.name; |
| 1508 SourceString name = member.name; | |
| 1509 bool isPrivate = name.isPrivate(); | |
| 1510 | 1550 |
| 1511 // Keep track of whether or not we're dealing with a field mixin | 1551 // Keep track of whether or not we're dealing with a field mixin |
| 1512 // into a native class. | 1552 // into a native class. |
| 1513 bool isMixinNativeField = | 1553 bool isMixinNativeField = |
| 1514 classElement.isNative() && enclosingClass.isMixinApplication; | 1554 isClass && element.isNative() && holder.isMixinApplication; |
| 1515 | 1555 |
| 1516 // See if we can dynamically create getters and setters. | 1556 // See if we can dynamically create getters and setters. |
| 1517 // We can only generate getters and setters for [classElement] since | 1557 // We can only generate getters and setters for [element] since |
| 1518 // the fields of super classes could be overwritten with getters or | 1558 // the fields of super classes could be overwritten with getters or |
| 1519 // setters. | 1559 // setters. |
| 1520 bool needsGetter = false; | 1560 bool needsGetter = false; |
| 1521 bool needsSetter = false; | 1561 bool needsSetter = false; |
| 1522 // We need to name shadowed fields differently, so they don't clash with | 1562 // We need to name shadowed fields differently, so they don't clash with |
| 1523 // the non-shadowed field. | 1563 // the non-shadowed field. |
| 1524 bool isShadowed = false; | 1564 bool isShadowed = false; |
| 1525 if (isMixinNativeField || identical(enclosingClass, classElement)) { | 1565 if (isLibrary || isMixinNativeField || holder == element) { |
| 1526 needsGetter = instanceFieldNeedsGetter(member); | 1566 needsGetter = fieldNeedsGetter(field); |
| 1527 needsSetter = instanceFieldNeedsSetter(member); | 1567 needsSetter = fieldNeedsSetter(field); |
| 1528 } else { | 1568 } else { |
| 1529 isShadowed = classElement.isShadowedByField(member); | 1569 isShadowed = element.isShadowedByField(field); |
| 1530 } | 1570 } |
| 1531 | 1571 |
| 1532 if ((isInstantiated && !enclosingClass.isNative()) | 1572 if ((isInstantiated && !holder.isNative()) |
| 1533 || needsGetter | 1573 || needsGetter |
| 1534 || needsSetter) { | 1574 || needsSetter) { |
| 1535 String accessorName = isShadowed | 1575 String accessorName = isShadowed |
| 1536 ? namer.shadowedFieldName(member) | 1576 ? namer.shadowedFieldName(field) |
| 1537 : namer.getName(member); | 1577 : namer.getName(field); |
| 1538 String fieldName = member.hasFixedBackendName() | 1578 String fieldName = field.hasFixedBackendName() |
| 1539 ? member.fixedBackendName() | 1579 ? field.fixedBackendName() |
| 1540 : (isMixinNativeField ? member.name.slowToString() : accessorName); | 1580 : (isMixinNativeField ? name.slowToString() : accessorName); |
| 1541 bool needsCheckedSetter = false; | 1581 bool needsCheckedSetter = false; |
| 1542 if (needsSetter) { | 1582 if (compiler.enableTypeAssertions && needsSetter |
| 1543 if (compiler.enableTypeAssertions | 1583 && canGenerateCheckedSetter(field)) { |
| 1544 && canGenerateCheckedSetter(member)) { | 1584 needsCheckedSetter = true; |
| 1545 needsCheckedSetter = true; | 1585 needsSetter = false; |
| 1546 needsSetter = false; | |
| 1547 } | |
| 1548 } | 1586 } |
| 1549 // Getters and setters with suffixes will be generated dynamically. | 1587 // Getters and setters with suffixes will be generated dynamically. |
| 1550 addField(member, | 1588 f(field, fieldName, accessorName, needsGetter, needsSetter, |
| 1551 fieldName, | 1589 needsCheckedSetter); |
| 1552 accessorName, | |
| 1553 needsGetter, | |
| 1554 needsSetter, | |
| 1555 needsCheckedSetter); | |
| 1556 } | 1590 } |
| 1557 } | 1591 } |
| 1558 | 1592 |
| 1559 if (visitStatics) { | 1593 if (isLibrary) { |
| 1560 classElement.implementation.forEachStaticField(visitField); | 1594 LibraryElement library = element; |
| 1595 library.implementation.forEachLocalMember((Element member) { |
| 1596 if (member.isField()) visitField(library, member); |
| 1597 }); |
| 1598 } else if (visitStatics) { |
| 1599 ClassElement cls = element; |
| 1600 cls.implementation.forEachStaticField(visitField); |
| 1561 } else { | 1601 } else { |
| 1602 ClassElement cls = element; |
| 1562 // TODO(kasperl): We should make sure to only emit one version of | 1603 // TODO(kasperl): We should make sure to only emit one version of |
| 1563 // overridden fields. Right now, we rely on the ordering so the | 1604 // overridden fields. Right now, we rely on the ordering so the |
| 1564 // fields pulled in from mixins are replaced with the fields from | 1605 // fields pulled in from mixins are replaced with the fields from |
| 1565 // the class definition. | 1606 // the class definition. |
| 1566 | 1607 |
| 1567 // If a class is not instantiated then we add the field just so we can | 1608 // If a class is not instantiated then we add the field just so we can |
| 1568 // generate the field getter/setter dynamically. Since this is only | 1609 // generate the field getter/setter dynamically. Since this is only |
| 1569 // allowed on fields that are in [classElement] we don't need to visit | 1610 // allowed on fields that are in [element] we don't need to visit |
| 1570 // superclasses for non-instantiated classes. | 1611 // superclasses for non-instantiated classes. |
| 1571 classElement.implementation.forEachInstanceField( | 1612 cls.implementation.forEachInstanceField( |
| 1572 visitField, | 1613 visitField, includeSuperAndInjectedMembers: isInstantiated); |
| 1573 includeSuperAndInjectedMembers: isInstantiated); | |
| 1574 } | 1614 } |
| 1575 } | 1615 } |
| 1576 | 1616 |
| 1577 void generateGetter(Element member, String fieldName, String accessorName, | 1617 void generateGetter(Element member, String fieldName, String accessorName, |
| 1578 ClassBuilder builder) { | 1618 ClassBuilder builder) { |
| 1579 String getterName = namer.getterNameFromAccessorName(accessorName); | 1619 String getterName = namer.getterNameFromAccessorName(accessorName); |
| 1580 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) | 1620 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) |
| 1581 ? 'receiver' : 'this'; | 1621 ? 'receiver' : 'this'; |
| 1582 List<String> args = backend.isInterceptedMethod(member) | 1622 List<String> args = backend.isInterceptedMethod(member) |
| 1583 ? ['receiver'] | 1623 ? ['receiver'] |
| 1584 : []; | 1624 : []; |
| 1585 builder.addProperty(getterName, | 1625 builder.addProperty(getterName, |
| 1586 js.fun(args, js.return_(js('$receiver.$fieldName')))); | 1626 js.fun(args, js.return_(js('$receiver.$fieldName')))); |
| 1587 } | 1627 } |
| 1588 | 1628 |
| 1589 void generateSetter(Element member, String fieldName, String accessorName, | 1629 void generateSetter(Element member, String fieldName, String accessorName, |
| 1590 ClassBuilder builder) { | 1630 ClassBuilder builder) { |
| 1591 String setterName = namer.setterNameFromAccessorName(accessorName); | 1631 String setterName = namer.setterNameFromAccessorName(accessorName); |
| 1592 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) | 1632 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) |
| 1593 ? 'receiver' : 'this'; | 1633 ? 'receiver' : 'this'; |
| 1594 List<String> args = backend.isInterceptedMethod(member) | 1634 List<String> args = backend.isInterceptedMethod(member) |
| 1595 ? ['receiver', 'v'] | 1635 ? ['receiver', 'v'] |
| 1596 : ['v']; | 1636 : ['v']; |
| 1597 builder.addProperty(setterName, | 1637 builder.addProperty(setterName, |
| 1598 js.fun(args, js('$receiver.$fieldName = v'))); | 1638 js.fun(args, js('$receiver.$fieldName = v'))); |
| 1599 } | 1639 } |
| 1600 | 1640 |
| 1601 bool canGenerateCheckedSetter(Element member) { | 1641 bool canGenerateCheckedSetter(VariableElement field) { |
| 1602 // We never generate accessors for top-level/static fields. | 1642 // We never generate accessors for top-level/static fields. |
| 1603 if (!member.isInstanceMember()) return false; | 1643 if (!field.isInstanceMember()) return false; |
| 1604 DartType type = member.computeType(compiler).unalias(compiler); | 1644 DartType type = field.computeType(compiler).unalias(compiler); |
| 1605 if (type.element.isTypeVariable() || | 1645 if (type.element.isTypeVariable() || |
| 1606 (type is FunctionType && type.containsTypeVariables) || | 1646 (type is FunctionType && type.containsTypeVariables) || |
| 1607 type.element == compiler.dynamicClass || | 1647 type.element == compiler.dynamicClass || |
| 1608 type.element == compiler.objectClass) { | 1648 type.element == compiler.objectClass) { |
| 1609 // TODO(ngeoffray): Support type checks on type parameters. | 1649 // TODO(ngeoffray): Support type checks on type parameters. |
| 1610 return false; | 1650 return false; |
| 1611 } | 1651 } |
| 1612 return true; | 1652 return true; |
| 1613 } | 1653 } |
| 1614 | 1654 |
| 1615 void generateCheckedSetter(Element member, | 1655 void generateCheckedSetter(Element member, |
| 1616 String fieldName, | 1656 String fieldName, |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1667 } else { | 1707 } else { |
| 1668 previousName = mangledGlobalFieldNames.putIfAbsent( | 1708 previousName = mangledGlobalFieldNames.putIfAbsent( |
| 1669 accessorName, | 1709 accessorName, |
| 1670 () => memberName); | 1710 () => memberName); |
| 1671 } | 1711 } |
| 1672 assert(invariant(member, previousName == memberName, | 1712 assert(invariant(member, previousName == memberName, |
| 1673 message: '$previousName != ${memberName}')); | 1713 message: '$previousName != ${memberName}')); |
| 1674 } | 1714 } |
| 1675 | 1715 |
| 1676 /// Returns `true` if fields added. | 1716 /// Returns `true` if fields added. |
| 1677 bool emitClassFields(ClassElement classElement, | 1717 bool emitFields(Element element, |
| 1678 ClassBuilder builder, | 1718 ClassBuilder builder, |
| 1679 String superName, | 1719 String superName, |
| 1680 { bool classIsNative: false, | 1720 { bool classIsNative: false, |
| 1681 bool emitStatics: false }) { | 1721 bool emitStatics: false }) { |
| 1722 bool isClass = false; |
| 1723 bool isLibrary = false; |
| 1724 if (element is ClassElement) { |
| 1725 isClass = true; |
| 1726 } else if (element is LibraryElement) { |
| 1727 isLibrary = false; |
| 1728 assert(invariant(element, emitStatics)); |
| 1729 } else { |
| 1730 throw new SpannableAssertionFailure( |
| 1731 element, 'Must be a ClassElement or a LibraryElement'); |
| 1732 } |
| 1682 StringBuffer buffer = new StringBuffer(); | 1733 StringBuffer buffer = new StringBuffer(); |
| 1683 if (emitStatics) { | 1734 if (emitStatics) { |
| 1684 assert(invariant(classElement, superName == null, message: superName)); | 1735 assert(invariant(element, superName == null, message: superName)); |
| 1685 } else { | 1736 } else { |
| 1686 assert(invariant(classElement, superName != null)); | 1737 assert(invariant(element, superName != null)); |
| 1687 String nativeName = | 1738 String nativeName = |
| 1688 namer.getPrimitiveInterceptorRuntimeName(classElement); | 1739 namer.getPrimitiveInterceptorRuntimeName(element); |
| 1689 if (nativeName != null) { | 1740 if (nativeName != null) { |
| 1690 buffer.write('$nativeName/'); | 1741 buffer.write('$nativeName/'); |
| 1691 } | 1742 } |
| 1692 buffer.write('$superName;'); | 1743 buffer.write('$superName;'); |
| 1693 } | 1744 } |
| 1694 int bufferClassLength = buffer.length; | 1745 int bufferClassLength = buffer.length; |
| 1695 | 1746 |
| 1696 String separator = ''; | 1747 String separator = ''; |
| 1697 | 1748 |
| 1698 var fieldMetadata = []; | 1749 var fieldMetadata = []; |
| 1699 bool hasMetadata = false; | 1750 bool hasMetadata = false; |
| 1700 | 1751 |
| 1701 visitClassFields(classElement, emitStatics, | 1752 visitFields(element, emitStatics, |
| 1702 (Element member, | 1753 (VariableElement field, |
| 1703 String name, | 1754 String name, |
| 1704 String accessorName, | 1755 String accessorName, |
| 1705 bool needsGetter, | 1756 bool needsGetter, |
| 1706 bool needsSetter, | 1757 bool needsSetter, |
| 1707 bool needsCheckedSetter) { | 1758 bool needsCheckedSetter) { |
| 1708 // Ignore needsCheckedSetter - that is handled below. | 1759 // Ignore needsCheckedSetter - that is handled below. |
| 1709 bool needsAccessor = (needsGetter || needsSetter); | 1760 bool needsAccessor = (needsGetter || needsSetter); |
| 1710 // We need to output the fields for non-native classes so we can auto- | 1761 // We need to output the fields for non-native classes so we can auto- |
| 1711 // generate the constructor. For native classes there are no | 1762 // generate the constructor. For native classes there are no |
| 1712 // constructors, so we don't need the fields unless we are generating | 1763 // constructors, so we don't need the fields unless we are generating |
| 1713 // accessors at runtime. | 1764 // accessors at runtime. |
| 1714 if (!classIsNative || needsAccessor) { | 1765 if (!classIsNative || needsAccessor) { |
| 1715 buffer.write(separator); | 1766 buffer.write(separator); |
| 1716 separator = ','; | 1767 separator = ','; |
| 1717 var metadata = buildMetadataFunction(member); | 1768 var metadata = buildMetadataFunction(field); |
| 1718 if (metadata != null) { | 1769 if (metadata != null) { |
| 1719 hasMetadata = true; | 1770 hasMetadata = true; |
| 1720 } else { | 1771 } else { |
| 1721 metadata = new jsAst.LiteralNull(); | 1772 metadata = new jsAst.LiteralNull(); |
| 1722 } | 1773 } |
| 1723 fieldMetadata.add(metadata); | 1774 fieldMetadata.add(metadata); |
| 1724 recordMangledField(member, accessorName, member.name.slowToString()); | 1775 recordMangledField(field, accessorName, field.name.slowToString()); |
| 1725 if (!needsAccessor) { | 1776 if (!needsAccessor) { |
| 1726 // Emit field for constructor generation. | 1777 // Emit field for constructor generation. |
| 1727 assert(!classIsNative); | 1778 assert(!classIsNative); |
| 1728 buffer.write(name); | 1779 buffer.write(name); |
| 1729 } else { | 1780 } else { |
| 1730 // Emit (possibly renaming) field name so we can add accessors at | 1781 // Emit (possibly renaming) field name so we can add accessors at |
| 1731 // runtime. | 1782 // runtime. |
| 1732 buffer.write(accessorName); | 1783 buffer.write(accessorName); |
| 1733 if (name != accessorName) { | 1784 if (name != accessorName) { |
| 1734 buffer.write(':$name'); | 1785 buffer.write(':$name'); |
| 1735 // Only the native classes can have renaming accessors. | 1786 // Only the native classes can have renaming accessors. |
| 1736 assert(classIsNative); | 1787 assert(classIsNative); |
| 1737 } | 1788 } |
| 1738 | 1789 |
| 1739 int getterCode = 0; | 1790 int getterCode = 0; |
| 1740 if (needsGetter) { | 1791 if (needsGetter) { |
| 1741 // 01: function() { return this.field; } | 1792 if (field.isInstanceMember()) { |
| 1742 // 10: function(receiver) { return receiver.field; } | 1793 // 01: function() { return this.field; } |
| 1743 // 11: function(receiver) { return this.field; } | 1794 // 10: function(receiver) { return receiver.field; } |
| 1744 getterCode += backend.fieldHasInterceptedGetter(member) ? 2 : 0; | 1795 // 11: function(receiver) { return this.field; } |
| 1745 getterCode += backend.isInterceptorClass(classElement) ? 0 : 1; | 1796 getterCode += backend.fieldHasInterceptedGetter(field) ? 2 : 0; |
| 1746 // TODO(sra): 'isInterceptorClass' might not be the correct test for | 1797 getterCode += backend.isInterceptorClass(element) ? 0 : 1; |
| 1747 // methods forced to use the interceptor convention because the | 1798 // TODO(sra): 'isInterceptorClass' might not be the correct test f
or |
| 1748 // method's class was elsewhere mixed-in to an interceptor. | 1799 // methods forced to use the interceptor convention because the |
| 1749 assert(!member.isInstanceMember() || getterCode != 0); | 1800 // method's class was elsewhere mixed-in to an interceptor. |
| 1801 assert(!field.isInstanceMember() || getterCode != 0); |
| 1802 } else { |
| 1803 getterCode = 1; |
| 1804 } |
| 1750 } | 1805 } |
| 1751 int setterCode = 0; | 1806 int setterCode = 0; |
| 1752 if (needsSetter) { | 1807 if (needsSetter) { |
| 1753 // 01: function(value) { this.field = value; } | 1808 if (field.isInstanceMember()) { |
| 1754 // 10: function(receiver, value) { receiver.field = value; } | 1809 // 01: function(value) { this.field = value; } |
| 1755 // 11: function(receiver, value) { this.field = value; } | 1810 // 10: function(receiver, value) { receiver.field = value; } |
| 1756 setterCode += backend.fieldHasInterceptedSetter(member) ? 2 : 0; | 1811 // 11: function(receiver, value) { this.field = value; } |
| 1757 setterCode += backend.isInterceptorClass(classElement) ? 0 : 1; | 1812 setterCode += backend.fieldHasInterceptedSetter(field) ? 2 : 0; |
| 1758 assert(!member.isInstanceMember() || setterCode != 0); | 1813 setterCode += backend.isInterceptorClass(element) ? 0 : 1; |
| 1814 assert(!field.isInstanceMember() || setterCode != 0); |
| 1815 } else { |
| 1816 setterCode = 1; |
| 1817 } |
| 1759 } | 1818 } |
| 1760 int code = getterCode + (setterCode << 2); | 1819 int code = getterCode + (setterCode << 2); |
| 1761 if (code == 0) { | 1820 if (code == 0) { |
| 1762 compiler.reportInternalError( | 1821 compiler.reportInternalError( |
| 1763 member, 'Internal error: code is 0 ($classElement/$member)'); | 1822 field, 'Internal error: code is 0 ($classElement/$member)'); |
| 1764 } else { | 1823 } else { |
| 1765 buffer.write(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]); | 1824 buffer.write(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]); |
| 1766 } | 1825 } |
| 1767 } | 1826 } |
| 1768 } | 1827 } |
| 1769 }); | 1828 }); |
| 1770 | 1829 |
| 1771 bool fieldsAdded = buffer.length > bufferClassLength; | 1830 bool fieldsAdded = buffer.length > bufferClassLength; |
| 1772 String compactClassData = buffer.toString(); | 1831 String compactClassData = buffer.toString(); |
| 1773 jsAst.Expression classDataNode = js.string(compactClassData); | 1832 jsAst.Expression classDataNode = js.string(compactClassData); |
| 1774 if (hasMetadata) { | 1833 if (hasMetadata) { |
| 1775 fieldMetadata.insert(0, classDataNode); | 1834 fieldMetadata.insert(0, classDataNode); |
| 1776 classDataNode = new jsAst.ArrayInitializer.from(fieldMetadata); | 1835 classDataNode = new jsAst.ArrayInitializer.from(fieldMetadata); |
| 1777 } | 1836 } |
| 1778 builder.addProperty('', classDataNode); | 1837 builder.addProperty('', classDataNode); |
| 1779 return fieldsAdded; | 1838 return fieldsAdded; |
| 1780 } | 1839 } |
| 1781 | 1840 |
| 1782 void emitClassGettersSetters(ClassElement classElement, | 1841 void emitClassGettersSetters(ClassElement classElement, |
| 1783 ClassBuilder builder) { | 1842 ClassBuilder builder) { |
| 1784 | 1843 |
| 1785 visitClassFields(classElement, false, | 1844 visitFields(classElement, false, |
| 1786 (Element member, | 1845 (VariableElement member, |
| 1787 String name, | 1846 String name, |
| 1788 String accessorName, | 1847 String accessorName, |
| 1789 bool needsGetter, | 1848 bool needsGetter, |
| 1790 bool needsSetter, | 1849 bool needsSetter, |
| 1791 bool needsCheckedSetter) { | 1850 bool needsCheckedSetter) { |
| 1792 compiler.withCurrentElement(member, () { | 1851 compiler.withCurrentElement(member, () { |
| 1793 if (needsCheckedSetter) { | 1852 if (needsCheckedSetter) { |
| 1794 assert(!needsSetter); | 1853 assert(!needsSetter); |
| 1795 generateCheckedSetter(member, name, accessorName, builder); | 1854 generateCheckedSetter(member, name, accessorName, builder); |
| 1796 } | 1855 } |
| 1797 if (!getterAndSetterCanBeImplementedByFieldSpec) { | 1856 if (!getterAndSetterCanBeImplementedByFieldSpec) { |
| 1798 if (needsGetter) { | 1857 if (needsGetter) { |
| 1799 generateGetter(member, name, accessorName, builder); | 1858 generateGetter(member, name, accessorName, builder); |
| 1800 } | 1859 } |
| 1801 if (needsSetter) { | 1860 if (needsSetter) { |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1829 if (classElement.isMixinApplication) { | 1888 if (classElement.isMixinApplication) { |
| 1830 String mixinName = namer.getName(computeMixinClass(classElement)); | 1889 String mixinName = namer.getName(computeMixinClass(classElement)); |
| 1831 superName = '$superName+$mixinName'; | 1890 superName = '$superName+$mixinName'; |
| 1832 needsMixinSupport = true; | 1891 needsMixinSupport = true; |
| 1833 } | 1892 } |
| 1834 | 1893 |
| 1835 ClassBuilder builder = new ClassBuilder(); | 1894 ClassBuilder builder = new ClassBuilder(); |
| 1836 emitClassConstructor(classElement, builder); | 1895 emitClassConstructor(classElement, builder); |
| 1837 emitSuper(superName, builder); | 1896 emitSuper(superName, builder); |
| 1838 emitRuntimeName(runtimeName, builder); | 1897 emitRuntimeName(runtimeName, builder); |
| 1839 emitClassFields(classElement, builder, superName); | 1898 emitFields(classElement, builder, superName); |
| 1840 emitClassGettersSetters(classElement, builder); | 1899 emitClassGettersSetters(classElement, builder); |
| 1841 if (!classElement.isMixinApplication) { | 1900 if (!classElement.isMixinApplication) { |
| 1842 emitInstanceMembers(classElement, builder); | 1901 emitInstanceMembers(classElement, builder); |
| 1843 } | 1902 } |
| 1844 emitIsTests(classElement, builder); | 1903 emitIsTests(classElement, builder); |
| 1845 | 1904 |
| 1846 emitClassBuilderWithReflectionData( | 1905 emitClassBuilderWithReflectionData( |
| 1847 className, classElement, builder, buffer); | 1906 className, classElement, builder, buffer); |
| 1848 } | 1907 } |
| 1849 | 1908 |
| 1850 void emitClassBuilderWithReflectionData(String className, | 1909 void emitClassBuilderWithReflectionData(String className, |
| 1851 ClassElement classElement, | 1910 ClassElement classElement, |
| 1852 ClassBuilder builder, | 1911 ClassBuilder builder, |
| 1853 CodeBuffer buffer) { | 1912 CodeBuffer buffer) { |
| 1854 var metadata = buildMetadataFunction(classElement); | 1913 var metadata = buildMetadataFunction(classElement); |
| 1855 if (metadata != null) { | 1914 if (metadata != null) { |
| 1856 builder.addProperty("@", metadata); | 1915 builder.addProperty("@", metadata); |
| 1857 } | 1916 } |
| 1858 | 1917 |
| 1859 List<CodeBuffer> classBuffers = elementBuffers[classElement]; | 1918 List<CodeBuffer> classBuffers = elementBuffers[classElement]; |
| 1860 if (classBuffers == null) { | 1919 if (classBuffers == null) { |
| 1861 classBuffers = []; | 1920 classBuffers = []; |
| 1862 } else { | 1921 } else { |
| 1863 elementBuffers.remove(classElement); | 1922 elementBuffers.remove(classElement); |
| 1864 } | 1923 } |
| 1865 CodeBuffer statics = new CodeBuffer(); | 1924 CodeBuffer statics = new CodeBuffer(); |
| 1866 statics.write('{$n'); | 1925 statics.write('{$n'); |
| 1867 bool hasStatics = false; | 1926 bool hasStatics = false; |
| 1868 ClassBuilder staticsBuilder = new ClassBuilder(); | 1927 ClassBuilder staticsBuilder = new ClassBuilder(); |
| 1869 if (emitClassFields( | 1928 if (emitFields(classElement, staticsBuilder, null, emitStatics: true)) { |
| 1870 classElement, staticsBuilder, null, emitStatics: true)) { | |
| 1871 hasStatics = true; | 1929 hasStatics = true; |
| 1872 statics.write('"":$_'); | 1930 statics.write('"":$_'); |
| 1873 statics.write( | 1931 statics.write( |
| 1874 jsAst.prettyPrint(staticsBuilder.properties.single.value, compiler)); | 1932 jsAst.prettyPrint(staticsBuilder.properties.single.value, compiler)); |
| 1875 statics.write(',$n'); | 1933 statics.write(',$n'); |
| 1876 } | 1934 } |
| 1877 for (CodeBuffer classBuffer in classBuffers) { | 1935 for (CodeBuffer classBuffer in classBuffers) { |
| 1878 // TODO(ahe): What about deferred? | 1936 // TODO(ahe): What about deferred? |
| 1879 if (classBuffer != null) { | 1937 if (classBuffer != null) { |
| 1880 hasStatics = true; | 1938 hasStatics = true; |
| (...skipping 1671 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3552 // After this assignment we will produce invalid JavaScript code if we use | 3610 // After this assignment we will produce invalid JavaScript code if we use |
| 3553 // the classesCollector variable. | 3611 // the classesCollector variable. |
| 3554 classesCollector = 'classesCollector should not be used from now on'; | 3612 classesCollector = 'classesCollector should not be used from now on'; |
| 3555 | 3613 |
| 3556 if (!elementBuffers.isEmpty) { | 3614 if (!elementBuffers.isEmpty) { |
| 3557 var oldClassesCollector = classesCollector; | 3615 var oldClassesCollector = classesCollector; |
| 3558 classesCollector = r"$$"; | 3616 classesCollector = r"$$"; |
| 3559 if (compiler.enableMinification) { | 3617 if (compiler.enableMinification) { |
| 3560 mainBuffer.write(';'); | 3618 mainBuffer.write(';'); |
| 3561 } | 3619 } |
| 3620 |
| 3621 for (Element element in elementBuffers.keys) { |
| 3622 if (element is LibraryElement) { |
| 3623 LibraryElement library = element; |
| 3624 ClassBuilder builder = new ClassBuilder(); |
| 3625 if (emitFields(library, builder, null, emitStatics: true)) { |
| 3626 List<CodeBuffer> buffers = elementBuffers[library]; |
| 3627 var buffer = buffers[0]; |
| 3628 if (buffer == null) { |
| 3629 buffers[0] = buffer = new CodeBuffer(); |
| 3630 } |
| 3631 for (jsAst.Property property in builder.properties) { |
| 3632 if (!buffer.isEmpty) buffer.write(',$n'); |
| 3633 buffer.addBuffer(jsAst.prettyPrint(property, compiler)); |
| 3634 } |
| 3635 } |
| 3636 } |
| 3637 } |
| 3638 |
| 3562 if (!mangledFieldNames.isEmpty) { | 3639 if (!mangledFieldNames.isEmpty) { |
| 3563 var keys = mangledFieldNames.keys.toList(); | 3640 var keys = mangledFieldNames.keys.toList(); |
| 3564 keys.sort(); | 3641 keys.sort(); |
| 3565 var properties = []; | 3642 var properties = []; |
| 3566 for (String key in keys) { | 3643 for (String key in keys) { |
| 3567 var value = js.string('${mangledFieldNames[key]}'); | 3644 var value = js.string('${mangledFieldNames[key]}'); |
| 3568 properties.add(new jsAst.Property(js.string(key), value)); | 3645 properties.add(new jsAst.Property(js.string(key), value)); |
| 3569 } | 3646 } |
| 3570 var map = new jsAst.ObjectInitializer(properties); | 3647 var map = new jsAst.ObjectInitializer(properties); |
| 3571 mainBuffer.write( | 3648 mainBuffer.write( |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3848 var mangledNames = init.mangledNames; | 3925 var mangledNames = init.mangledNames; |
| 3849 var mangledGlobalNames = init.mangledGlobalNames; | 3926 var mangledGlobalNames = init.mangledGlobalNames; |
| 3850 var hasOwnProperty = Object.prototype.hasOwnProperty; | 3927 var hasOwnProperty = Object.prototype.hasOwnProperty; |
| 3851 var length = reflectionData.length; | 3928 var length = reflectionData.length; |
| 3852 for (var i = 0; i < length; i++) { | 3929 for (var i = 0; i < length; i++) { |
| 3853 var data = reflectionData[i]; | 3930 var data = reflectionData[i]; |
| 3854 var name = data[0]; | 3931 var name = data[0]; |
| 3855 var uri = data[1]; | 3932 var uri = data[1]; |
| 3856 var metadata = data[2]; | 3933 var metadata = data[2]; |
| 3857 var descriptor = data[3]; | 3934 var descriptor = data[3]; |
| 3935 var fields = descriptor && descriptor[""]; |
| 3858 var classes = []; | 3936 var classes = []; |
| 3859 var functions = []; | 3937 var functions = []; |
| 3860 function processStatics(descriptor) { | 3938 function processStatics(descriptor) { |
| 3861 for (var property in descriptor) { | 3939 for (var property in descriptor) { |
| 3862 if (!hasOwnProperty.call(descriptor, property)) continue; | 3940 if (!hasOwnProperty.call(descriptor, property)) continue; |
| 3863 if (property === "") continue; | 3941 if (property === "") continue; |
| 3864 var element = descriptor[property]; | 3942 var element = descriptor[property]; |
| 3865 var firstChar = property.substring(0, 1); | 3943 var firstChar = property.substring(0, 1); |
| 3866 var previousProperty; | 3944 var previousProperty; |
| 3867 if (firstChar === "+") { | 3945 if (firstChar === "+") { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 3888 } else { | 3966 } else { |
| 3889 newDesc[previousProp = prop] = element[prop]; | 3967 newDesc[previousProp = prop] = element[prop]; |
| 3890 } | 3968 } |
| 3891 } | 3969 } |
| 3892 $classesCollector[property] = newDesc; | 3970 $classesCollector[property] = newDesc; |
| 3893 classes.push(property); | 3971 classes.push(property); |
| 3894 } | 3972 } |
| 3895 } | 3973 } |
| 3896 } | 3974 } |
| 3897 processStatics(descriptor); | 3975 processStatics(descriptor); |
| 3898 libraries.push([name, uri, classes, functions, metadata]); | 3976 libraries.push([name, uri, classes, functions, metadata, fields]); |
| 3899 } | 3977 } |
| 3900 })'''; | 3978 })'''; |
| 3901 } | 3979 } |
| 3902 } | 3980 } |
| 3903 | 3981 |
| 3904 const String GENERATED_BY = """ | 3982 const String GENERATED_BY = """ |
| 3905 // Generated by dart2js, the Dart to JavaScript compiler. | 3983 // Generated by dart2js, the Dart to JavaScript compiler. |
| 3906 """; | 3984 """; |
| 3907 | 3985 |
| 3908 const String HOOKS_API_USAGE = """ | 3986 const String HOOKS_API_USAGE = """ |
| 3909 // The code supports the following hooks: | 3987 // The code supports the following hooks: |
| 3910 // dartPrint(message) - if this function is defined it is called | 3988 // dartPrint(message) - if this function is defined it is called |
| 3911 // instead of the Dart [print] method. | 3989 // instead of the Dart [print] method. |
| 3912 // dartMainRunner(main) - if this function is defined, the Dart [main] | 3990 // dartMainRunner(main) - if this function is defined, the Dart [main] |
| 3913 // method will not be invoked directly. | 3991 // method will not be invoked directly. |
| 3914 // Instead, a closure that will invoke [main] is | 3992 // Instead, a closure that will invoke [main] is |
| 3915 // passed to [dartMainRunner]. | 3993 // passed to [dartMainRunner]. |
| 3916 """; | 3994 """; |
| OLD | NEW |