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