Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart

Issue 14986002: Make static tear-off closures a class, like instance tear-off closures. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 151
152 checkedClasses = new Set<ClassElement>(); 152 checkedClasses = new Set<ClassElement>();
153 checkedTypedefs = new Set<TypedefElement>(); 153 checkedTypedefs = new Set<TypedefElement>();
154 compiler.codegenWorld.isChecks.forEach((DartType t) { 154 compiler.codegenWorld.isChecks.forEach((DartType t) {
155 if (t is InterfaceType) { 155 if (t is InterfaceType) {
156 checkedClasses.add(t.element); 156 checkedClasses.add(t.element);
157 } else if (t is TypedefType) { 157 } else if (t is TypedefType) {
158 checkedTypedefs.add(t.element); 158 checkedTypedefs.add(t.element);
159 } 159 }
160 }); 160 });
161 print(compiler.resolverWorld.isChecks);
162 print(compiler.codegenWorld.isChecks);
161 } 163 }
162 164
163 ClassElement computeMixinClass(MixinApplicationElement mixinApplication) { 165 ClassElement computeMixinClass(MixinApplicationElement mixinApplication) {
164 ClassElement mixin = mixinApplication.mixin; 166 ClassElement mixin = mixinApplication.mixin;
165 while (mixin.isMixinApplication) { 167 while (mixin.isMixinApplication) {
166 mixinApplication = mixin; 168 mixinApplication = mixin;
167 mixin = mixinApplication.mixin; 169 mixin = mixinApplication.mixin;
168 } 170 }
169 return mixin; 171 return mixin;
170 } 172 }
(...skipping 1581 matching lines...) Expand 10 before | Expand all | Expand 10 after
1752 } 1754 }
1753 1755
1754 return (ClassElement cls) => !unneededClasses.contains(cls); 1756 return (ClassElement cls) => !unneededClasses.contains(cls);
1755 } 1757 }
1756 1758
1757 void emitClosureClassIfNeeded(CodeBuffer buffer) { 1759 void emitClosureClassIfNeeded(CodeBuffer buffer) {
1758 // The closure class could have become necessary because of the generation 1760 // The closure class could have become necessary because of the generation
1759 // of stubs. 1761 // of stubs.
1760 ClassElement closureClass = compiler.closureClass; 1762 ClassElement closureClass = compiler.closureClass;
1761 if (needsClosureClass && !instantiatedClasses.contains(closureClass)) { 1763 if (needsClosureClass && !instantiatedClasses.contains(closureClass)) {
1764 ClassElement objectClass = compiler.objectClass;
1765 if (!instantiatedClasses.contains(objectClass)) {
1766 generateClass(objectClass, bufferForElement(objectClass, buffer));
1767 }
1762 generateClass(closureClass, bufferForElement(closureClass, buffer)); 1768 generateClass(closureClass, bufferForElement(closureClass, buffer));
1763 } 1769 }
1764 } 1770 }
1765 1771
1766 void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) { 1772 void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) {
1767 if (needsDefineClass) { 1773 if (needsDefineClass) {
1768 buffer.write('$finishClassesName($classesCollector,' 1774 buffer.write('$finishClassesName($classesCollector,'
1769 '$_$isolateProperties,' 1775 '$_$isolateProperties,'
1770 '${_}null)$N'); 1776 '${_}null)$N');
1771 1777
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
1807 1813
1808 // Is it possible the primary function was inlined but the bailout was not? 1814 // Is it possible the primary function was inlined but the bailout was not?
1809 for (Element element in 1815 for (Element element in
1810 Elements.sortedByPosition(pendingElementsWithBailouts)) { 1816 Elements.sortedByPosition(pendingElementsWithBailouts)) {
1811 CodeBuffer buffer = bufferForElement(element, eagerBuffer); 1817 CodeBuffer buffer = bufferForElement(element, eagerBuffer);
1812 jsAst.Expression bailoutCode = backend.generatedBailoutCode[element]; 1818 jsAst.Expression bailoutCode = backend.generatedBailoutCode[element];
1813 emitStaticFunction(buffer, namer.getBailoutName(element), bailoutCode); 1819 emitStaticFunction(buffer, namer.getBailoutName(element), bailoutCode);
1814 } 1820 }
1815 } 1821 }
1816 1822
1823 final Map<Element, Element> staticGetters = new Map<Element, Element>();
1824
1817 void emitStaticFunctionGetters(CodeBuffer eagerBuffer) { 1825 void emitStaticFunctionGetters(CodeBuffer eagerBuffer) {
1826 for (FunctionElement element in
1827 Elements.sortedByPosition(staticGetters.keys)) {
1828 Element closure = staticGetters[element];
1829 CodeBuffer buffer = bufferForElement(element, eagerBuffer);
1830 String closureClass = namer.isolateAccess(closure);
1831 String name = namer.getStaticClosureName(element);
1832 String staticName = namer.getName(element);
1833
1834 String closureName = namer.getStaticClosureName(element);
1835 jsAst.Node assignment = js('$isolateProperties.$name = '
1836 'new $closureClass($isolateProperties.$staticName, "$closureName")');
ahe 2013/05/16 20:07:56 This broke deferred loading (and try.dartlang.org)
sra1 2013/05/16 20:28:33 This would 'just work' if static function closures
ahe 2013/05/16 20:30:44 I was thinking the same thing. Well, up to the se
1837 buffer.write(jsAst.prettyPrint(assignment, compiler));
1838 buffer.write('$N');
1839 }
1840 }
1841
1842 void emitStaticFunctionClosures() {
1818 Set<FunctionElement> functionsNeedingGetter = 1843 Set<FunctionElement> functionsNeedingGetter =
1819 compiler.codegenWorld.staticFunctionsNeedingGetter; 1844 compiler.codegenWorld.staticFunctionsNeedingGetter;
1820 for (FunctionElement element in 1845 for (FunctionElement element in
1821 Elements.sortedByPosition(functionsNeedingGetter)) { 1846 Elements.sortedByPosition(functionsNeedingGetter)) {
1822 CodeBuffer buffer = bufferForElement(element, eagerBuffer); 1847 String staticName = namer.getName(element);
1848 String superName = namer.getName(compiler.closureClass);
1849 String name = 'Closure\$${element.name.slowToString()}';
1850 needsClosureClass = true;
1823 1851
1824 // The static function does not have the correct name. Since 1852 ClassElement closureClassElement = new ClosureClassElement(
1825 // [addParameterStubs] use the name to create its stubs we simply 1853 null, new SourceString(name), compiler, element,
1826 // create a fake element with the correct name. 1854 element.getCompilationUnit());
1855 // Now add the methods on the closure class. The instance method does not
1856 // have the correct name. Since [addParameterStubs] use the name to create
1857 // its stubs we simply create a fake element with the correct name.
1827 // Note: the callElement will not have any enclosingElement. 1858 // Note: the callElement will not have any enclosingElement.
1828 FunctionElement callElement = 1859 FunctionElement callElement =
1829 new ClosureInvocationElement(namer.closureInvocationSelectorName, 1860 new ClosureInvocationElement(namer.closureInvocationSelectorName,
1830 element); 1861 element);
1831 String staticName = namer.getName(element); 1862
1832 String invocationName = namer.instanceMethodName(callElement); 1863 String invocationName = namer.instanceMethodName(callElement);
1833 String fieldAccess = '$isolateProperties.$staticName'; 1864 String mangledName = namer.getName(closureClassElement);
1834 buffer.write("$fieldAccess.$invocationName$_=$_$fieldAccess$N");
1835 1865
1836 addParameterStubs(callElement, (String name, jsAst.Expression value) { 1866 // Define the constructor with a name so that Object.toString can
1837 jsAst.Expression assignment = 1867 // find the class name of the closure class.
1838 js('$isolateProperties.$staticName.$name = #', value); 1868 ClassBuilder closureBuilder = new ClassBuilder();
1839 buffer.write(jsAst.prettyPrint(assignment.toStatement(), compiler)); 1869 // If a static function is used as a closure we need to add its name
1840 buffer.write('$N'); 1870 // in case it is used in spawnFunction.
1871 String methodName = namer.STATIC_CLOSURE_NAME_NAME;
1872 emitBoundClosureClassHeader(
1873 mangledName, superName, <String>[invocationName, methodName],
1874 closureBuilder);
1875
1876 addParameterStubs(callElement, closureBuilder.addProperty);
1877
1878 DartType type = element.computeType(compiler);
1879 getTypedefChecksOn(type).forEach((Element typedef) {
1880 String operator = namer.operatorIs(typedef);
1881 closureBuilder.addProperty(operator, js('true'));
1841 }); 1882 });
1842 1883
1843 // If a static function is used as a closure we need to add its name 1884 // TODO(ngeoffray): Cache common base classes for clsures, bound
1844 // in case it is used in spawnFunction. 1885 // closures, and static closures that have common type checks.
1845 String fieldName = namer.STATIC_CLOSURE_NAME_NAME; 1886 boundClosures.add(
1846 buffer.write('$fieldAccess.$fieldName$_=$_"$staticName"$N'); 1887 js('$classesCollector.$mangledName = #',
1847 getTypedefChecksOn(element.computeType(compiler)).forEach( 1888 closureBuilder.toObjectInitializer()));
1848 (Element typedef) { 1889
1849 String operator = namer.operatorIs(typedef); 1890 staticGetters[element] = closureClassElement;
1850 buffer.write('$fieldAccess.$operator$_=${_}true$N');
1851 }
1852 );
1853 } 1891 }
1854 } 1892 }
1855 1893
1856 void emitBoundClosureClassHeader(String mangledName, 1894 void emitBoundClosureClassHeader(String mangledName,
1857 String superName, 1895 String superName,
1858 List<String> fieldNames, 1896 List<String> fieldNames,
1859 ClassBuilder builder) { 1897 ClassBuilder builder) {
1860 builder.addProperty('', 1898 builder.addProperty('',
1861 js.string("$superName;${fieldNames.join(',')}")); 1899 js.string("$superName;${fieldNames.join(',')}"));
1862 } 1900 }
(...skipping 483 matching lines...) Expand 10 before | Expand all | Expand 10 after
2346 for (String jsName in addedJsNames.keys.toList()..sort()) { 2384 for (String jsName in addedJsNames.keys.toList()..sort()) {
2347 Selector selector = addedJsNames[jsName]; 2385 Selector selector = addedJsNames[jsName];
2348 jsAst.Expression method = generateMethod(jsName, selector); 2386 jsAst.Expression method = generateMethod(jsName, selector);
2349 if (method != null) defineStub(jsName, method); 2387 if (method != null) defineStub(jsName, method);
2350 } 2388 }
2351 } 2389 }
2352 2390
2353 String buildIsolateSetup(CodeBuffer buffer, 2391 String buildIsolateSetup(CodeBuffer buffer,
2354 Element appMain, 2392 Element appMain,
2355 Element isolateMain) { 2393 Element isolateMain) {
2356 String mainAccess = "${namer.isolateAccess(appMain)}"; 2394 String mainAccess = "${namer.isolateStaticClosureAccess(appMain)}";
2357 String currentIsolate = "${namer.CURRENT_ISOLATE}"; 2395 String currentIsolate = "${namer.CURRENT_ISOLATE}";
2358 // Since we pass the closurized version of the main method to 2396 // Since we pass the closurized version of the main method to
2359 // the isolate method, we must make sure that it exists. 2397 // the isolate method, we must make sure that it exists.
2360 if (!compiler.codegenWorld.staticFunctionsNeedingGetter.contains(appMain)) {
2361 Selector selector = new Selector.callClosure(0);
2362 String invocationName = namer.invocationName(selector);
2363 buffer.write("$mainAccess.$invocationName = $mainAccess$N");
2364 }
2365 return "${namer.isolateAccess(isolateMain)}($mainAccess)"; 2398 return "${namer.isolateAccess(isolateMain)}($mainAccess)";
2366 } 2399 }
2367 2400
2368 String get nameOfDispatchPropertyInitializer => 'initializeDispatchProperty'; 2401 String get nameOfDispatchPropertyInitializer => 'initializeDispatchProperty';
2369 2402
2370 jsAst.Expression generateDispatchPropertyInitialization() { 2403 jsAst.Expression generateDispatchPropertyInitialization() {
2371 String ref(Element element) { 2404 String ref(Element element) {
2372 return '${namer.CURRENT_ISOLATE}.${namer.getName(element)}'; 2405 return '${namer.CURRENT_ISOLATE}.${namer.getName(element)}';
2373 } 2406 }
2374 2407
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
2446 throw 'internal error'; 2479 throw 'internal error';
2447 } else if (cls == backend.jsArrayClass || 2480 } else if (cls == backend.jsArrayClass ||
2448 cls == backend.jsMutableArrayClass || 2481 cls == backend.jsMutableArrayClass ||
2449 cls == backend.jsFixedArrayClass || 2482 cls == backend.jsFixedArrayClass ||
2450 cls == backend.jsExtendableArrayClass) { 2483 cls == backend.jsExtendableArrayClass) {
2451 condition = js('receiver.constructor == Array'); 2484 condition = js('receiver.constructor == Array');
2452 } else if (cls == backend.jsStringClass) { 2485 } else if (cls == backend.jsStringClass) {
2453 condition = js('(typeof receiver) == "string"'); 2486 condition = js('(typeof receiver) == "string"');
2454 } else if (cls == backend.jsNullClass) { 2487 } else if (cls == backend.jsNullClass) {
2455 condition = js('receiver == null'); 2488 condition = js('receiver == null');
2456 } else if (cls == backend.jsFunctionClass) {
2457 condition = js('(typeof receiver) == "function"');
2458 } else { 2489 } else {
2459 throw 'internal error'; 2490 throw 'internal error';
2460 } 2491 }
2461 return js.if_(condition, buildReturnInterceptor(cls)); 2492 return js.if_(condition, buildReturnInterceptor(cls));
2462 } 2493 }
2463 2494
2464 bool hasArray = false; 2495 bool hasArray = false;
2465 bool hasBool = false; 2496 bool hasBool = false;
2466 bool hasDouble = false; 2497 bool hasDouble = false;
2467 bool hasFunction = false;
2468 bool hasInt = false; 2498 bool hasInt = false;
2469 bool hasNull = false; 2499 bool hasNull = false;
2470 bool hasNumber = false; 2500 bool hasNumber = false;
2471 bool hasString = false; 2501 bool hasString = false;
2472 bool hasNative = false; 2502 bool hasNative = false;
2473 for (ClassElement cls in classes) { 2503 for (ClassElement cls in classes) {
2474 if (cls == backend.jsArrayClass || 2504 if (cls == backend.jsArrayClass ||
2475 cls == backend.jsMutableArrayClass || 2505 cls == backend.jsMutableArrayClass ||
2476 cls == backend.jsFixedArrayClass || 2506 cls == backend.jsFixedArrayClass ||
2477 cls == backend.jsExtendableArrayClass) hasArray = true; 2507 cls == backend.jsExtendableArrayClass) hasArray = true;
2478 else if (cls == backend.jsBoolClass) hasBool = true; 2508 else if (cls == backend.jsBoolClass) hasBool = true;
2479 else if (cls == backend.jsDoubleClass) hasDouble = true; 2509 else if (cls == backend.jsDoubleClass) hasDouble = true;
2480 else if (cls == backend.jsFunctionClass) hasFunction = true;
2481 else if (cls == backend.jsIntClass) hasInt = true; 2510 else if (cls == backend.jsIntClass) hasInt = true;
2482 else if (cls == backend.jsNullClass) hasNull = true; 2511 else if (cls == backend.jsNullClass) hasNull = true;
2483 else if (cls == backend.jsNumberClass) hasNumber = true; 2512 else if (cls == backend.jsNumberClass) hasNumber = true;
2484 else if (cls == backend.jsStringClass) hasString = true; 2513 else if (cls == backend.jsStringClass) hasString = true;
2485 else { 2514 else {
2486 // TODO(sra): The set of classes includes classes mixed-in to 2515 // TODO(sra): The set of classes includes classes mixed-in to
2487 // interceptor classes. 2516 // interceptor classes.
2488 // assert(cls == compiler.objectClass || cls.isNative()); 2517 // assert(cls == compiler.objectClass || cls.isNative());
2489 if (cls.isNative()) hasNative = true; 2518 if (cls.isNative()) hasNative = true;
2490 } 2519 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
2531 } 2560 }
2532 if (hasNull) { 2561 if (hasNull) {
2533 block.statements.add(buildInterceptorCheck(backend.jsNullClass)); 2562 block.statements.add(buildInterceptorCheck(backend.jsNullClass));
2534 } else { 2563 } else {
2535 // Returning "undefined" or "null" here will provoke a JavaScript 2564 // Returning "undefined" or "null" here will provoke a JavaScript
2536 // TypeError which is later identified as a null-error by 2565 // TypeError which is later identified as a null-error by
2537 // [unwrapException] in js_helper.dart. 2566 // [unwrapException] in js_helper.dart.
2538 block.statements.add(js.if_('receiver == null', 2567 block.statements.add(js.if_('receiver == null',
2539 js.return_(js('receiver')))); 2568 js.return_(js('receiver'))));
2540 } 2569 }
2541 if (hasFunction) {
2542 block.statements.add(buildInterceptorCheck(backend.jsFunctionClass));
2543 }
2544 if (hasBool) { 2570 if (hasBool) {
2545 block.statements.add(buildInterceptorCheck(backend.jsBoolClass)); 2571 block.statements.add(buildInterceptorCheck(backend.jsBoolClass));
2546 } 2572 }
2547 // TODO(ahe): It might be faster to check for Array before 2573 // TODO(ahe): It might be faster to check for Array before
2548 // function and bool. 2574 // function and bool.
2549 if (hasArray) { 2575 if (hasArray) {
2550 block.statements.add(buildInterceptorCheck(backend.jsArrayClass)); 2576 block.statements.add(buildInterceptorCheck(backend.jsArrayClass));
2551 } 2577 }
2552 2578
2553 if (hasNative) { 2579 if (hasNative) {
(...skipping 359 matching lines...) Expand 10 before | Expand all | Expand 10 after
2913 addComment(HOOKS_API_USAGE, mainBuffer); 2939 addComment(HOOKS_API_USAGE, mainBuffer);
2914 mainBuffer.add('function ${namer.isolateName}()$_{}\n'); 2940 mainBuffer.add('function ${namer.isolateName}()$_{}\n');
2915 mainBuffer.add('init()$N$n'); 2941 mainBuffer.add('init()$N$n');
2916 // Shorten the code by using [namer.CURRENT_ISOLATE] as temporary. 2942 // Shorten the code by using [namer.CURRENT_ISOLATE] as temporary.
2917 isolateProperties = namer.CURRENT_ISOLATE; 2943 isolateProperties = namer.CURRENT_ISOLATE;
2918 mainBuffer.add( 2944 mainBuffer.add(
2919 'var $isolateProperties$_=$_$isolatePropertiesName$N'); 2945 'var $isolateProperties$_=$_$isolatePropertiesName$N');
2920 2946
2921 if (!regularClasses.isEmpty || 2947 if (!regularClasses.isEmpty ||
2922 !deferredClasses.isEmpty || 2948 !deferredClasses.isEmpty ||
2923 !nativeClasses.isEmpty) { 2949 !nativeClasses.isEmpty ||
2950 !compiler.codegenWorld.staticFunctionsNeedingGetter.isEmpty) {
2924 // Shorten the code by using "$$" as temporary. 2951 // Shorten the code by using "$$" as temporary.
2925 classesCollector = r"$$"; 2952 classesCollector = r"$$";
2926 mainBuffer.add('var $classesCollector$_=$_{}$N$n'); 2953 mainBuffer.add('var $classesCollector$_=$_{}$N$n');
2927 } 2954 }
2928 2955
2929 // As a side-effect, emitting classes will produce "bound closures" in 2956 // As a side-effect, emitting classes will produce "bound closures" in
2930 // [boundClosures]. The bound closures are JS AST nodes that add 2957 // [boundClosures]. The bound closures are JS AST nodes that add
2931 // properties to $$ [classesCollector]. The bound closures are not 2958 // properties to $$ [classesCollector]. The bound closures are not
2932 // emitted until we have emitted all other classes (native or not). 2959 // emitted until we have emitted all other classes (native or not).
2933 2960
(...skipping 25 matching lines...) Expand all
2959 generateClass(element, deferredBuffer); 2986 generateClass(element, deferredBuffer);
2960 } 2987 }
2961 2988
2962 deferredBuffer.add('$finishClassesName(\$\$,' 2989 deferredBuffer.add('$finishClassesName(\$\$,'
2963 '$_${namer.CURRENT_ISOLATE},' 2990 '$_${namer.CURRENT_ISOLATE},'
2964 '$_$isolatePropertiesName)$N'); 2991 '$_$isolatePropertiesName)$N');
2965 // Reset the map. 2992 // Reset the map.
2966 deferredBuffer.add("\$\$$_=${_}null$N$n"); 2993 deferredBuffer.add("\$\$$_=${_}null$N$n");
2967 } 2994 }
2968 2995
2996 emitStaticFunctionClosures();
2969 emitClosureClassIfNeeded(mainBuffer); 2997 emitClosureClassIfNeeded(mainBuffer);
2970 2998
2971 addComment('Bound closures', mainBuffer); 2999 addComment('Bound closures', mainBuffer);
2972 // Now that we have emitted all classes, we know all the bound 3000 // Now that we have emitted all classes, we know all the bound
2973 // closures that will be needed. 3001 // closures that will be needed.
2974 for (jsAst.Node node in boundClosures) { 3002 for (jsAst.Node node in boundClosures) {
2975 // TODO(ahe): Some of these can be deferred. 3003 // TODO(ahe): Some of these can be deferred.
2976 mainBuffer.add(jsAst.prettyPrint(node, compiler)); 3004 mainBuffer.add(jsAst.prettyPrint(node, compiler));
2977 mainBuffer.add("$N$n"); 3005 mainBuffer.add("$N$n");
2978 } 3006 }
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
3089 """; 3117 """;
3090 const String HOOKS_API_USAGE = """ 3118 const String HOOKS_API_USAGE = """
3091 // The code supports the following hooks: 3119 // The code supports the following hooks:
3092 // dartPrint(message) - if this function is defined it is called 3120 // dartPrint(message) - if this function is defined it is called
3093 // instead of the Dart [print] method. 3121 // instead of the Dart [print] method.
3094 // dartMainRunner(main) - if this function is defined, the Dart [main] 3122 // dartMainRunner(main) - if this function is defined, the Dart [main]
3095 // method will not be invoked directly. 3123 // method will not be invoked directly.
3096 // Instead, a closure that will invoke [main] is 3124 // Instead, a closure that will invoke [main] is
3097 // passed to [dartMainRunner]. 3125 // passed to [dartMainRunner].
3098 """; 3126 """;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698