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

Side by Side Diff: dart/sdk/lib/_internal/compiler/implementation/js_emitter/container_builder.dart

Issue 27524003: Generate tear-off closures dynamically. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 7 years, 2 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) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 dart2js.js_emitter; 5 part of dart2js.js_emitter;
6 6
7 /// This class should morph into something that makes it easy to build 7 /// This class should morph into something that makes it easy to build
8 /// JavaScript representations of libraries, class-sides, and instance-sides. 8 /// JavaScript representations of libraries, class-sides, and instance-sides.
9 /// Initially, it is just a placeholder for code that is moved from 9 /// Initially, it is just a placeholder for code that is moved from
10 /// [CodeEmitterTask]. 10 /// [CodeEmitterTask].
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 parametersBuffer, argumentsBuffer, 122 parametersBuffer, argumentsBuffer,
123 indexOfLastOptionalArgumentInParameters); 123 indexOfLastOptionalArgumentInParameters);
124 } else { 124 } else {
125 body = [js.return_( 125 body = [js.return_(
126 js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))]; 126 js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))];
127 } 127 }
128 128
129 jsAst.Fun function = js.fun(parametersBuffer, body); 129 jsAst.Fun function = js.fun(parametersBuffer, body);
130 130
131 defineStub(invocationName, function); 131 defineStub(invocationName, function);
132
133 String reflectionName = task.getReflectionName(selector, invocationName);
134 if (reflectionName != null) {
135 var reflectable =
136 js(backend.isAccessibleByReflection(member) ? '1' : '0');
137 defineStub('+$reflectionName', reflectable);
138 }
139 } 132 }
140 133
141 void addParameterStubs(FunctionElement member, 134 void addParameterStubs(FunctionElement member,
142 DefineStubFunction defineStub) { 135 DefineStubFunction defineStub) {
136 // Bound closures are generated dynamically.
137 if (member.enclosingElement.isClosure()) return;
138
143 // We fill the lists depending on the selector. For example, 139 // We fill the lists depending on the selector. For example,
144 // take method foo: 140 // take method foo:
145 // foo(a, b, {c, d}); 141 // foo(a, b, {c, d});
146 // 142 //
147 // We may have multiple ways of calling foo: 143 // We may have multiple ways of calling foo:
148 // (1) foo(1, 2); 144 // (1) foo(1, 2);
149 // (2) foo(1, 2, c: 3); 145 // (2) foo(1, 2, c: 3);
150 // (3) foo(1, 2, d: 4); 146 // (3) foo(1, 2, d: 4);
151 // (4) foo(1, 2, c: 3, d: 4); 147 // (4) foo(1, 2, c: 3, d: 4);
152 // (5) foo(1, 2, d: 4, c: 3); 148 // (5) foo(1, 2, d: 4, c: 3);
153 // 149 //
154 // What we generate at the call sites are: 150 // What we generate at the call sites are:
155 // (1) foo$2(1, 2); 151 // (1) foo$2(1, 2);
156 // (2) foo$3$c(1, 2, 3); 152 // (2) foo$3$c(1, 2, 3);
157 // (3) foo$3$d(1, 2, 4); 153 // (3) foo$3$d(1, 2, 4);
158 // (4) foo$4$c$d(1, 2, 3, 4); 154 // (4) foo$4$c$d(1, 2, 3, 4);
159 // (5) foo$4$c$d(1, 2, 3, 4); 155 // (5) foo$4$c$d(1, 2, 3, 4);
160 // 156 //
161 // The stubs we generate are (expressed in Dart): 157 // The stubs we generate are (expressed in Dart):
162 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) 158 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null)
163 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); 159 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null);
164 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); 160 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d);
165 // (4) No stub generated, call is direct. 161 // (4) No stub generated, call is direct.
166 // (5) No stub generated, call is direct. 162 // (5) No stub generated, call is direct.
167 163
168 // Keep a cache of which stubs have already been generated, to 164 // Keep a cache of which stubs have already been generated, to
169 // avoid duplicates. Note that even if selectors are 165 // avoid duplicates. Note that even if selectors are
170 // canonicalized, we would still need this cache: a typed selector 166 // canonicalized, we would still need this cache: a typed selector
171 // on A and a typed selector on B could yield the same stub. 167 // on A and a typed selector on B could yield the same stub.
172 Set<String> generatedStubNames = new Set<String>(); 168 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name];
173 bool isClosureInvocation = 169 if (selectors == null) {
174 member.name == namer.closureInvocationSelectorName; 170 selectors = compiler.codegenWorld.invokedNames[
175 if (backend.isNeededForReflection(member) || 171 namer.closureInvocationSelectorName];
kasperl 2013/10/21 10:25:44 Cache namer.closureInvocationSelectorName in a loc
ahe 2013/12/06 15:57:53 The code is now so distant that I don't think it h
176 (compiler.enabledFunctionApply && isClosureInvocation)) { 172 if (selectors == null) return;
177 // If [Function.apply] is called, we pessimistically compile all 173 } else {
178 // possible stubs for this closure. 174 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[
179 FunctionSignature signature = member.computeSignature(compiler); 175 namer.closureInvocationSelectorName];
180 Set<Selector> selectors = signature.optionalParametersAreNamed 176 if (callSelectors != null) {
181 ? computeSeenNamedSelectors(member) 177 selectors = new Set<Selector>.from(selectors)..addAll(callSelectors);
kasperl 2013/10/21 10:25:44 Can you use Set.union here?
ahe 2013/10/22 10:52:34 Oh yeah! I didn't know about that method :-)
182 : computeOptionalSelectors(signature, member);
183 for (Selector selector in selectors) {
184 addParameterStub(member, selector, defineStub, generatedStubNames);
185 } 178 }
186 if (signature.optionalParametersAreNamed && isClosureInvocation) { 179 }
187 addCatchAllParameterStub(member, signature, defineStub); 180 for (Selector selector in selectors) {
188 } 181 if (!selector.applies(member, compiler)) continue;
189 } else { 182 // TODO(ahe): Is the last argument to [addParameterStub] needed?
190 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; 183 addParameterStub(member, selector, defineStub, new Set<String>());
191 if (selectors == null) return;
192 for (Selector selector in selectors) {
193 if (!selector.applies(member, compiler)) continue;
194 addParameterStub(member, selector, defineStub, generatedStubNames);
195 }
196 } 184 }
197 } 185 }
198 186
199 Set<Selector> computeSeenNamedSelectors(FunctionElement element) {
200 Set<Selector> selectors = compiler.codegenWorld.invokedNames[element.name];
201 Set<Selector> result = new Set<Selector>();
202 if (selectors == null) return result;
203 for (Selector selector in selectors) {
204 if (!selector.applies(element, compiler)) continue;
205 result.add(selector);
206 }
207 return result;
208 }
209
210 void addCatchAllParameterStub(FunctionElement member,
211 FunctionSignature signature,
212 DefineStubFunction defineStub) {
213 // See Primities.applyFunction in js_helper.dart for details.
214 List<jsAst.Property> properties = <jsAst.Property>[];
215 for (Element element in signature.orderedOptionalParameters) {
216 String jsName = backend.namer.safeName(element.name.slowToString());
217 Constant value = compiler.constantHandler.initialVariableValues[element];
218 jsAst.Expression reference = null;
219 if (value == null) {
220 reference = new jsAst.LiteralNull();
221 } else {
222 reference = task.constantReference(value);
223 }
224 properties.add(new jsAst.Property(js.string(jsName), reference));
225 }
226 defineStub(
227 backend.namer.callCatchAllName,
228 js.fun([], js.return_(new jsAst.ObjectInitializer(properties))));
229 }
230
231 /**
232 * Compute the set of possible selectors in the presence of optional
233 * non-named parameters.
234 */
235 Set<Selector> computeOptionalSelectors(FunctionSignature signature,
236 FunctionElement element) {
237 Set<Selector> selectors = new Set<Selector>();
238 // Add the selector that does not have any optional argument.
239 selectors.add(new Selector(SelectorKind.CALL,
240 element.name,
241 element.getLibrary(),
242 signature.requiredParameterCount,
243 <SourceString>[]));
244
245 // For each optional parameter, we increment the number of passed
246 // argument.
247 for (int i = 1; i <= signature.optionalParameterCount; i++) {
248 selectors.add(new Selector(SelectorKind.CALL,
249 element.name,
250 element.getLibrary(),
251 signature.requiredParameterCount + i,
252 <SourceString>[]));
253 }
254 return selectors;
255 }
256
257 void emitStaticFunctionGetters(CodeBuffer eagerBuffer) { 187 void emitStaticFunctionGetters(CodeBuffer eagerBuffer) {
188 return;
258 task.addComment('Static function getters', task.mainBuffer); 189 task.addComment('Static function getters', task.mainBuffer);
259 for (FunctionElement element in 190 for (FunctionElement element in
260 Elements.sortedByPosition(staticGetters.keys)) { 191 Elements.sortedByPosition(staticGetters.keys)) {
261 Element closure = staticGetters[element]; 192 Element closure = staticGetters[element];
262 CodeBuffer buffer = 193 CodeBuffer buffer =
263 task.isDeferred(element) ? task.deferredConstants : eagerBuffer; 194 task.isDeferred(element) ? task.deferredConstants : eagerBuffer;
264 String closureClass = namer.isolateAccess(closure); 195 String closureClass = namer.isolateAccess(closure);
265 String name = namer.getStaticClosureName(element); 196 String name = namer.getStaticClosureName(element);
266 197
267 String closureName = namer.getStaticClosureName(element); 198 String closureName = namer.getStaticClosureName(element);
(...skipping 304 matching lines...) Expand 10 before | Expand all | Expand 10 after
572 * 503 *
573 * Invariant: [member] must be a declaration element. 504 * Invariant: [member] must be a declaration element.
574 */ 505 */
575 void emitExtraAccessors(Element member, ClassBuilder builder) { 506 void emitExtraAccessors(Element member, ClassBuilder builder) {
576 assert(invariant(member, member.isDeclaration)); 507 assert(invariant(member, member.isDeclaration));
577 if (member.isGetter() || member.isField()) { 508 if (member.isGetter() || member.isField()) {
578 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; 509 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name];
579 if (selectors != null && !selectors.isEmpty) { 510 if (selectors != null && !selectors.isEmpty) {
580 emitCallStubForGetter(member, selectors, builder.addProperty); 511 emitCallStubForGetter(member, selectors, builder.addProperty);
581 } 512 }
582 } else if (member.isFunction()) {
583 if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) {
584 emitDynamicFunctionGetter(member, builder.addProperty);
585 }
586 } 513 }
587 } 514 }
588 515
589 void addMember(Element member, ClassBuilder builder) { 516 void addMember(Element member, ClassBuilder builder) {
590 assert(invariant(member, member.isDeclaration)); 517 assert(invariant(member, member.isDeclaration));
591 518
592 if (member.isField()) { 519 if (member.isField()) {
593 addMemberField(member, builder); 520 addMemberField(member, builder);
594 } else if (member.isFunction() || 521 } else if (member.isFunction() ||
595 member.isGenerativeConstructorBody() || 522 member.isGenerativeConstructorBody() ||
596 member.isGenerativeConstructor() || 523 member.isGenerativeConstructor() ||
597 member.isAccessor()) { 524 member.isAccessor()) {
598 addMemberMethod(member, builder); 525 addMemberMethod(member, builder);
599 } else { 526 } else {
600 compiler.internalErrorOnElement( 527 compiler.internalErrorOnElement(
601 member, 'unexpected kind: "${member.kind}"'); 528 member, 'unexpected kind: "${member.kind}"');
602 } 529 }
603 if (member.isInstanceMember()) emitExtraAccessors(member, builder); 530 if (member.isInstanceMember()) emitExtraAccessors(member, builder);
604 } 531 }
605 532
606 void addMemberMethod(FunctionElement member, ClassBuilder builder) { 533 void addMemberMethod(FunctionElement member, ClassBuilder builder) {
607 if (member.isAbstract(compiler)) return; 534 if (member.isAbstract(compiler)) return;
608 jsAst.Expression code = backend.generatedCode[member]; 535 jsAst.Expression code = backend.generatedCode[member];
609 if (code == null) return; 536 if (code == null) return;
610 String name = namer.getNameOfMember(member); 537 String name = namer.getNameOfMember(member);
611 if (backend.isInterceptedMethod(member)) { 538 task.interceptorEmitter.recordMangledNameOfMemberMethod(member, name);
612 task.interceptorEmitter.interceptorInvocationNames.add(name); 539 FunctionSignature parameters = member.computeSignature(compiler);
540 bool needsStubs = !parameters.optionalParameters.isEmpty;
541 bool canTearOff = false;
kasperl 2013/10/21 10:25:44 Consider only using tearOffName (null could repres
ahe 2013/12/06 15:57:53 I forgot about this comment, but I'm weary of chan
542 String tearOffName;
543 if (member.isInstanceMember()) {
544 // Careful with operators.
545 canTearOff = compiler.codegenWorld.hasInvokedGetter(member, compiler);
546 tearOffName = namer.getterName(member);
547 } else {
548 canTearOff =
549 compiler.codegenWorld.staticFunctionsNeedingGetter.contains(member);
550 tearOffName = namer.getStaticClosureName(member);
613 } 551 }
614 code = task.metadataEmitter.extendWithMetadata(member, code); 552
615 builder.addProperty(name, code); 553 if (!canTearOff && !needsStubs && !backend.isNeededForReflection(member)) {
616 String reflectionName = task.getReflectionName(member, name); 554 builder.addProperty(name, code);
617 if (reflectionName != null) { 555 addBailout(member, builder);
618 var reflectable = 556 return;
619 js(backend.isAccessibleByReflection(member) ? '1' : '0');
620 builder.addProperty('+$reflectionName', reflectable);
621 jsAst.Node defaultValues =
622 task.metadataEmitter.reifyDefaultArguments(member);
623 if (defaultValues != null) {
624 String unmangledName = member.name.slowToString();
625 builder.addProperty('*$unmangledName', defaultValues);
626 }
627 } 557 }
628 code = backend.generatedBailoutCode[member]; 558 // This element is needed for reflection or needs additional stubs. So we
559 // need to retain additional information.
560
561 List expressions = [];
562
563 bool canBeReflected = backend.isAccessibleByReflection(member);
564 expressions
565 ..add(code)
566 ..add(member.isAccessor() ? js("null") : js.string(tearOffName))
567 // TODO(ahe): Obtain proper name.
568 ..add(js.string("call\$${parameters.requiredParameterCount}"))
569 ..add(canBeReflected ? parameters.requiredParameterCount + 1 : 0)
570 ..add(parameters.optionalParameterCount)
571 ..addAll(task.metadataEmitter.reifyDefaultArguments(member));
572
573 if (needsStubs) {
574 addParameterStubs(member, (String name, jsAst.Fun function) {
575 expressions.add(function);
576 expressions.add(js.string(name));
577 // TODO(ahe): Obtain proper call name.
578 expressions.add(js.string("call\$${function.params.length}"));
579 });
580 }
581
582 if (canBeReflected) {
583 expressions
584 ..add(js.string(member.name.slowToString()))
585 ..addAll(task.metadataEmitter.computeMetadata(member));
586 }
587
588 builder.addProperty(name, js.toExpression(expressions));
589
590 addBailout(member, builder);
591
592 // if (canTearOff) {
593 // emitDynamicFunctionGetter(member, builder.addProperty);
594 // }
595 }
596
597 void addBailout(FunctionElement member, ClassBuilder builder) {
598 jsAst.Expression code = backend.generatedBailoutCode[member];
629 if (code != null) { 599 if (code != null) {
630 builder.addProperty(namer.getBailoutName(member), code); 600 builder.addProperty(namer.getBailoutName(member), code);
631 } 601 }
632 if (member.isInstanceMember()) {
633 // TODO(ahe): Where is this done for static/top-level methods?
634 FunctionSignature parameters = member.computeSignature(compiler);
635 if (!parameters.optionalParameters.isEmpty) {
636 addParameterStubs(member, builder.addProperty);
637 }
638 }
639 } 602 }
640 603
641 void addMemberField(VariableElement member, ClassBuilder builder) { 604 void addMemberField(VariableElement member, ClassBuilder builder) {
642 // For now, do nothing. 605 // For now, do nothing.
643 } 606 }
644 } 607 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698