Chromium Code Reviews| 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 |