Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 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.isClass()) { |
| 1497 bool needsSetter, | 1533 isClass = true; |
| 1498 bool needsCheckedSetter)) { | 1534 } else if (element.isLibrary()) { |
| 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 ClassElement cls = element; |
| 1570 isShadowed = cls.isShadowedByField(field); | |
| 1530 } | 1571 } |
| 1531 | 1572 |
| 1532 if ((isInstantiated && !enclosingClass.isNative()) | 1573 if ((isInstantiated && !holder.isNative()) |
| 1533 || needsGetter | 1574 || needsGetter |
| 1534 || needsSetter) { | 1575 || needsSetter) { |
| 1535 String accessorName = isShadowed | 1576 String accessorName = isShadowed |
| 1536 ? namer.shadowedFieldName(member) | 1577 ? namer.shadowedFieldName(field) |
| 1537 : namer.getName(member); | 1578 : namer.getName(field); |
| 1538 String fieldName = member.hasFixedBackendName() | 1579 String fieldName = field.hasFixedBackendName() |
| 1539 ? member.fixedBackendName() | 1580 ? field.fixedBackendName() |
| 1540 : (isMixinNativeField ? member.name.slowToString() : accessorName); | 1581 : (isMixinNativeField ? name.slowToString() : accessorName); |
| 1541 bool needsCheckedSetter = false; | 1582 bool needsCheckedSetter = false; |
| 1542 if (needsSetter) { | 1583 if (compiler.enableTypeAssertions |
| 1543 if (compiler.enableTypeAssertions | 1584 && needsSetter |
| 1544 && canGenerateCheckedSetter(member)) { | 1585 && canGenerateCheckedSetter(field)) { |
| 1545 needsCheckedSetter = true; | 1586 needsCheckedSetter = true; |
| 1546 needsSetter = false; | 1587 needsSetter = false; |
| 1547 } | |
| 1548 } | 1588 } |
| 1549 // Getters and setters with suffixes will be generated dynamically. | 1589 // Getters and setters with suffixes will be generated dynamically. |
| 1550 addField(member, | 1590 f(field, fieldName, accessorName, needsGetter, needsSetter, |
| 1551 fieldName, | 1591 needsCheckedSetter); |
| 1552 accessorName, | |
| 1553 needsGetter, | |
| 1554 needsSetter, | |
| 1555 needsCheckedSetter); | |
| 1556 } | 1592 } |
| 1557 } | 1593 } |
| 1558 | 1594 |
| 1559 if (visitStatics) { | 1595 if (isLibrary) { |
| 1560 classElement.implementation.forEachStaticField(visitField); | 1596 LibraryElement library = element; |
| 1597 library.implementation.forEachLocalMember((Element member) { | |
| 1598 if (member.isField()) visitField(library, member); | |
| 1599 }); | |
| 1600 } else if (visitStatics) { | |
| 1601 ClassElement cls = element; | |
| 1602 cls.implementation.forEachStaticField(visitField); | |
| 1561 } else { | 1603 } else { |
| 1604 ClassElement cls = element; | |
| 1562 // TODO(kasperl): We should make sure to only emit one version of | 1605 // TODO(kasperl): We should make sure to only emit one version of |
| 1563 // overridden fields. Right now, we rely on the ordering so the | 1606 // overridden fields. Right now, we rely on the ordering so the |
| 1564 // fields pulled in from mixins are replaced with the fields from | 1607 // fields pulled in from mixins are replaced with the fields from |
| 1565 // the class definition. | 1608 // the class definition. |
| 1566 | 1609 |
| 1567 // If a class is not instantiated then we add the field just so we can | 1610 // 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 | 1611 // generate the field getter/setter dynamically. Since this is only |
| 1569 // allowed on fields that are in [classElement] we don't need to visit | 1612 // allowed on fields that are in [element] we don't need to visit |
| 1570 // superclasses for non-instantiated classes. | 1613 // superclasses for non-instantiated classes. |
| 1571 classElement.implementation.forEachInstanceField( | 1614 cls.implementation.forEachInstanceField( |
| 1572 visitField, | 1615 visitField, includeSuperAndInjectedMembers: isInstantiated); |
| 1573 includeSuperAndInjectedMembers: isInstantiated); | |
| 1574 } | 1616 } |
| 1575 } | 1617 } |
| 1576 | 1618 |
| 1577 void generateGetter(Element member, String fieldName, String accessorName, | 1619 void generateGetter(Element member, String fieldName, String accessorName, |
| 1578 ClassBuilder builder) { | 1620 ClassBuilder builder) { |
| 1579 String getterName = namer.getterNameFromAccessorName(accessorName); | 1621 String getterName = namer.getterNameFromAccessorName(accessorName); |
| 1580 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) | 1622 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) |
| 1581 ? 'receiver' : 'this'; | 1623 ? 'receiver' : 'this'; |
| 1582 List<String> args = backend.isInterceptedMethod(member) | 1624 List<String> args = backend.isInterceptedMethod(member) |
| 1583 ? ['receiver'] | 1625 ? ['receiver'] |
| 1584 : []; | 1626 : []; |
| 1585 builder.addProperty(getterName, | 1627 builder.addProperty(getterName, |
| 1586 js.fun(args, js.return_(js('$receiver.$fieldName')))); | 1628 js.fun(args, js.return_(js('$receiver.$fieldName')))); |
| 1587 } | 1629 } |
| 1588 | 1630 |
| 1589 void generateSetter(Element member, String fieldName, String accessorName, | 1631 void generateSetter(Element member, String fieldName, String accessorName, |
| 1590 ClassBuilder builder) { | 1632 ClassBuilder builder) { |
| 1591 String setterName = namer.setterNameFromAccessorName(accessorName); | 1633 String setterName = namer.setterNameFromAccessorName(accessorName); |
| 1592 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) | 1634 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) |
| 1593 ? 'receiver' : 'this'; | 1635 ? 'receiver' : 'this'; |
| 1594 List<String> args = backend.isInterceptedMethod(member) | 1636 List<String> args = backend.isInterceptedMethod(member) |
| 1595 ? ['receiver', 'v'] | 1637 ? ['receiver', 'v'] |
| 1596 : ['v']; | 1638 : ['v']; |
| 1597 builder.addProperty(setterName, | 1639 builder.addProperty(setterName, |
| 1598 js.fun(args, js('$receiver.$fieldName = v'))); | 1640 js.fun(args, js('$receiver.$fieldName = v'))); |
| 1599 } | 1641 } |
| 1600 | 1642 |
| 1601 bool canGenerateCheckedSetter(Element member) { | 1643 bool canGenerateCheckedSetter(VariableElement field) { |
| 1602 // We never generate accessors for top-level/static fields. | 1644 // We never generate accessors for top-level/static fields. |
| 1603 if (!member.isInstanceMember()) return false; | 1645 if (!field.isInstanceMember()) return false; |
| 1604 DartType type = member.computeType(compiler).unalias(compiler); | 1646 DartType type = field.computeType(compiler).unalias(compiler); |
| 1605 if (type.element.isTypeVariable() || | 1647 if (type.element.isTypeVariable() || |
| 1606 (type is FunctionType && type.containsTypeVariables) || | 1648 (type is FunctionType && type.containsTypeVariables) || |
| 1607 type.element == compiler.dynamicClass || | 1649 type.element == compiler.dynamicClass || |
| 1608 type.element == compiler.objectClass) { | 1650 type.element == compiler.objectClass) { |
| 1609 // TODO(ngeoffray): Support type checks on type parameters. | 1651 // TODO(ngeoffray): Support type checks on type parameters. |
| 1610 return false; | 1652 return false; |
| 1611 } | 1653 } |
| 1612 return true; | 1654 return true; |
| 1613 } | 1655 } |
| 1614 | 1656 |
| 1615 void generateCheckedSetter(Element member, | 1657 void generateCheckedSetter(Element member, |
| 1616 String fieldName, | 1658 String fieldName, |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1667 } else { | 1709 } else { |
| 1668 previousName = mangledGlobalFieldNames.putIfAbsent( | 1710 previousName = mangledGlobalFieldNames.putIfAbsent( |
| 1669 accessorName, | 1711 accessorName, |
| 1670 () => memberName); | 1712 () => memberName); |
| 1671 } | 1713 } |
| 1672 assert(invariant(member, previousName == memberName, | 1714 assert(invariant(member, previousName == memberName, |
| 1673 message: '$previousName != ${memberName}')); | 1715 message: '$previousName != ${memberName}')); |
| 1674 } | 1716 } |
| 1675 | 1717 |
| 1676 /// Returns `true` if fields added. | 1718 /// Returns `true` if fields added. |
| 1677 bool emitClassFields(ClassElement classElement, | 1719 bool emitFields(Element element, |
| 1678 ClassBuilder builder, | 1720 ClassBuilder builder, |
| 1679 String superName, | 1721 String superName, |
| 1680 { bool classIsNative: false, | 1722 { bool classIsNative: false, |
| 1681 bool emitStatics: false }) { | 1723 bool emitStatics: false }) { |
| 1724 bool isClass = false; | |
| 1725 bool isLibrary = false; | |
| 1726 if (element.isClass()) { | |
| 1727 isClass = true; | |
| 1728 } else if (element.isLibrary()) { | |
| 1729 isLibrary = false; | |
| 1730 assert(invariant(element, emitStatics)); | |
| 1731 } else { | |
| 1732 throw new SpannableAssertionFailure( | |
| 1733 element, 'Must be a ClassElement or a LibraryElement'); | |
| 1734 } | |
| 1682 StringBuffer buffer = new StringBuffer(); | 1735 StringBuffer buffer = new StringBuffer(); |
| 1683 if (emitStatics) { | 1736 if (emitStatics) { |
| 1684 assert(invariant(classElement, superName == null, message: superName)); | 1737 assert(invariant(element, superName == null, message: superName)); |
| 1685 } else { | 1738 } else { |
| 1686 assert(invariant(classElement, superName != null)); | 1739 assert(invariant(element, superName != null)); |
| 1687 String nativeName = | 1740 String nativeName = |
| 1688 namer.getPrimitiveInterceptorRuntimeName(classElement); | 1741 namer.getPrimitiveInterceptorRuntimeName(element); |
| 1689 if (nativeName != null) { | 1742 if (nativeName != null) { |
| 1690 buffer.write('$nativeName/'); | 1743 buffer.write('$nativeName/'); |
| 1691 } | 1744 } |
| 1692 buffer.write('$superName;'); | 1745 buffer.write('$superName;'); |
| 1693 } | 1746 } |
| 1694 int bufferClassLength = buffer.length; | 1747 int bufferClassLength = buffer.length; |
| 1695 | 1748 |
| 1696 String separator = ''; | 1749 String separator = ''; |
| 1697 | 1750 |
| 1698 var fieldMetadata = []; | 1751 var fieldMetadata = []; |
| 1699 bool hasMetadata = false; | 1752 bool hasMetadata = false; |
| 1700 | 1753 |
| 1701 visitClassFields(classElement, emitStatics, | 1754 visitFields(element, emitStatics, |
| 1702 (Element member, | 1755 (VariableElement field, |
| 1703 String name, | 1756 String name, |
| 1704 String accessorName, | 1757 String accessorName, |
| 1705 bool needsGetter, | 1758 bool needsGetter, |
| 1706 bool needsSetter, | 1759 bool needsSetter, |
| 1707 bool needsCheckedSetter) { | 1760 bool needsCheckedSetter) { |
| 1708 // Ignore needsCheckedSetter - that is handled below. | 1761 // Ignore needsCheckedSetter - that is handled below. |
| 1709 bool needsAccessor = (needsGetter || needsSetter); | 1762 bool needsAccessor = (needsGetter || needsSetter); |
| 1710 // We need to output the fields for non-native classes so we can auto- | 1763 // We need to output the fields for non-native classes so we can auto- |
| 1711 // generate the constructor. For native classes there are no | 1764 // generate the constructor. For native classes there are no |
| 1712 // constructors, so we don't need the fields unless we are generating | 1765 // constructors, so we don't need the fields unless we are generating |
| 1713 // accessors at runtime. | 1766 // accessors at runtime. |
| 1714 if (!classIsNative || needsAccessor) { | 1767 if (!classIsNative || needsAccessor) { |
| 1715 buffer.write(separator); | 1768 buffer.write(separator); |
| 1716 separator = ','; | 1769 separator = ','; |
| 1717 var metadata = buildMetadataFunction(member); | 1770 var metadata = buildMetadataFunction(field); |
| 1718 if (metadata != null) { | 1771 if (metadata != null) { |
| 1719 hasMetadata = true; | 1772 hasMetadata = true; |
| 1720 } else { | 1773 } else { |
| 1721 metadata = new jsAst.LiteralNull(); | 1774 metadata = new jsAst.LiteralNull(); |
| 1722 } | 1775 } |
| 1723 fieldMetadata.add(metadata); | 1776 fieldMetadata.add(metadata); |
| 1724 recordMangledField(member, accessorName, member.name.slowToString()); | 1777 recordMangledField(field, accessorName, field.name.slowToString()); |
| 1725 if (!needsAccessor) { | 1778 if (!needsAccessor) { |
| 1726 // Emit field for constructor generation. | 1779 // Emit field for constructor generation. |
| 1727 assert(!classIsNative); | 1780 assert(!classIsNative); |
| 1728 buffer.write(name); | 1781 buffer.write(name); |
| 1729 } else { | 1782 } else { |
| 1730 // Emit (possibly renaming) field name so we can add accessors at | 1783 // Emit (possibly renaming) field name so we can add accessors at |
| 1731 // runtime. | 1784 // runtime. |
| 1732 buffer.write(accessorName); | 1785 buffer.write(accessorName); |
| 1733 if (name != accessorName) { | 1786 if (name != accessorName) { |
| 1734 buffer.write(':$name'); | 1787 buffer.write(':$name'); |
| 1735 // Only the native classes can have renaming accessors. | 1788 // Only the native classes can have renaming accessors. |
| 1736 assert(classIsNative); | 1789 assert(classIsNative); |
| 1737 } | 1790 } |
| 1738 | 1791 |
| 1739 int getterCode = 0; | 1792 int getterCode = 0; |
| 1740 if (needsGetter) { | 1793 if (needsGetter) { |
| 1741 // 01: function() { return this.field; } | 1794 if (field.isInstanceMember()) { |
| 1742 // 10: function(receiver) { return receiver.field; } | 1795 // 01: function() { return this.field; } |
| 1743 // 11: function(receiver) { return this.field; } | 1796 // 10: function(receiver) { return receiver.field; } |
| 1744 getterCode += backend.fieldHasInterceptedGetter(member) ? 2 : 0; | 1797 // 11: function(receiver) { return this.field; } |
| 1745 getterCode += backend.isInterceptorClass(classElement) ? 0 : 1; | 1798 getterCode += backend.fieldHasInterceptedGetter(field) ? 2 : 0; |
| 1746 // TODO(sra): 'isInterceptorClass' might not be the correct test for | 1799 getterCode += backend.isInterceptorClass(element) ? 0 : 1; |
| 1747 // methods forced to use the interceptor convention because the | 1800 // TODO(sra): 'isInterceptorClass' might not be the correct test |
| 1748 // method's class was elsewhere mixed-in to an interceptor. | 1801 // for methods forced to use the interceptor convention because |
| 1749 assert(!member.isInstanceMember() || getterCode != 0); | 1802 // the method's class was elsewhere mixed-in to an interceptor. |
| 1803 assert(!field.isInstanceMember() || getterCode != 0); | |
| 1804 } else { | |
| 1805 getterCode = 1; | |
| 1806 } | |
| 1750 } | 1807 } |
| 1751 int setterCode = 0; | 1808 int setterCode = 0; |
| 1752 if (needsSetter) { | 1809 if (needsSetter) { |
| 1753 // 01: function(value) { this.field = value; } | 1810 if (field.isInstanceMember()) { |
| 1754 // 10: function(receiver, value) { receiver.field = value; } | 1811 // 01: function(value) { this.field = value; } |
| 1755 // 11: function(receiver, value) { this.field = value; } | 1812 // 10: function(receiver, value) { receiver.field = value; } |
| 1756 setterCode += backend.fieldHasInterceptedSetter(member) ? 2 : 0; | 1813 // 11: function(receiver, value) { this.field = value; } |
| 1757 setterCode += backend.isInterceptorClass(classElement) ? 0 : 1; | 1814 setterCode += backend.fieldHasInterceptedSetter(field) ? 2 : 0; |
| 1758 assert(!member.isInstanceMember() || setterCode != 0); | 1815 setterCode += backend.isInterceptorClass(element) ? 0 : 1; |
| 1816 assert(!field.isInstanceMember() || setterCode != 0); | |
| 1817 } else { | |
| 1818 setterCode = 1; | |
| 1819 } | |
| 1759 } | 1820 } |
| 1760 int code = getterCode + (setterCode << 2); | 1821 int code = getterCode + (setterCode << 2); |
| 1761 if (code == 0) { | 1822 if (code == 0) { |
| 1762 compiler.reportInternalError( | 1823 compiler.reportInternalError( |
| 1763 member, 'Internal error: code is 0 ($classElement/$member)'); | 1824 field, 'Internal error: code is 0 ($element/$field)'); |
| 1764 } else { | 1825 } else { |
| 1765 buffer.write(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]); | 1826 buffer.write(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]); |
| 1766 } | 1827 } |
| 1767 } | 1828 } |
| 1768 } | 1829 } |
| 1769 }); | 1830 }); |
| 1770 | 1831 |
| 1771 bool fieldsAdded = buffer.length > bufferClassLength; | 1832 bool fieldsAdded = buffer.length > bufferClassLength; |
| 1772 String compactClassData = buffer.toString(); | 1833 String compactClassData = buffer.toString(); |
| 1773 jsAst.Expression classDataNode = js.string(compactClassData); | 1834 jsAst.Expression classDataNode = js.string(compactClassData); |
| 1774 if (hasMetadata) { | 1835 if (hasMetadata) { |
| 1775 fieldMetadata.insert(0, classDataNode); | 1836 fieldMetadata.insert(0, classDataNode); |
| 1776 classDataNode = new jsAst.ArrayInitializer.from(fieldMetadata); | 1837 classDataNode = new jsAst.ArrayInitializer.from(fieldMetadata); |
| 1777 } | 1838 } |
| 1778 builder.addProperty('', classDataNode); | 1839 builder.addProperty('', classDataNode); |
| 1779 return fieldsAdded; | 1840 return fieldsAdded; |
| 1780 } | 1841 } |
| 1781 | 1842 |
| 1782 void emitClassGettersSetters(ClassElement classElement, | 1843 void emitClassGettersSetters(ClassElement classElement, |
| 1783 ClassBuilder builder) { | 1844 ClassBuilder builder) { |
| 1784 | 1845 |
| 1785 visitClassFields(classElement, false, | 1846 visitFields(classElement, false, |
| 1786 (Element member, | 1847 (VariableElement member, |
| 1787 String name, | 1848 String name, |
| 1788 String accessorName, | 1849 String accessorName, |
| 1789 bool needsGetter, | 1850 bool needsGetter, |
| 1790 bool needsSetter, | 1851 bool needsSetter, |
| 1791 bool needsCheckedSetter) { | 1852 bool needsCheckedSetter) { |
| 1792 compiler.withCurrentElement(member, () { | 1853 compiler.withCurrentElement(member, () { |
| 1793 if (needsCheckedSetter) { | 1854 if (needsCheckedSetter) { |
| 1794 assert(!needsSetter); | 1855 assert(!needsSetter); |
| 1795 generateCheckedSetter(member, name, accessorName, builder); | 1856 generateCheckedSetter(member, name, accessorName, builder); |
| 1796 } | 1857 } |
| 1797 if (!getterAndSetterCanBeImplementedByFieldSpec) { | 1858 if (!getterAndSetterCanBeImplementedByFieldSpec) { |
| 1798 if (needsGetter) { | 1859 if (needsGetter) { |
| 1799 generateGetter(member, name, accessorName, builder); | 1860 generateGetter(member, name, accessorName, builder); |
| 1800 } | 1861 } |
| 1801 if (needsSetter) { | 1862 if (needsSetter) { |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 1829 if (classElement.isMixinApplication) { | 1890 if (classElement.isMixinApplication) { |
| 1830 String mixinName = namer.getName(computeMixinClass(classElement)); | 1891 String mixinName = namer.getName(computeMixinClass(classElement)); |
| 1831 superName = '$superName+$mixinName'; | 1892 superName = '$superName+$mixinName'; |
| 1832 needsMixinSupport = true; | 1893 needsMixinSupport = true; |
| 1833 } | 1894 } |
| 1834 | 1895 |
| 1835 ClassBuilder builder = new ClassBuilder(); | 1896 ClassBuilder builder = new ClassBuilder(); |
| 1836 emitClassConstructor(classElement, builder); | 1897 emitClassConstructor(classElement, builder); |
| 1837 emitSuper(superName, builder); | 1898 emitSuper(superName, builder); |
| 1838 emitRuntimeName(runtimeName, builder); | 1899 emitRuntimeName(runtimeName, builder); |
| 1839 emitClassFields(classElement, builder, superName); | 1900 emitFields(classElement, builder, superName); |
| 1840 emitClassGettersSetters(classElement, builder); | 1901 emitClassGettersSetters(classElement, builder); |
| 1841 if (!classElement.isMixinApplication) { | 1902 if (!classElement.isMixinApplication) { |
| 1842 emitInstanceMembers(classElement, builder); | 1903 emitInstanceMembers(classElement, builder); |
| 1843 } | 1904 } |
| 1844 emitIsTests(classElement, builder); | 1905 emitIsTests(classElement, builder); |
| 1845 | 1906 |
| 1846 emitClassBuilderWithReflectionData( | 1907 emitClassBuilderWithReflectionData( |
| 1847 className, classElement, builder, buffer); | 1908 className, classElement, builder, buffer); |
| 1848 } | 1909 } |
| 1849 | 1910 |
| 1850 void emitClassBuilderWithReflectionData(String className, | 1911 void emitClassBuilderWithReflectionData(String className, |
| 1851 ClassElement classElement, | 1912 ClassElement classElement, |
| 1852 ClassBuilder builder, | 1913 ClassBuilder builder, |
| 1853 CodeBuffer buffer) { | 1914 CodeBuffer buffer) { |
| 1854 var metadata = buildMetadataFunction(classElement); | 1915 var metadata = buildMetadataFunction(classElement); |
| 1855 if (metadata != null) { | 1916 if (metadata != null) { |
| 1856 builder.addProperty("@", metadata); | 1917 builder.addProperty("@", metadata); |
| 1857 } | 1918 } |
| 1858 | 1919 |
| 1859 List<CodeBuffer> classBuffers = elementBuffers[classElement]; | 1920 List<CodeBuffer> classBuffers = elementBuffers[classElement]; |
| 1860 if (classBuffers == null) { | 1921 if (classBuffers == null) { |
| 1861 classBuffers = []; | 1922 classBuffers = []; |
| 1862 } else { | 1923 } else { |
| 1863 elementBuffers.remove(classElement); | 1924 elementBuffers.remove(classElement); |
| 1864 } | 1925 } |
| 1865 CodeBuffer statics = new CodeBuffer(); | 1926 CodeBuffer statics = new CodeBuffer(); |
| 1866 statics.write('{$n'); | 1927 statics.write('{$n'); |
| 1867 bool hasStatics = false; | 1928 bool hasStatics = false; |
| 1868 ClassBuilder staticsBuilder = new ClassBuilder(); | 1929 ClassBuilder staticsBuilder = new ClassBuilder(); |
| 1869 if (emitClassFields( | 1930 if (emitFields(classElement, staticsBuilder, null, emitStatics: true)) { |
| 1870 classElement, staticsBuilder, null, emitStatics: true)) { | |
| 1871 hasStatics = true; | 1931 hasStatics = true; |
| 1872 statics.write('"":$_'); | 1932 statics.write('"":$_'); |
| 1873 statics.write( | 1933 statics.write( |
| 1874 jsAst.prettyPrint(staticsBuilder.properties.single.value, compiler)); | 1934 jsAst.prettyPrint(staticsBuilder.properties.single.value, compiler)); |
| 1875 statics.write(',$n'); | 1935 statics.write(',$n'); |
| 1876 } | 1936 } |
| 1877 for (CodeBuffer classBuffer in classBuffers) { | 1937 for (CodeBuffer classBuffer in classBuffers) { |
| 1878 // TODO(ahe): What about deferred? | 1938 // TODO(ahe): What about deferred? |
| 1879 if (classBuffer != null) { | 1939 if (classBuffer != null) { |
| 1880 hasStatics = true; | 1940 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 | 3612 // After this assignment we will produce invalid JavaScript code if we use |
| 3553 // the classesCollector variable. | 3613 // the classesCollector variable. |
| 3554 classesCollector = 'classesCollector should not be used from now on'; | 3614 classesCollector = 'classesCollector should not be used from now on'; |
| 3555 | 3615 |
| 3556 if (!elementBuffers.isEmpty) { | 3616 if (!elementBuffers.isEmpty) { |
| 3557 var oldClassesCollector = classesCollector; | 3617 var oldClassesCollector = classesCollector; |
| 3558 classesCollector = r"$$"; | 3618 classesCollector = r"$$"; |
| 3559 if (compiler.enableMinification) { | 3619 if (compiler.enableMinification) { |
| 3560 mainBuffer.write(';'); | 3620 mainBuffer.write(';'); |
| 3561 } | 3621 } |
| 3622 | |
| 3623 for (Element element in elementBuffers.keys) { | |
| 3624 // TODO(ahe): Should to iterate over all libraries. Otherwise, we | |
|
ngeoffray
2013/07/22 12:59:06
I think you should rewrite the comment, it's not r
ahe
2013/07/22 13:27:27
Done.
| |
| 3625 // wont see libraries that only has fields. | |
| 3626 if (element.isLibrary()) { | |
| 3627 LibraryElement library = element; | |
| 3628 ClassBuilder builder = new ClassBuilder(); | |
| 3629 if (emitFields(library, builder, null, emitStatics: true)) { | |
| 3630 List<CodeBuffer> buffers = elementBuffers[library]; | |
| 3631 var buffer = buffers[0]; | |
| 3632 if (buffer == null) { | |
| 3633 buffers[0] = buffer = new CodeBuffer(); | |
| 3634 } | |
| 3635 for (jsAst.Property property in builder.properties) { | |
| 3636 if (!buffer.isEmpty) buffer.write(',$n'); | |
| 3637 buffer.addBuffer(jsAst.prettyPrint(property, compiler)); | |
| 3638 } | |
| 3639 } | |
| 3640 } | |
| 3641 } | |
| 3642 | |
| 3562 if (!mangledFieldNames.isEmpty) { | 3643 if (!mangledFieldNames.isEmpty) { |
| 3563 var keys = mangledFieldNames.keys.toList(); | 3644 var keys = mangledFieldNames.keys.toList(); |
| 3564 keys.sort(); | 3645 keys.sort(); |
| 3565 var properties = []; | 3646 var properties = []; |
| 3566 for (String key in keys) { | 3647 for (String key in keys) { |
| 3567 var value = js.string('${mangledFieldNames[key]}'); | 3648 var value = js.string('${mangledFieldNames[key]}'); |
| 3568 properties.add(new jsAst.Property(js.string(key), value)); | 3649 properties.add(new jsAst.Property(js.string(key), value)); |
| 3569 } | 3650 } |
| 3570 var map = new jsAst.ObjectInitializer(properties); | 3651 var map = new jsAst.ObjectInitializer(properties); |
| 3571 mainBuffer.write( | 3652 mainBuffer.write( |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3848 var mangledNames = init.mangledNames; | 3929 var mangledNames = init.mangledNames; |
| 3849 var mangledGlobalNames = init.mangledGlobalNames; | 3930 var mangledGlobalNames = init.mangledGlobalNames; |
| 3850 var hasOwnProperty = Object.prototype.hasOwnProperty; | 3931 var hasOwnProperty = Object.prototype.hasOwnProperty; |
| 3851 var length = reflectionData.length; | 3932 var length = reflectionData.length; |
| 3852 for (var i = 0; i < length; i++) { | 3933 for (var i = 0; i < length; i++) { |
| 3853 var data = reflectionData[i]; | 3934 var data = reflectionData[i]; |
| 3854 var name = data[0]; | 3935 var name = data[0]; |
| 3855 var uri = data[1]; | 3936 var uri = data[1]; |
| 3856 var metadata = data[2]; | 3937 var metadata = data[2]; |
| 3857 var descriptor = data[3]; | 3938 var descriptor = data[3]; |
| 3939 var fields = descriptor && descriptor[""]; | |
| 3858 var classes = []; | 3940 var classes = []; |
| 3859 var functions = []; | 3941 var functions = []; |
| 3860 function processStatics(descriptor) { | 3942 function processStatics(descriptor) { |
| 3861 for (var property in descriptor) { | 3943 for (var property in descriptor) { |
| 3862 if (!hasOwnProperty.call(descriptor, property)) continue; | 3944 if (!hasOwnProperty.call(descriptor, property)) continue; |
| 3863 if (property === "") continue; | 3945 if (property === "") continue; |
| 3864 var element = descriptor[property]; | 3946 var element = descriptor[property]; |
| 3865 var firstChar = property.substring(0, 1); | 3947 var firstChar = property.substring(0, 1); |
| 3866 var previousProperty; | 3948 var previousProperty; |
| 3867 if (firstChar === "+") { | 3949 if (firstChar === "+") { |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 3888 } else { | 3970 } else { |
| 3889 newDesc[previousProp = prop] = element[prop]; | 3971 newDesc[previousProp = prop] = element[prop]; |
| 3890 } | 3972 } |
| 3891 } | 3973 } |
| 3892 $classesCollector[property] = newDesc; | 3974 $classesCollector[property] = newDesc; |
| 3893 classes.push(property); | 3975 classes.push(property); |
| 3894 } | 3976 } |
| 3895 } | 3977 } |
| 3896 } | 3978 } |
| 3897 processStatics(descriptor); | 3979 processStatics(descriptor); |
| 3898 libraries.push([name, uri, classes, functions, metadata]); | 3980 libraries.push([name, uri, classes, functions, metadata, fields]); |
| 3899 } | 3981 } |
| 3900 })'''; | 3982 })'''; |
| 3901 } | 3983 } |
| 3902 } | 3984 } |
| 3903 | 3985 |
| 3904 const String GENERATED_BY = """ | 3986 const String GENERATED_BY = """ |
| 3905 // Generated by dart2js, the Dart to JavaScript compiler. | 3987 // Generated by dart2js, the Dart to JavaScript compiler. |
| 3906 """; | 3988 """; |
| 3907 | 3989 |
| 3908 const String HOOKS_API_USAGE = """ | 3990 const String HOOKS_API_USAGE = """ |
| 3909 // The code supports the following hooks: | 3991 // The code supports the following hooks: |
| 3910 // dartPrint(message) - if this function is defined it is called | 3992 // dartPrint(message) - if this function is defined it is called |
| 3911 // instead of the Dart [print] method. | 3993 // instead of the Dart [print] method. |
| 3912 // dartMainRunner(main) - if this function is defined, the Dart [main] | 3994 // dartMainRunner(main) - if this function is defined, the Dart [main] |
| 3913 // method will not be invoked directly. | 3995 // method will not be invoked directly. |
| 3914 // Instead, a closure that will invoke [main] is | 3996 // Instead, a closure that will invoke [main] is |
| 3915 // passed to [dartMainRunner]. | 3997 // passed to [dartMainRunner]. |
| 3916 """; | 3998 """; |
| OLD | NEW |