| 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 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 51 /** | 51 /** |
| 52 * Generates the code for all used classes in the program. Static fields (even | 52 * Generates the code for all used classes in the program. Static fields (even |
| 53 * in classes) are ignored, since they can be treated as non-class elements. | 53 * in classes) are ignored, since they can be treated as non-class elements. |
| 54 * | 54 * |
| 55 * The code for the containing (used) methods must exist in the [:universe:]. | 55 * The code for the containing (used) methods must exist in the [:universe:]. |
| 56 */ | 56 */ |
| 57 class CodeEmitterTask extends CompilerTask { | 57 class CodeEmitterTask extends CompilerTask { |
| 58 bool needsInheritFunction = false; | 58 bool needsInheritFunction = false; |
| 59 bool needsDefineClass = false; | 59 bool needsDefineClass = false; |
| 60 bool needsMixinSupport = false; | 60 bool needsMixinSupport = false; |
| 61 bool needsClosureClass = false; |
| 61 bool needsLazyInitializer = false; | 62 bool needsLazyInitializer = false; |
| 62 final Namer namer; | 63 final Namer namer; |
| 63 ConstantEmitter constantEmitter; | 64 ConstantEmitter constantEmitter; |
| 64 NativeEmitter nativeEmitter; | 65 NativeEmitter nativeEmitter; |
| 65 CodeBuffer mainBuffer; | 66 CodeBuffer mainBuffer; |
| 66 final CodeBuffer deferredBuffer = new CodeBuffer(); | 67 final CodeBuffer deferredBuffer = new CodeBuffer(); |
| 67 /** Shorter access to [isolatePropertiesName]. Both here in the code, as | 68 /** Shorter access to [isolatePropertiesName]. Both here in the code, as |
| 68 well as in the generated code. */ | 69 well as in the generated code. */ |
| 69 String isolateProperties; | 70 String isolateProperties; |
| 70 String classesCollector; | 71 String classesCollector; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 86 String get N => compiler.enableMinification ? "\n" : ";\n"; | 87 String get N => compiler.enableMinification ? "\n" : ";\n"; |
| 87 | 88 |
| 88 /** | 89 /** |
| 89 * A cache of closures that are used to closurize instance methods. | 90 * A cache of closures that are used to closurize instance methods. |
| 90 * A closure is dynamically bound to the instance used when | 91 * A closure is dynamically bound to the instance used when |
| 91 * closurized. | 92 * closurized. |
| 92 */ | 93 */ |
| 93 final Map<int, String> boundClosureCache; | 94 final Map<int, String> boundClosureCache; |
| 94 | 95 |
| 95 /** | 96 /** |
| 97 * A cache of closures that are used to closurize instance methods |
| 98 * of interceptors. These closures are dynamically bound to the |
| 99 * interceptor instance, and the actual receiver of the method. |
| 100 */ |
| 101 final Map<int, String> interceptorClosureCache; |
| 102 |
| 103 /** |
| 96 * Raw ClassElement symbols occuring in is-checks and type assertions. If the | 104 * Raw ClassElement symbols occuring in is-checks and type assertions. If the |
| 97 * program contains parameterized checks `x is Set<int>` and | 105 * program contains parameterized checks `x is Set<int>` and |
| 98 * `x is Set<String>` then the ClassElement `Set` will occur once in | 106 * `x is Set<String>` then the ClassElement `Set` will occur once in |
| 99 * [checkedClasses]. | 107 * [checkedClasses]. |
| 100 */ | 108 */ |
| 101 Set<ClassElement> checkedClasses; | 109 Set<ClassElement> checkedClasses; |
| 102 | 110 |
| 103 /** | 111 /** |
| 104 * Raw Typedef symbols occuring in is-checks and type assertions. If the | 112 * Raw Typedef symbols occuring in is-checks and type assertions. If the |
| 105 * program contains `x is F<int>` and `x is F<bool>` then the TypedefElement | 113 * program contains `x is F<int>` and `x is F<bool>` then the TypedefElement |
| (...skipping 12 matching lines...) Expand all Loading... |
| 118 .map((TypeVariableType v) => v.element.getEnclosingClass()) | 126 .map((TypeVariableType v) => v.element.getEnclosingClass()) |
| 119 .toList(); | 127 .toList(); |
| 120 } | 128 } |
| 121 return cachedClassesUsingTypeVariableTests; | 129 return cachedClassesUsingTypeVariableTests; |
| 122 } | 130 } |
| 123 | 131 |
| 124 CodeEmitterTask(Compiler compiler, Namer namer, this.generateSourceMap) | 132 CodeEmitterTask(Compiler compiler, Namer namer, this.generateSourceMap) |
| 125 : mainBuffer = new CodeBuffer(), | 133 : mainBuffer = new CodeBuffer(), |
| 126 this.namer = namer, | 134 this.namer = namer, |
| 127 boundClosureCache = new Map<int, String>(), | 135 boundClosureCache = new Map<int, String>(), |
| 136 interceptorClosureCache = new Map<int, String>(), |
| 128 constantEmitter = new ConstantEmitter(compiler, namer), | 137 constantEmitter = new ConstantEmitter(compiler, namer), |
| 129 super(compiler) { | 138 super(compiler) { |
| 130 nativeEmitter = new NativeEmitter(this); | 139 nativeEmitter = new NativeEmitter(this); |
| 131 } | 140 } |
| 132 | 141 |
| 133 void addComment(String comment, CodeBuffer buffer) { | 142 void addComment(String comment, CodeBuffer buffer) { |
| 134 buffer.write(jsAst.prettyPrint(js.comment(comment), compiler)); | 143 buffer.write(jsAst.prettyPrint(js.comment(comment), compiler)); |
| 135 } | 144 } |
| 136 | 145 |
| 137 void computeRequiredTypeChecks() { | 146 void computeRequiredTypeChecks() { |
| (...skipping 1602 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1740 for (ClassElement interceptor in backend.interceptedClasses) { | 1749 for (ClassElement interceptor in backend.interceptedClasses) { |
| 1741 if (!needed.contains(interceptor) | 1750 if (!needed.contains(interceptor) |
| 1742 && interceptor != compiler.objectClass) { | 1751 && interceptor != compiler.objectClass) { |
| 1743 unneededClasses.add(interceptor); | 1752 unneededClasses.add(interceptor); |
| 1744 } | 1753 } |
| 1745 } | 1754 } |
| 1746 | 1755 |
| 1747 return (ClassElement cls) => !unneededClasses.contains(cls); | 1756 return (ClassElement cls) => !unneededClasses.contains(cls); |
| 1748 } | 1757 } |
| 1749 | 1758 |
| 1759 void emitClosureClassIfNeeded(CodeBuffer buffer) { |
| 1760 // The closure class could have become necessary because of the generation |
| 1761 // of stubs. |
| 1762 ClassElement closureClass = compiler.closureClass; |
| 1763 if (needsClosureClass && !instantiatedClasses.contains(closureClass)) { |
| 1764 ClassElement objectClass = compiler.objectClass; |
| 1765 if (!instantiatedClasses.contains(objectClass)) { |
| 1766 generateClass(objectClass, bufferForElement(objectClass, buffer)); |
| 1767 } |
| 1768 generateClass(closureClass, bufferForElement(closureClass, buffer)); |
| 1769 } |
| 1770 } |
| 1771 |
| 1750 void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) { | 1772 void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) { |
| 1751 if (needsDefineClass) { | 1773 if (needsDefineClass) { |
| 1752 buffer.write('$finishClassesName($classesCollector,' | 1774 buffer.write('$finishClassesName($classesCollector,' |
| 1753 '$_$isolateProperties,' | 1775 '$_$isolateProperties,' |
| 1754 '${_}null)$N'); | 1776 '${_}null)$N'); |
| 1755 | 1777 |
| 1756 // Reset the map. | 1778 // Reset the map. |
| 1757 buffer.write("$classesCollector$_=${_}null$N$n"); | 1779 buffer.write("$classesCollector$_=${_}null$N$n"); |
| 1758 } | 1780 } |
| 1759 } | 1781 } |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1823 buffer.write(jsAst.prettyPrint(assignment, compiler)); | 1845 buffer.write(jsAst.prettyPrint(assignment, compiler)); |
| 1824 buffer.write('$N'); | 1846 buffer.write('$N'); |
| 1825 } | 1847 } |
| 1826 } | 1848 } |
| 1827 | 1849 |
| 1828 void emitStaticFunctionClosures() { | 1850 void emitStaticFunctionClosures() { |
| 1829 Set<FunctionElement> functionsNeedingGetter = | 1851 Set<FunctionElement> functionsNeedingGetter = |
| 1830 compiler.codegenWorld.staticFunctionsNeedingGetter; | 1852 compiler.codegenWorld.staticFunctionsNeedingGetter; |
| 1831 for (FunctionElement element in | 1853 for (FunctionElement element in |
| 1832 Elements.sortedByPosition(functionsNeedingGetter)) { | 1854 Elements.sortedByPosition(functionsNeedingGetter)) { |
| 1833 assert(instantiatedClasses.contains(compiler.closureClass)); | |
| 1834 String staticName = namer.getName(element); | 1855 String staticName = namer.getName(element); |
| 1835 String superName = namer.getName(compiler.closureClass); | 1856 String superName = namer.getName(compiler.closureClass); |
| 1836 String name = 'Closure\$${element.name.slowToString()}'; | 1857 String name = 'Closure\$${element.name.slowToString()}'; |
| 1858 needsClosureClass = true; |
| 1837 | 1859 |
| 1838 ClassElement closureClassElement = new ClosureClassElement( | 1860 ClassElement closureClassElement = new ClosureClassElement( |
| 1839 null, new SourceString(name), compiler, element, | 1861 null, new SourceString(name), compiler, element, |
| 1840 element.getCompilationUnit()); | 1862 element.getCompilationUnit()); |
| 1841 // Now add the methods on the closure class. The instance method does not | 1863 // Now add the methods on the closure class. The instance method does not |
| 1842 // have the correct name. Since [addParameterStubs] use the name to create | 1864 // have the correct name. Since [addParameterStubs] use the name to create |
| 1843 // its stubs we simply create a fake element with the correct name. | 1865 // its stubs we simply create a fake element with the correct name. |
| 1844 // Note: the callElement will not have any enclosingElement. | 1866 // Note: the callElement will not have any enclosingElement. |
| 1845 FunctionElement callElement = | 1867 FunctionElement callElement = |
| 1846 new ClosureInvocationElement(namer.closureInvocationSelectorName, | 1868 new ClosureInvocationElement(namer.closureInvocationSelectorName, |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1885 js.string("$superName;${fieldNames.join(',')}")); | 1907 js.string("$superName;${fieldNames.join(',')}")); |
| 1886 } | 1908 } |
| 1887 | 1909 |
| 1888 /** | 1910 /** |
| 1889 * Documentation wanted -- johnniwinther | 1911 * Documentation wanted -- johnniwinther |
| 1890 * | 1912 * |
| 1891 * Invariant: [member] must be a declaration element. | 1913 * Invariant: [member] must be a declaration element. |
| 1892 */ | 1914 */ |
| 1893 void emitDynamicFunctionGetter(FunctionElement member, | 1915 void emitDynamicFunctionGetter(FunctionElement member, |
| 1894 DefineStubFunction defineStub) { | 1916 DefineStubFunction defineStub) { |
| 1895 assert(instantiatedClasses.contains(compiler.boundClosureClass)); | |
| 1896 assert(invariant(member, member.isDeclaration)); | 1917 assert(invariant(member, member.isDeclaration)); |
| 1897 // For every method that has the same name as a property-get we create a | 1918 // For every method that has the same name as a property-get we create a |
| 1898 // getter that returns a bound closure. Say we have a class 'A' with method | 1919 // getter that returns a bound closure. Say we have a class 'A' with method |
| 1899 // 'foo' and somewhere in the code there is a dynamic property get of | 1920 // 'foo' and somewhere in the code there is a dynamic property get of |
| 1900 // 'foo'. Then we generate the following code (in pseudo Dart/JavaScript): | 1921 // 'foo'. Then we generate the following code (in pseudo Dart/JavaScript): |
| 1901 // | 1922 // |
| 1902 // class A { | 1923 // class A { |
| 1903 // foo(x, y, z) { ... } // Original function. | 1924 // foo(x, y, z) { ... } // Original function. |
| 1904 // get foo { return new BoundClosure499(this, "foo"); } | 1925 // get foo { return new BoundClosure499(this, "foo"); } |
| 1905 // } | 1926 // } |
| 1906 // class BoundClosure499 extends BoundClosure { | 1927 // class BoundClosure499 extends Closure { |
| 1907 // BoundClosure499(this.self, this.target); | 1928 // var self; |
| 1908 // $call3(x, y, z) { return self[target](x, y, z); } | 1929 // BoundClosure499(this.self, this.name); |
| 1930 // $call3(x, y, z) { return self[name](x, y, z); } |
| 1909 // } | 1931 // } |
| 1910 | 1932 |
| 1911 // TODO(floitsch): share the closure classes with other classes | 1933 // TODO(floitsch): share the closure classes with other classes |
| 1912 // if they share methods with the same signature. Currently we do this only | 1934 // if they share methods with the same signature. Currently we do this only |
| 1913 // if there are no optional parameters. Closures with optional parameters | 1935 // if there are no optional parameters. Closures with optional parameters |
| 1914 // are more difficult to canonicalize because they would need to have the | 1936 // are more difficult to canonicalize because they would need to have the |
| 1915 // same default values. | 1937 // same default values. |
| 1916 | 1938 |
| 1917 bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0; | 1939 bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0; |
| 1918 int parameterCount = member.parameterCount(compiler); | 1940 int parameterCount = member.parameterCount(compiler); |
| 1919 | 1941 |
| 1920 Map<int, String> cache = boundClosureCache; | 1942 Map<int, String> cache; |
| 1943 String extraArg = null; |
| 1921 // Intercepted methods take an extra parameter, which is the | 1944 // Intercepted methods take an extra parameter, which is the |
| 1922 // receiver of the call. | 1945 // receiver of the call. |
| 1923 bool inInterceptor = backend.isInterceptedMethod(member); | 1946 bool inInterceptor = backend.isInterceptedMethod(member); |
| 1924 List<String> fieldNames = <String>[]; | 1947 if (inInterceptor) { |
| 1925 compiler.boundClosureClass.forEachInstanceField((_, field) { | 1948 cache = interceptorClosureCache; |
| 1926 fieldNames.add(namer.getName(field)); | 1949 extraArg = 'receiver'; |
| 1927 }); | 1950 } else { |
| 1951 cache = boundClosureCache; |
| 1952 } |
| 1953 List<String> fieldNames = compiler.enableMinification |
| 1954 ? inInterceptor ? const ['a', 'b', 'c'] |
| 1955 : const ['a', 'b'] |
| 1956 : inInterceptor ? const ['self', 'target', 'receiver'] |
| 1957 : const ['self', 'target']; |
| 1928 | 1958 |
| 1929 Iterable<Element> typedefChecks = | 1959 Iterable<Element> typedefChecks = |
| 1930 getTypedefChecksOn(member.computeType(compiler)); | 1960 getTypedefChecksOn(member.computeType(compiler)); |
| 1931 bool hasTypedefChecks = !typedefChecks.isEmpty; | 1961 bool hasTypedefChecks = !typedefChecks.isEmpty; |
| 1932 | 1962 |
| 1933 bool canBeShared = !hasOptionalParameters && !hasTypedefChecks; | 1963 bool canBeShared = !hasOptionalParameters && !hasTypedefChecks; |
| 1934 | 1964 |
| 1935 String closureClass = canBeShared ? cache[parameterCount] : null; | 1965 String closureClass = canBeShared ? cache[parameterCount] : null; |
| 1936 if (closureClass == null) { | 1966 if (closureClass == null) { |
| 1937 // Either the class was not cached yet, or there are optional parameters. | 1967 // Either the class was not cached yet, or there are optional parameters. |
| 1938 // Create a new closure class. | 1968 // Create a new closure class. |
| 1939 String name; | 1969 String name; |
| 1940 if (canBeShared) { | 1970 if (canBeShared) { |
| 1941 name = 'BoundClosure\$${parameterCount}'; | 1971 if (inInterceptor) { |
| 1972 name = 'BoundClosure\$i${parameterCount}'; |
| 1973 } else { |
| 1974 name = 'BoundClosure\$${parameterCount}'; |
| 1975 } |
| 1942 } else { | 1976 } else { |
| 1943 name = 'Bound_${member.name.slowToString()}' | 1977 name = 'Bound_${member.name.slowToString()}' |
| 1944 '_${member.enclosingElement.name.slowToString()}'; | 1978 '_${member.enclosingElement.name.slowToString()}'; |
| 1945 } | 1979 } |
| 1946 | 1980 |
| 1947 ClassElement closureClassElement = new ClosureClassElement( | 1981 ClassElement closureClassElement = new ClosureClassElement( |
| 1948 null, new SourceString(name), compiler, member, | 1982 null, new SourceString(name), compiler, member, |
| 1949 member.getCompilationUnit()); | 1983 member.getCompilationUnit()); |
| 1950 String mangledName = namer.getName(closureClassElement); | 1984 String mangledName = namer.getName(closureClassElement); |
| 1951 String superName = namer.getName(closureClassElement.superclass); | 1985 String superName = namer.getName(closureClassElement.superclass); |
| 1986 needsClosureClass = true; |
| 1952 | 1987 |
| 1953 // Define the constructor with a name so that Object.toString can | 1988 // Define the constructor with a name so that Object.toString can |
| 1954 // find the class name of the closure class. | 1989 // find the class name of the closure class. |
| 1955 ClassBuilder boundClosureBuilder = new ClassBuilder(); | 1990 ClassBuilder boundClosureBuilder = new ClassBuilder(); |
| 1956 emitBoundClosureClassHeader( | 1991 emitBoundClosureClassHeader( |
| 1957 mangledName, superName, fieldNames, boundClosureBuilder); | 1992 mangledName, superName, fieldNames, boundClosureBuilder); |
| 1958 // Now add the methods on the closure class. The instance method does not | 1993 // Now add the methods on the closure class. The instance method does not |
| 1959 // have the correct name. Since [addParameterStubs] use the name to create | 1994 // have the correct name. Since [addParameterStubs] use the name to create |
| 1960 // its stubs we simply create a fake element with the correct name. | 1995 // its stubs we simply create a fake element with the correct name. |
| 1961 // Note: the callElement will not have any enclosingElement. | 1996 // Note: the callElement will not have any enclosingElement. |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2002 | 2037 |
| 2003 // And finally the getter. | 2038 // And finally the getter. |
| 2004 String getterName = namer.getterName(member); | 2039 String getterName = namer.getterName(member); |
| 2005 String targetName = namer.instanceMethodName(member); | 2040 String targetName = namer.instanceMethodName(member); |
| 2006 | 2041 |
| 2007 List<String> parameters = <String>[]; | 2042 List<String> parameters = <String>[]; |
| 2008 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | 2043 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
| 2009 arguments.add(js('this')); | 2044 arguments.add(js('this')); |
| 2010 arguments.add(js.string(targetName)); | 2045 arguments.add(js.string(targetName)); |
| 2011 if (inInterceptor) { | 2046 if (inInterceptor) { |
| 2012 parameters.add('receiver'); | 2047 parameters.add(extraArg); |
| 2013 arguments.add(js('receiver')); | 2048 arguments.add(js(extraArg)); |
| 2014 } | 2049 } |
| 2015 | 2050 |
| 2016 jsAst.Expression getterFunction = js.fun( | 2051 jsAst.Expression getterFunction = js.fun( |
| 2017 parameters, | 2052 parameters, |
| 2018 js.return_(js(closureClass).newWith(arguments))); | 2053 js.return_(js(closureClass).newWith(arguments))); |
| 2019 | 2054 |
| 2020 defineStub(getterName, getterFunction); | 2055 defineStub(getterName, getterFunction); |
| 2021 } | 2056 } |
| 2022 | 2057 |
| 2023 /** | 2058 /** |
| (...skipping 931 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2955 nativeEmitter.assembleCode(nativeBuffer); | 2990 nativeEmitter.assembleCode(nativeBuffer); |
| 2956 | 2991 |
| 2957 // Might create boundClosures. | 2992 // Might create boundClosures. |
| 2958 if (!deferredClasses.isEmpty) { | 2993 if (!deferredClasses.isEmpty) { |
| 2959 for (ClassElement element in deferredClasses) { | 2994 for (ClassElement element in deferredClasses) { |
| 2960 generateClass(element, bufferForElement(element, mainBuffer)); | 2995 generateClass(element, bufferForElement(element, mainBuffer)); |
| 2961 } | 2996 } |
| 2962 } | 2997 } |
| 2963 | 2998 |
| 2964 emitStaticFunctionClosures(); | 2999 emitStaticFunctionClosures(); |
| 3000 emitClosureClassIfNeeded(mainBuffer); |
| 2965 | 3001 |
| 2966 addComment('Bound closures', mainBuffer); | 3002 addComment('Bound closures', mainBuffer); |
| 2967 // Now that we have emitted all classes, we know all the bound | 3003 // Now that we have emitted all classes, we know all the bound |
| 2968 // closures that will be needed. | 3004 // closures that will be needed. |
| 2969 for (jsAst.Node node in boundClosures) { | 3005 for (jsAst.Node node in boundClosures) { |
| 2970 // TODO(ahe): Some of these can be deferred. | 3006 // TODO(ahe): Some of these can be deferred. |
| 2971 mainBuffer.add(jsAst.prettyPrint(node, compiler)); | 3007 mainBuffer.add(jsAst.prettyPrint(node, compiler)); |
| 2972 mainBuffer.add("$N$n"); | 3008 mainBuffer.add("$N$n"); |
| 2973 } | 3009 } |
| 2974 | 3010 |
| (...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3217 functions.push(property); | 3253 functions.push(property); |
| 3218 } else { | 3254 } else { |
| 3219 $$[property] = element; | 3255 $$[property] = element; |
| 3220 classes.push(property); | 3256 classes.push(property); |
| 3221 classes.push(element[""]); | 3257 classes.push(element[""]); |
| 3222 } | 3258 } |
| 3223 } | 3259 } |
| 3224 libraries.push([name, uri, classes, functions]); | 3260 libraries.push([name, uri, classes, functions]); |
| 3225 } | 3261 } |
| 3226 })'''; | 3262 })'''; |
| OLD | NEW |