OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |