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 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
64 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); | 64 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); |
65 // The arguments that will be passed to the real method. | 65 // The arguments that will be passed to the real method. |
66 List<jsAst.Expression> argumentsBuffer = | 66 List<jsAst.Expression> argumentsBuffer = |
67 new List<jsAst.Expression>( | 67 new List<jsAst.Expression>( |
68 parameters.parameterCount + extraArgumentCount); | 68 parameters.parameterCount + extraArgumentCount); |
69 | 69 |
70 int count = 0; | 70 int count = 0; |
71 if (isInterceptedMethod) { | 71 if (isInterceptedMethod) { |
72 count++; | 72 count++; |
73 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); | 73 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); |
74 argumentsBuffer[0] = js(receiverArgumentName); | 74 argumentsBuffer[0] = js('#', receiverArgumentName); |
75 task.interceptorEmitter.interceptorInvocationNames.add(invocationName); | 75 task.interceptorEmitter.interceptorInvocationNames.add(invocationName); |
76 } | 76 } |
77 | 77 |
78 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; | 78 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; |
79 // Includes extra receiver argument when using interceptor convention | 79 // Includes extra receiver argument when using interceptor convention |
80 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; | 80 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; |
81 | 81 |
82 TreeElements elements = | 82 TreeElements elements = |
83 compiler.enqueuer.resolution.getCachedElements(member); | 83 compiler.enqueuer.resolution.getCachedElements(member); |
84 | 84 |
85 int parameterIndex = 0; | 85 int parameterIndex = 0; |
86 parameters.orderedForEachParameter((Element element) { | 86 parameters.orderedForEachParameter((Element element) { |
87 String jsName = backend.namer.safeName(element.name); | 87 String jsName = backend.namer.safeName(element.name); |
88 assert(jsName != receiverArgumentName); | 88 assert(jsName != receiverArgumentName); |
89 if (count < optionalParameterStart) { | 89 if (count < optionalParameterStart) { |
90 parametersBuffer[count] = new jsAst.Parameter(jsName); | 90 parametersBuffer[count] = new jsAst.Parameter(jsName); |
91 argumentsBuffer[count] = js(jsName); | 91 argumentsBuffer[count] = js('#', jsName); |
92 } else { | 92 } else { |
93 int index = names.indexOf(element.name); | 93 int index = names.indexOf(element.name); |
94 if (index != -1) { | 94 if (index != -1) { |
95 indexOfLastOptionalArgumentInParameters = count; | 95 indexOfLastOptionalArgumentInParameters = count; |
96 // The order of the named arguments is not the same as the | 96 // The order of the named arguments is not the same as the |
97 // one in the real method (which is in Dart source order). | 97 // one in the real method (which is in Dart source order). |
98 argumentsBuffer[count] = js(jsName); | 98 argumentsBuffer[count] = js('#', jsName); |
99 parametersBuffer[optionalParameterStart + index] = | 99 parametersBuffer[optionalParameterStart + index] = |
100 new jsAst.Parameter(jsName); | 100 new jsAst.Parameter(jsName); |
101 } else { | 101 } else { |
102 Constant value = handler.getConstantForVariable(element); | 102 Constant value = handler.getConstantForVariable(element); |
103 if (value == null) { | 103 if (value == null) { |
104 argumentsBuffer[count] = task.constantReference(new NullConstant()); | 104 argumentsBuffer[count] = task.constantReference(new NullConstant()); |
105 } else { | 105 } else { |
106 if (!value.isNull) { | 106 if (!value.isNull) { |
107 // If the value is the null constant, we should not pass it | 107 // If the value is the null constant, we should not pass it |
108 // down to the native method. | 108 // down to the native method. |
109 indexOfLastOptionalArgumentInParameters = count; | 109 indexOfLastOptionalArgumentInParameters = count; |
110 } | 110 } |
111 argumentsBuffer[count] = task.constantReference(value); | 111 argumentsBuffer[count] = task.constantReference(value); |
112 } | 112 } |
113 } | 113 } |
114 } | 114 } |
115 count++; | 115 count++; |
116 }); | 116 }); |
117 | 117 |
118 List body; | 118 var body; // List or jsAst.Statement. |
119 if (member.hasFixedBackendName()) { | 119 if (member.hasFixedBackendName()) { |
120 body = task.nativeEmitter.generateParameterStubStatements( | 120 body = task.nativeEmitter.generateParameterStubStatements( |
121 member, isInterceptedMethod, invocationName, | 121 member, isInterceptedMethod, invocationName, |
122 parametersBuffer, argumentsBuffer, | 122 parametersBuffer, argumentsBuffer, |
123 indexOfLastOptionalArgumentInParameters); | 123 indexOfLastOptionalArgumentInParameters); |
124 } else if (member.isInstanceMember()) { | 124 } else if (member.isInstanceMember()) { |
125 if (needsSuperGetter(member)) { | 125 if (needsSuperGetter(member)) { |
126 ClassElement superClass = member.getEnclosingClass(); | 126 ClassElement superClass = member.getEnclosingClass(); |
127 String methodName = namer.getNameOfInstanceMember(member); | 127 String methodName = namer.getNameOfInstanceMember(member); |
128 // When redirecting, we must ensure that we don't end up in a subclass. | 128 // When redirecting, we must ensure that we don't end up in a subclass. |
129 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. | 129 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. |
130 // Instead we need to call the statically resolved target. | 130 // Instead we need to call the statically resolved target. |
131 // `<class>.prototype.bar$1.call(this, argument0, ...)`. | 131 // `<class>.prototype.bar$1.call(this, argument0, ...)`. |
132 body = [js.return_( | 132 body = js.statement( |
133 backend.namer.elementAccess(superClass)['prototype'][methodName] | 133 'return #.prototype.#.call(this, #);', |
134 ["call"](["this"]..addAll(argumentsBuffer)))]; | 134 [backend.namer.elementAccess(superClass), methodName, |
135 argumentsBuffer]); | |
135 } else { | 136 } else { |
136 body = [js.return_( | 137 body = js.statement( |
137 js('this') | 138 'return this.#(#);', |
138 [namer.getNameOfInstanceMember(member)](argumentsBuffer))]; | 139 [namer.getNameOfInstanceMember(member), argumentsBuffer]); |
139 } | 140 } |
140 } else { | 141 } else { |
141 body = [js.return_(namer.elementAccess(member)(argumentsBuffer))]; | 142 body = js.statement('return #(#)', |
143 [namer.elementAccess(member), argumentsBuffer]); | |
142 } | 144 } |
143 | 145 |
144 jsAst.Fun function = js.fun(parametersBuffer, body); | 146 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]); |
145 | 147 |
146 addStub(selector, function); | 148 addStub(selector, function); |
147 } | 149 } |
148 | 150 |
149 void addParameterStubs(FunctionElement member, AddStubFunction defineStub, | 151 void addParameterStubs(FunctionElement member, AddStubFunction defineStub, |
150 [bool canTearOff = false]) { | 152 [bool canTearOff = false]) { |
151 if (member.enclosingElement.isClosure()) { | 153 if (member.enclosingElement.isClosure()) { |
152 ClosureClassElement cls = member.enclosingElement; | 154 ClosureClassElement cls = member.enclosingElement; |
153 if (cls.supertype.element == compiler.boundClosureClass) { | 155 if (cls.supertype.element == compiler.boundClosureClass) { |
154 compiler.internalError(cls.methodElement, 'Bound closure1.'); | 156 compiler.internalError(cls.methodElement, 'Bound closure1.'); |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
260 backend.isInterceptorClass(member.getEnclosingClass()); | 262 backend.isInterceptorClass(member.getEnclosingClass()); |
261 | 263 |
262 const String receiverArgumentName = r'$receiver'; | 264 const String receiverArgumentName = r'$receiver'; |
263 | 265 |
264 jsAst.Expression buildGetter() { | 266 jsAst.Expression buildGetter() { |
265 jsAst.Expression receiver = | 267 jsAst.Expression receiver = |
266 js(isInterceptorClass ? receiverArgumentName : 'this'); | 268 js(isInterceptorClass ? receiverArgumentName : 'this'); |
267 if (member.isGetter()) { | 269 if (member.isGetter()) { |
268 String getterName = namer.getterName(member); | 270 String getterName = namer.getterName(member); |
269 if (isInterceptedMethod) { | 271 if (isInterceptedMethod) { |
270 return js('this')[getterName](<jsAst.Expression>[receiver]); | 272 //return js('this')[getterName](<jsAst.Expression>[receiver]); |
floitsch
2014/04/22 16:11:18
dead code.
sra1
2014/04/23 02:33:50
Done.
| |
273 return js('this.#(#)', [getterName, receiver]); | |
271 } | 274 } |
272 return receiver[getterName](<jsAst.Expression>[]); | 275 //return receiver[getterName](<jsAst.Expression>[]); |
floitsch
2014/04/22 16:11:18
ditto.
sra1
2014/04/23 02:33:50
Done.
| |
276 return js('#.#()', [receiver, getterName]); | |
273 } else { | 277 } else { |
274 String fieldName = namer.instanceFieldPropertyName(member); | 278 String fieldName = namer.instanceFieldPropertyName(member); |
275 return receiver[fieldName]; | 279 return js('#.#', [receiver, fieldName]); |
276 } | 280 } |
277 } | 281 } |
278 | 282 |
279 // Two selectors may match but differ only in type. To avoid generating | 283 // Two selectors may match but differ only in type. To avoid generating |
280 // identical stubs for each we track untyped selectors which already have | 284 // identical stubs for each we track untyped selectors which already have |
281 // stubs. | 285 // stubs. |
282 Set<Selector> generatedSelectors = new Set<Selector>(); | 286 Set<Selector> generatedSelectors = new Set<Selector>(); |
283 for (Selector selector in selectors) { | 287 for (Selector selector in selectors) { |
284 if (selector.applies(member, compiler)) { | 288 if (selector.applies(member, compiler)) { |
285 selector = selector.asUntyped; | 289 selector = selector.asUntyped; |
286 if (generatedSelectors.contains(selector)) continue; | 290 if (generatedSelectors.contains(selector)) continue; |
287 generatedSelectors.add(selector); | 291 generatedSelectors.add(selector); |
288 | 292 |
289 String invocationName = namer.invocationName(selector); | 293 String invocationName = namer.invocationName(selector); |
290 Selector callSelector = new Selector.callClosureFrom(selector); | 294 Selector callSelector = new Selector.callClosureFrom(selector); |
291 String closureCallName = namer.invocationName(callSelector); | 295 String closureCallName = namer.invocationName(callSelector); |
292 | 296 |
293 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; | 297 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; |
294 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | 298 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
295 if (isInterceptedMethod) { | 299 if (isInterceptedMethod) { |
296 parameters.add(new jsAst.Parameter(receiverArgumentName)); | 300 parameters.add(new jsAst.Parameter(receiverArgumentName)); |
297 } | 301 } |
298 | 302 |
299 for (int i = 0; i < selector.argumentCount; i++) { | 303 for (int i = 0; i < selector.argumentCount; i++) { |
300 String name = 'arg$i'; | 304 String name = 'arg$i'; |
301 parameters.add(new jsAst.Parameter(name)); | 305 parameters.add(new jsAst.Parameter(name)); |
302 arguments.add(js(name)); | 306 arguments.add(js('#', name)); |
303 } | 307 } |
304 | 308 |
305 jsAst.Fun function = js.fun( | 309 jsAst.Fun function = js( |
306 parameters, | 310 'function(#) { return #.#(#); }', |
307 js.return_(buildGetter()[closureCallName](arguments))); | 311 [ parameters, buildGetter(), closureCallName, arguments]); |
308 | 312 |
309 addProperty(invocationName, function); | 313 addProperty(invocationName, function); |
310 } | 314 } |
311 } | 315 } |
312 } | 316 } |
313 | 317 |
314 /** | 318 /** |
315 * Documentation wanted -- johnniwinther | 319 * Documentation wanted -- johnniwinther |
316 * | 320 * |
317 * Invariant: [member] must be a declaration element. | 321 * Invariant: [member] must be a declaration element. |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
417 // N+4. First default argument. | 421 // N+4. First default argument. |
418 // ... | 422 // ... |
419 // O. First parameter name (if needed for reflection or Function.apply). | 423 // O. First parameter name (if needed for reflection or Function.apply). |
420 // ... | 424 // ... |
421 // P. Unmangled name (if reflectable). | 425 // P. Unmangled name (if reflectable). |
422 // P+1. First metadata (if reflectable). | 426 // P+1. First metadata (if reflectable). |
423 // ... | 427 // ... |
424 // TODO(ahe): Consider one of the parameter counts can be replaced by the | 428 // TODO(ahe): Consider one of the parameter counts can be replaced by the |
425 // length property of the JavaScript function object. | 429 // length property of the JavaScript function object. |
426 | 430 |
427 List expressions = []; | 431 List<jsAst.Expression> expressions = <jsAst.Expression>[]; |
428 | 432 |
429 String callSelectorString = 'null'; | 433 String callSelectorString = 'null'; |
430 if (member.isFunction()) { | 434 if (member.isFunction()) { |
431 Selector callSelector = | 435 Selector callSelector = |
432 new Selector.fromElement(member, compiler).toCallSelector(); | 436 new Selector.fromElement(member, compiler).toCallSelector(); |
433 callSelectorString = '"${namer.invocationName(callSelector)}"'; | 437 callSelectorString = '"${namer.invocationName(callSelector)}"'; |
434 } | 438 } |
435 | 439 |
436 // On [requiredParameterCount], the lower bit is set if this method can be | 440 // On [requiredParameterCount], the lower bit is set if this method can be |
437 // called reflectively. | 441 // called reflectively. |
438 int requiredParameterCount = parameters.requiredParameterCount << 1; | 442 int requiredParameterCount = parameters.requiredParameterCount << 1; |
439 if (member.isAccessor()) requiredParameterCount++; | 443 if (member.isAccessor()) requiredParameterCount++; |
440 | 444 |
441 int optionalParameterCount = parameters.optionalParameterCount << 1; | 445 int optionalParameterCount = parameters.optionalParameterCount << 1; |
442 if (parameters.optionalParametersAreNamed) optionalParameterCount++; | 446 if (parameters.optionalParametersAreNamed) optionalParameterCount++; |
443 | 447 |
444 expressions.add(code); | 448 expressions.add(code); |
445 | 449 |
450 // TODO(sra): Don't use LiteralString for non-strings. | |
446 List tearOffInfo = [new jsAst.LiteralString(callSelectorString)]; | 451 List tearOffInfo = [new jsAst.LiteralString(callSelectorString)]; |
447 | 452 |
448 if (needsStubs || canTearOff) { | 453 if (needsStubs || canTearOff) { |
449 addParameterStubs(member, (Selector selector, jsAst.Fun function) { | 454 addParameterStubs(member, (Selector selector, jsAst.Fun function) { |
450 expressions.add(function); | 455 expressions.add(function); |
451 if (member.isInstanceMember()) { | 456 if (member.isInstanceMember()) { |
452 Set invokedSelectors = | 457 Set invokedSelectors = |
453 compiler.codegenWorld.invokedNames[member.name]; | 458 compiler.codegenWorld.invokedNames[member.name]; |
454 expressions.add(js.string(namer.invocationName(selector))); | 459 expressions.add(js.string(namer.invocationName(selector))); |
455 } else { | 460 } else { |
456 expressions.add("null"); | 461 expressions.add(js('null')); |
457 // TOOD(ahe): Since we know when reading static data versus instance | 462 // TOOD(ahe): Since we know when reading static data versus instance |
458 // data, we can eliminate this element. | 463 // data, we can eliminate this element. |
459 } | 464 } |
460 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ | 465 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ |
461 namer.closureInvocationSelectorName]; | 466 namer.closureInvocationSelectorName]; |
462 Selector callSelector = selector.toCallSelector(); | 467 Selector callSelector = selector.toCallSelector(); |
463 String callSelectorString = 'null'; | 468 String callSelectorString = 'null'; |
464 if (canTearOff && callSelectors != null && | 469 if (canTearOff && callSelectors != null && |
465 callSelectors.contains(callSelector)) { | 470 callSelectors.contains(callSelector)) { |
466 callSelectorString = '"${namer.invocationName(callSelector)}"'; | 471 callSelectorString = '"${namer.invocationName(callSelector)}"'; |
(...skipping 10 matching lines...) Expand all Loading... | |
477 memberType = body.constructor.type; | 482 memberType = body.constructor.type; |
478 } else { | 483 } else { |
479 memberType = member.type; | 484 memberType = member.type; |
480 } | 485 } |
481 if (memberType.containsTypeVariables) { | 486 if (memberType.containsTypeVariables) { |
482 jsAst.Expression thisAccess = js(r'this.$receiver'); | 487 jsAst.Expression thisAccess = js(r'this.$receiver'); |
483 memberTypeExpression = | 488 memberTypeExpression = |
484 backend.rti.getSignatureEncoding(memberType, thisAccess); | 489 backend.rti.getSignatureEncoding(memberType, thisAccess); |
485 } else { | 490 } else { |
486 memberTypeExpression = | 491 memberTypeExpression = |
487 js.toExpression(task.metadataEmitter.reifyType(memberType)); | 492 js.number(task.metadataEmitter.reifyType(memberType)); |
488 } | 493 } |
489 } else { | 494 } else { |
490 memberTypeExpression = js('null'); | 495 memberTypeExpression = js('null'); |
491 } | 496 } |
492 | 497 |
493 expressions | 498 expressions |
494 ..addAll(tearOffInfo) | 499 ..addAll(tearOffInfo) |
495 ..add((tearOffName == null || member.isAccessor()) | 500 ..add((tearOffName == null || member.isAccessor()) |
496 ? js("null") : js.string(tearOffName)) | 501 ? js("null") : js.string(tearOffName)) |
497 ..add(requiredParameterCount) | 502 ..add(js.number(requiredParameterCount)) |
498 ..add(optionalParameterCount) | 503 ..add(js.number(optionalParameterCount)) |
499 ..add(memberTypeExpression) | 504 ..add(memberTypeExpression) |
500 ..addAll(task.metadataEmitter.reifyDefaultArguments(member)); | 505 ..addAll( |
506 task.metadataEmitter.reifyDefaultArguments(member).map(js.number)); | |
501 | 507 |
502 if (canBeReflected || canBeApplied) { | 508 if (canBeReflected || canBeApplied) { |
503 parameters.forEachParameter((Element parameter) { | 509 parameters.forEachParameter((Element parameter) { |
504 expressions.add(task.metadataEmitter.reifyName(parameter.name)); | 510 expressions.add( |
511 js.number(task.metadataEmitter.reifyName(parameter.name))); | |
505 if (backend.mustRetainMetadata) { | 512 if (backend.mustRetainMetadata) { |
506 List<MetadataAnnotation> annotations = parameter.metadata.toList(); | 513 List<MetadataAnnotation> annotations = parameter.metadata.toList(); |
507 Iterable<int> metadataIndices = | 514 Iterable<int> metadataIndices = |
508 annotations.map((MetadataAnnotation annotation) { | 515 annotations.map((MetadataAnnotation annotation) { |
509 Constant constant = | 516 Constant constant = |
510 backend.constants.getConstantForMetadata(annotation); | 517 backend.constants.getConstantForMetadata(annotation); |
511 backend.constants.addCompileTimeConstantForEmission(constant); | 518 backend.constants.addCompileTimeConstantForEmission(constant); |
512 return task.metadataEmitter.reifyMetadata(annotation); | 519 return task.metadataEmitter.reifyMetadata(annotation); |
513 }); | 520 }); |
514 expressions.add(metadataIndices.isNotEmpty ? metadataIndices.toList() | 521 expressions.add( |
515 : js('[]')); | 522 new jsAst.ArrayInitializer.from(metadataIndices.map(js.number))); |
516 } | 523 } |
517 }); | 524 }); |
518 } | 525 } |
519 if (canBeReflected) { | 526 if (canBeReflected) { |
520 jsAst.LiteralString reflectionName; | 527 jsAst.LiteralString reflectionName; |
521 if (member.isConstructor()) { | 528 if (member.isConstructor()) { |
522 String reflectionNameString = task.getReflectionName(member, name); | 529 String reflectionNameString = task.getReflectionName(member, name); |
523 reflectionName = | 530 reflectionName = |
524 new jsAst.LiteralString( | 531 new jsAst.LiteralString( |
525 '"new ${Elements.reconstructConstructorName(member)}"'); | 532 '"new ${Elements.reconstructConstructorName(member)}"'); |
526 } else { | 533 } else { |
527 reflectionName = js.string(member.name); | 534 reflectionName = js.string(member.name); |
528 } | 535 } |
529 expressions | 536 expressions |
530 ..add(reflectionName) | 537 ..add(reflectionName) |
531 ..addAll(task.metadataEmitter.computeMetadata(member)); | 538 ..addAll(task.metadataEmitter.computeMetadata(member).map(js.number)); |
532 } else if (isClosure && canBeApplied) { | 539 } else if (isClosure && canBeApplied) { |
533 expressions.add(js.string(member.name)); | 540 expressions.add(js.string(member.name)); |
534 } | 541 } |
535 | 542 builder.addProperty(name, new jsAst.ArrayInitializer.from(expressions)); |
536 builder.addProperty(name, js.toExpression(expressions)); | |
537 } | 543 } |
538 | 544 |
539 void addMemberField(VariableElement member, ClassBuilder builder) { | 545 void addMemberField(VariableElement member, ClassBuilder builder) { |
540 // For now, do nothing. | 546 // For now, do nothing. |
541 } | 547 } |
542 } | 548 } |
OLD | NEW |