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

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

Issue 103863004: Revert "Generate tear-off closures dynamically." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 7 years 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].
11 class ContainerBuilder extends CodeEmitterHelper { 11 class ContainerBuilder extends CodeEmitterHelper {
12 final Map<Element, Element> staticGetters = new Map<Element, Element>(); 12 final Map<Element, Element> staticGetters = new Map<Element, Element>();
13 13
14 /// A cache of synthesized closures for top-level, static or 14 /// A cache of synthesized closures for top-level, static or
15 /// instance methods. 15 /// instance methods.
16 final Map<String, Element> methodClosures = <String, Element>{}; 16 final Map<String, Element> methodClosures = <String, Element>{};
17 17
18 /** 18 /**
19 * Generate stubs to handle invocation of methods with optional 19 * Generate stubs to handle invocation of methods with optional
20 * arguments. 20 * arguments.
21 * 21 *
22 * A method like [: foo([x]) :] may be invoked by the following 22 * A method like [: foo([x]) :] may be invoked by the following
23 * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this 23 * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this
24 * function for detailed examples. 24 * function for detailed examples.
25 */ 25 */
26 void addParameterStub(FunctionElement member, 26 void addParameterStub(FunctionElement member,
27 Selector selector, 27 Selector selector,
28 AddStubFunction addStub, 28 DefineStubFunction defineStub,
29 Set<String> alreadyGenerated) { 29 Set<String> alreadyGenerated) {
30 FunctionSignature parameters = member.computeSignature(compiler); 30 FunctionSignature parameters = member.computeSignature(compiler);
31 int positionalArgumentCount = selector.positionalArgumentCount; 31 int positionalArgumentCount = selector.positionalArgumentCount;
32 if (positionalArgumentCount == parameters.parameterCount) { 32 if (positionalArgumentCount == parameters.parameterCount) {
33 assert(selector.namedArgumentCount == 0); 33 assert(selector.namedArgumentCount == 0);
34 return; 34 return;
35 } 35 }
36 if (parameters.optionalParametersAreNamed 36 if (parameters.optionalParametersAreNamed
37 && selector.namedArgumentCount == parameters.optionalParameterCount) { 37 && selector.namedArgumentCount == parameters.optionalParameterCount) {
38 // If the selector has the same number of named arguments as the element, 38 // If the selector has the same number of named arguments as the element,
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
74 74
75 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; 75 int optionalParameterStart = positionalArgumentCount + extraArgumentCount;
76 // Includes extra receiver argument when using interceptor convention 76 // Includes extra receiver argument when using interceptor convention
77 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; 77 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1;
78 78
79 TreeElements elements = 79 TreeElements elements =
80 compiler.enqueuer.resolution.getCachedElements(member); 80 compiler.enqueuer.resolution.getCachedElements(member);
81 81
82 int parameterIndex = 0; 82 int parameterIndex = 0;
83 parameters.orderedForEachParameter((Element element) { 83 parameters.orderedForEachParameter((Element element) {
84 String jsName = backend.namer.safeName(element.name); 84 // Use generic names for closures to facilitate code sharing.
85 String jsName = member is ClosureInvocationElement
86 ? 'p${parameterIndex++}'
87 : backend.namer.safeName(element.name);
85 assert(jsName != receiverArgumentName); 88 assert(jsName != receiverArgumentName);
86 if (count < optionalParameterStart) { 89 if (count < optionalParameterStart) {
87 parametersBuffer[count] = new jsAst.Parameter(jsName); 90 parametersBuffer[count] = new jsAst.Parameter(jsName);
88 argumentsBuffer[count] = js(jsName); 91 argumentsBuffer[count] = js(jsName);
89 } else { 92 } else {
90 int index = names.indexOf(element.name); 93 int index = names.indexOf(element.name);
91 if (index != -1) { 94 if (index != -1) {
92 indexOfLastOptionalArgumentInParameters = count; 95 indexOfLastOptionalArgumentInParameters = count;
93 // 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
94 // one in the real method (which is in Dart source order). 97 // one in the real method (which is in Dart source order).
(...skipping 16 matching lines...) Expand all
111 } 114 }
112 count++; 115 count++;
113 }); 116 });
114 117
115 List body; 118 List body;
116 if (member.hasFixedBackendName()) { 119 if (member.hasFixedBackendName()) {
117 body = task.nativeEmitter.generateParameterStubStatements( 120 body = task.nativeEmitter.generateParameterStubStatements(
118 member, isInterceptedMethod, invocationName, 121 member, isInterceptedMethod, invocationName,
119 parametersBuffer, argumentsBuffer, 122 parametersBuffer, argumentsBuffer,
120 indexOfLastOptionalArgumentInParameters); 123 indexOfLastOptionalArgumentInParameters);
121 } else if (member.isInstanceMember()) { 124 } else {
122 body = [js.return_( 125 body = [js.return_(
123 js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))]; 126 js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))];
124 } else {
125 body = [js.return_(namer.elementAccess(member)(argumentsBuffer))];
126 } 127 }
127 128
128 jsAst.Fun function = js.fun(parametersBuffer, body); 129 jsAst.Fun function = js.fun(parametersBuffer, body);
129 130
130 addStub(selector, 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 }
131 } 139 }
132 140
133 void addParameterStubs(FunctionElement member, AddStubFunction defineStub, 141 void addParameterStubs(FunctionElement member,
134 [bool canTearOff = false]) { 142 DefineStubFunction defineStub) {
135 if (member.enclosingElement.isClosure()) {
136 ClosureClassElement cls = member.enclosingElement;
137 if (cls.supertype.element == compiler.boundClosureClass) {
138 compiler.internalErrorOnElement(cls.methodElement, 'Bound closure1.');
139 }
140 if (cls.methodElement.isInstanceMember()) {
141 compiler.internalErrorOnElement(cls.methodElement, 'Bound closure2.');
142 }
143 }
144
145 // We fill the lists depending on the selector. For example, 143 // We fill the lists depending on the selector. For example,
146 // take method foo: 144 // take method foo:
147 // foo(a, b, {c, d}); 145 // foo(a, b, {c, d});
148 // 146 //
149 // We may have multiple ways of calling foo: 147 // We may have multiple ways of calling foo:
150 // (1) foo(1, 2); 148 // (1) foo(1, 2);
151 // (2) foo(1, 2, c: 3); 149 // (2) foo(1, 2, c: 3);
152 // (3) foo(1, 2, d: 4); 150 // (3) foo(1, 2, d: 4);
153 // (4) foo(1, 2, c: 3, d: 4); 151 // (4) foo(1, 2, c: 3, d: 4);
154 // (5) foo(1, 2, d: 4, c: 3); 152 // (5) foo(1, 2, d: 4, c: 3);
155 // 153 //
156 // What we generate at the call sites are: 154 // What we generate at the call sites are:
157 // (1) foo$2(1, 2); 155 // (1) foo$2(1, 2);
158 // (2) foo$3$c(1, 2, 3); 156 // (2) foo$3$c(1, 2, 3);
159 // (3) foo$3$d(1, 2, 4); 157 // (3) foo$3$d(1, 2, 4);
160 // (4) foo$4$c$d(1, 2, 3, 4); 158 // (4) foo$4$c$d(1, 2, 3, 4);
161 // (5) foo$4$c$d(1, 2, 3, 4); 159 // (5) foo$4$c$d(1, 2, 3, 4);
162 // 160 //
163 // The stubs we generate are (expressed in Dart): 161 // The stubs we generate are (expressed in Dart):
164 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) 162 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null)
165 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); 163 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null);
166 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); 164 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d);
167 // (4) No stub generated, call is direct. 165 // (4) No stub generated, call is direct.
168 // (5) No stub generated, call is direct. 166 // (5) No stub generated, call is direct.
169 167
170 Set<Selector> selectors = member.isInstanceMember() 168 // Keep a cache of which stubs have already been generated, to
171 ? compiler.codegenWorld.invokedNames[member.name] 169 // avoid duplicates. Note that even if selectors are
172 : null; // No stubs needed for static methods. 170 // canonicalized, we would still need this cache: a typed selector
173 171 // on A and a typed selector on B could yield the same stub.
174 /// Returns all closure call selectors renamed to match this member. 172 Set<String> generatedStubNames = new Set<String>();
175 Set<Selector> callSelectorsAsNamed() { 173 bool isClosureInvocation =
176 if (!canTearOff) return null; 174 member.name == namer.closureInvocationSelectorName;
177 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ 175 if (backend.isNeededForReflection(member) ||
178 namer.closureInvocationSelectorName]; 176 (compiler.enabledFunctionApply && isClosureInvocation)) {
179 if (callSelectors == null) return null; 177 // If [Function.apply] is called, we pessimistically compile all
180 return callSelectors.map((Selector callSelector) { 178 // possible stubs for this closure.
181 return new Selector.call( 179 FunctionSignature signature = member.computeSignature(compiler);
182 member.name, member.getLibrary(), 180 Set<Selector> selectors = signature.optionalParametersAreNamed
183 callSelector.argumentCount, callSelector.namedArguments); 181 ? computeSeenNamedSelectors(member)
184 }).toSet(); 182 : computeOptionalSelectors(signature, member);
185 } 183 for (Selector selector in selectors) {
186 if (selectors == null) { 184 addParameterStub(member, selector, defineStub, generatedStubNames);
187 selectors = callSelectorsAsNamed(); 185 }
186 if (signature.optionalParametersAreNamed && isClosureInvocation) {
187 addCatchAllParameterStub(member, signature, defineStub);
188 }
189 } else {
190 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name];
188 if (selectors == null) return; 191 if (selectors == null) return;
189 } else {
190 Set<Selector> callSelectors = callSelectorsAsNamed();
191 if (callSelectors != null) {
192 selectors = selectors.union(callSelectors);
193 }
194 }
195 Set<Selector> untypedSelectors = new Set<Selector>();
196 if (selectors != null) {
197 for (Selector selector in selectors) { 192 for (Selector selector in selectors) {
198 if (!selector.appliesUnnamed(member, compiler)) continue; 193 if (!selector.applies(member, compiler)) continue;
199 if (untypedSelectors.add(selector.asUntyped)) { 194 addParameterStub(member, selector, defineStub, generatedStubNames);
200 // TODO(ahe): Is the last argument to [addParameterStub] needed? 195 }
201 addParameterStub(member, selector, defineStub, new Set<String>()); 196 }
202 } 197 }
203 } 198
204 } 199 Set<Selector> computeSeenNamedSelectors(FunctionElement element) {
205 if (canTearOff) { 200 Set<Selector> selectors = compiler.codegenWorld.invokedNames[element.name];
206 selectors = compiler.codegenWorld.invokedNames[ 201 Set<Selector> result = new Set<Selector>();
207 namer.closureInvocationSelectorName]; 202 if (selectors == null) return result;
208 if (selectors != null) { 203 for (Selector selector in selectors) {
209 for (Selector selector in selectors) { 204 if (!selector.applies(element, compiler)) continue;
210 selector = new Selector.call( 205 result.add(selector);
211 member.name, member.getLibrary(), 206 }
212 selector.argumentCount, selector.namedArguments); 207 return result;
213 if (!selector.appliesUnnamed(member, compiler)) continue; 208 }
214 if (untypedSelectors.add(selector)) { 209
215 // TODO(ahe): Is the last argument to [addParameterStub] needed? 210 void addCatchAllParameterStub(FunctionElement member,
216 addParameterStub(member, selector, defineStub, new Set<String>()); 211 FunctionSignature signature,
217 } 212 DefineStubFunction defineStub) {
218 } 213 // See Primities.applyFunction in js_helper.dart for details.
219 } 214 List<jsAst.Property> properties = <jsAst.Property>[];
220 } 215 for (Element element in signature.orderedOptionalParameters) {
216 String jsName = backend.namer.safeName(element.name);
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 <String>[]));
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 <String>[]));
253 }
254 return selectors;
255 }
256
257 void emitStaticFunctionGetters(CodeBuffer eagerBuffer) {
258 task.addComment('Static function getters', task.mainBuffer);
259 for (FunctionElement element in
260 Elements.sortedByPosition(staticGetters.keys)) {
261 Element closure = staticGetters[element];
262 CodeBuffer buffer =
263 task.isDeferred(element) ? task.deferredConstants : eagerBuffer;
264 String closureClass = namer.isolateAccess(closure);
265 String name = namer.getStaticClosureName(element);
266
267 String closureName = namer.getStaticClosureName(element);
268 jsAst.Node assignment = js(
269 'init.globalFunctions["$closureName"] ='
270 ' ${namer.globalObjectFor(element)}.$name ='
271 ' new $closureClass(#, "$closureName")',
272 namer.elementAccess(element));
273 buffer.write(jsAst.prettyPrint(assignment, compiler));
274 buffer.write('$N');
275 }
276 }
277
278 void emitStaticFunctionClosures() {
279 Set<FunctionElement> functionsNeedingGetter =
280 compiler.codegenWorld.staticFunctionsNeedingGetter;
281 for (FunctionElement element in
282 Elements.sortedByPosition(functionsNeedingGetter)) {
283 String superName = namer.getNameOfClass(compiler.closureClass);
284 int parameterCount = element.functionSignature.parameterCount;
285 String name = 'Closure\$$parameterCount';
286 assert(task.instantiatedClasses.contains(compiler.closureClass));
287
288 ClassElement closureClassElement = new ClosureClassElement(
289 null, name, compiler, element,
290 element.getCompilationUnit());
291 // Now add the methods on the closure class. The instance method does not
292 // have the correct name. Since [addParameterStubs] use the name to create
293 // its stubs we simply create a fake element with the correct name.
294 // Note: the callElement will not have any enclosingElement.
295 FunctionElement callElement =
296 new ClosureInvocationElement(namer.closureInvocationSelectorName,
297 element);
298
299 String invocationName = namer.instanceMethodName(callElement);
300 String mangledName = namer.getNameOfClass(closureClassElement);
301
302 // Define the constructor with a name so that Object.toString can
303 // find the class name of the closure class.
304 ClassBuilder closureBuilder = new ClassBuilder();
305 // If a static function is used as a closure we need to add its name
306 // in case it is used in spawnFunction.
307 String methodName = namer.STATIC_CLOSURE_NAME_NAME;
308 List<String> fieldNames = <String>[invocationName, methodName];
309 closureBuilder.addProperty('',
310 js.string("$superName;${fieldNames.join(',')}"));
311
312 addParameterStubs(callElement, closureBuilder.addProperty);
313
314 void emitFunctionTypeSignature(Element method, FunctionType methodType) {
315 RuntimeTypes rti = backend.rti;
316 // [:() => null:] is dummy encoding of [this] which is never needed for
317 // the encoding of the type of the static [method].
318 jsAst.Expression encoding =
319 rti.getSignatureEncoding(methodType, js('null'));
320 String operatorSignature = namer.operatorSignature();
321 // TODO(johnniwinther): Make MiniJsParser support function expressions.
322 closureBuilder.addProperty(operatorSignature, encoding);
323 }
324
325 FunctionType methodType = element.computeType(compiler);
326 Map<FunctionType, bool> functionTypeChecks =
327 task.typeTestEmitter.getFunctionTypeChecksOn(methodType);
328 task.typeTestEmitter.generateFunctionTypeTests(
329 element, methodType, functionTypeChecks,
330 emitFunctionTypeSignature);
331
332 closureClassElement =
333 addClosureIfNew(closureBuilder, closureClassElement, fieldNames);
334 staticGetters[element] = closureClassElement;
335
336 }
337 }
338
339 ClassElement addClosureIfNew(ClassBuilder builder,
340 ClassElement closure,
341 List<String> fieldNames) {
342 String key =
343 jsAst.prettyPrint(builder.toObjectInitializer(), compiler).getText();
344 return methodClosures.putIfAbsent(key, () {
345 String mangledName = namer.getNameOfClass(closure);
346 emitClosureInPrecompiledFunction(mangledName, fieldNames);
347 return closure;
348 });
349 }
350
351 void emitClosureInPrecompiledFunction(String mangledName,
352 List<String> fieldNames) {
353 List<String> fields = fieldNames;
354 String constructorName = mangledName;
355 task.precompiledFunction.add(new jsAst.FunctionDeclaration(
356 new jsAst.VariableDeclaration(constructorName),
357 js.fun(fields, fields.map(
358 (name) => js('this.$name = $name')).toList())));
359 task.precompiledFunction.addAll([
360 js('$constructorName.builtin\$cls = "$constructorName"'),
361 js('\$desc=\$collectedClasses.$constructorName'),
362 js.if_('\$desc instanceof Array', js('\$desc = \$desc[1]')),
363 js('$constructorName.prototype = \$desc'),
364 ]);
365
366 task.precompiledConstructorNames.add(js(constructorName));
221 } 367 }
222 368
223 /** 369 /**
224 * Documentation wanted -- johnniwinther 370 * Documentation wanted -- johnniwinther
225 * 371 *
372 * Invariant: [member] must be a declaration element.
373 */
374 void emitDynamicFunctionGetter(FunctionElement member,
375 DefineStubFunction defineStub) {
376 assert(invariant(member, member.isDeclaration));
377 assert(task.instantiatedClasses.contains(compiler.boundClosureClass));
378 // For every method that has the same name as a property-get we create a
379 // getter that returns a bound closure. Say we have a class 'A' with method
380 // 'foo' and somewhere in the code there is a dynamic property get of
381 // 'foo'. Then we generate the following code (in pseudo Dart/JavaScript):
382 //
383 // class A {
384 // foo(x, y, z) { ... } // Original function.
385 // get foo { return new BoundClosure499(this, "foo"); }
386 // }
387 // class BoundClosure499 extends BoundClosure {
388 // BoundClosure499(this.self, this.name);
389 // $call3(x, y, z) { return self[name](x, y, z); }
390 // }
391
392 bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0;
393 int parameterCount = member.parameterCount(compiler);
394
395 // Intercepted methods take an extra parameter, which is the
396 // receiver of the call.
397 bool inInterceptor = backend.isInterceptedMethod(member);
398 List<String> fieldNames = <String>[];
399 compiler.boundClosureClass.forEachInstanceField((_, Element field) {
400 fieldNames.add(namer.instanceFieldPropertyName(field));
401 });
402
403 ClassElement classElement = member.getEnclosingClass();
404 String name = inInterceptor
405 ? 'BoundClosure\$i${parameterCount}'
406 : 'BoundClosure\$${parameterCount}';
407
408 ClassElement closureClassElement = new ClosureClassElement(
409 null, name, compiler, member,
410 member.getCompilationUnit());
411 String superName = namer.getNameOfClass(closureClassElement.superclass);
412
413 // Define the constructor with a name so that Object.toString can
414 // find the class name of the closure class.
415 ClassBuilder boundClosureBuilder = new ClassBuilder();
416 boundClosureBuilder.addProperty('',
417 js.string("$superName;${fieldNames.join(',')}"));
418 // Now add the methods on the closure class. The instance method does not
419 // have the correct name. Since [addParameterStubs] use the name to create
420 // its stubs we simply create a fake element with the correct name.
421 // Note: the callElement will not have any enclosingElement.
422 FunctionElement callElement = new ClosureInvocationElement(
423 namer.closureInvocationSelectorName, member);
424
425 String invocationName = namer.instanceMethodName(callElement);
426
427 List<String> parameters = <String>[];
428 List<jsAst.Expression> arguments =
429 <jsAst.Expression>[js('this')[fieldNames[0]]];
430 if (inInterceptor) {
431 arguments.add(js('this')[fieldNames[2]]);
432 }
433 for (int i = 0; i < parameterCount; i++) {
434 String name = 'p$i';
435 parameters.add(name);
436 arguments.add(js(name));
437 }
438
439 jsAst.Expression fun = js.fun(
440 parameters,
441 js.return_(
442 js('this')[fieldNames[1]]['call'](arguments)));
443 boundClosureBuilder.addProperty(invocationName, fun);
444
445 addParameterStubs(callElement, boundClosureBuilder.addProperty);
446
447 void emitFunctionTypeSignature(Element method, FunctionType methodType) {
448 jsAst.Expression encoding = backend.rti.getSignatureEncoding(
449 methodType, js('this')[fieldNames[0]]);
450 String operatorSignature = namer.operatorSignature();
451 boundClosureBuilder.addProperty(operatorSignature, encoding);
452 }
453
454 DartType memberType = member.computeType(compiler);
455 Map<FunctionType, bool> functionTypeChecks =
456 task.typeTestEmitter.getFunctionTypeChecksOn(memberType);
457
458 task.typeTestEmitter.generateFunctionTypeTests(
459 member, memberType, functionTypeChecks,
460 emitFunctionTypeSignature);
461
462 closureClassElement =
463 addClosureIfNew(boundClosureBuilder, closureClassElement, fieldNames);
464
465 String closureClass = namer.isolateAccess(closureClassElement);
466
467 // And finally the getter.
468 String getterName = namer.getterName(member);
469 String targetName = namer.instanceMethodName(member);
470
471 parameters = <String>[];
472 jsAst.PropertyAccess method =
473 backend.namer.elementAccess(classElement)['prototype'][targetName];
474 arguments = <jsAst.Expression>[js('this'), method];
475
476 if (inInterceptor) {
477 String receiverArg = fieldNames[2];
478 parameters.add(receiverArg);
479 arguments.add(js(receiverArg));
480 } else {
481 // Put null in the intercepted receiver field.
482 arguments.add(new jsAst.LiteralNull());
483 }
484
485 arguments.add(js.string(targetName));
486
487 jsAst.Expression getterFunction = js.fun(
488 parameters, js.return_(js(closureClass).newWith(arguments)));
489
490 defineStub(getterName, getterFunction);
491 }
492
493 /**
494 * Documentation wanted -- johnniwinther
495 *
226 * Invariant: [member] must be a declaration element. 496 * Invariant: [member] must be a declaration element.
227 */ 497 */
228 void emitCallStubForGetter(Element member, 498 void emitCallStubForGetter(Element member,
229 Set<Selector> selectors, 499 Set<Selector> selectors,
230 AddPropertyFunction addProperty) { 500 DefineStubFunction defineStub) {
231 assert(invariant(member, member.isDeclaration)); 501 assert(invariant(member, member.isDeclaration));
232 LibraryElement memberLibrary = member.getLibrary(); 502 LibraryElement memberLibrary = member.getLibrary();
233 // If the method is intercepted, the stub gets the 503 // If the method is intercepted, the stub gets the
234 // receiver explicitely and we need to pass it to the getter call. 504 // receiver explicitely and we need to pass it to the getter call.
235 bool isInterceptedMethod = backend.isInterceptedMethod(member); 505 bool isInterceptedMethod = backend.isInterceptedMethod(member);
236 506
237 const String receiverArgumentName = r'$receiver'; 507 const String receiverArgumentName = r'$receiver';
238 508
239 jsAst.Expression buildGetter() { 509 jsAst.Expression buildGetter() {
240 if (member.isGetter()) { 510 if (member.isGetter()) {
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 for (int i = 0; i < selector.argumentCount; i++) { 542 for (int i = 0; i < selector.argumentCount; i++) {
273 String name = 'arg$i'; 543 String name = 'arg$i';
274 parameters.add(new jsAst.Parameter(name)); 544 parameters.add(new jsAst.Parameter(name));
275 arguments.add(js(name)); 545 arguments.add(js(name));
276 } 546 }
277 547
278 jsAst.Fun function = js.fun( 548 jsAst.Fun function = js.fun(
279 parameters, 549 parameters,
280 js.return_(buildGetter()[closureCallName](arguments))); 550 js.return_(buildGetter()[closureCallName](arguments)));
281 551
282 addProperty(invocationName, function); 552 defineStub(invocationName, function);
283 } 553 }
284 } 554 }
285 } 555 }
286 556
287 /** 557 /**
288 * Documentation wanted -- johnniwinther 558 * Documentation wanted -- johnniwinther
289 * 559 *
290 * Invariant: [member] must be a declaration element. 560 * Invariant: [member] must be a declaration element.
291 */ 561 */
292 void emitExtraAccessors(Element member, ClassBuilder builder) { 562 void emitExtraAccessors(Element member, ClassBuilder builder) {
293 assert(invariant(member, member.isDeclaration)); 563 assert(invariant(member, member.isDeclaration));
294 if (member.isGetter() || member.isField()) { 564 if (member.isGetter() || member.isField()) {
295 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; 565 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name];
296 if (selectors != null && !selectors.isEmpty) { 566 if (selectors != null && !selectors.isEmpty) {
297 emitCallStubForGetter(member, selectors, builder.addProperty); 567 emitCallStubForGetter(member, selectors, builder.addProperty);
298 } 568 }
569 } else if (member.isFunction()) {
570 if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) {
571 emitDynamicFunctionGetter(member, builder.addProperty);
572 }
299 } 573 }
300 } 574 }
301 575
302 void addMember(Element member, ClassBuilder builder) { 576 void addMember(Element member, ClassBuilder builder) {
303 assert(invariant(member, member.isDeclaration)); 577 assert(invariant(member, member.isDeclaration));
304 578
305 if (member.isField()) { 579 if (member.isField()) {
306 addMemberField(member, builder); 580 addMemberField(member, builder);
307 } else if (member.isFunction() || 581 } else if (member.isFunction() ||
308 member.isGenerativeConstructorBody() || 582 member.isGenerativeConstructorBody() ||
309 member.isGenerativeConstructor() || 583 member.isGenerativeConstructor() ||
310 member.isAccessor()) { 584 member.isAccessor()) {
311 addMemberMethod(member, builder); 585 addMemberMethod(member, builder);
312 } else { 586 } else {
313 compiler.internalErrorOnElement( 587 compiler.internalErrorOnElement(
314 member, 'unexpected kind: "${member.kind}"'); 588 member, 'unexpected kind: "${member.kind}"');
315 } 589 }
316 if (member.isInstanceMember()) emitExtraAccessors(member, builder); 590 if (member.isInstanceMember()) emitExtraAccessors(member, builder);
317 } 591 }
318 592
319 void addMemberMethod(FunctionElement member, ClassBuilder builder) { 593 void addMemberMethod(FunctionElement member, ClassBuilder builder) {
320 if (member.isAbstract) return; 594 if (member.isAbstract) return;
321 jsAst.Expression code = backend.generatedCode[member]; 595 jsAst.Expression code = backend.generatedCode[member];
322 if (code == null) return; 596 if (code == null) return;
323 String name = namer.getNameOfMember(member); 597 String name = namer.getNameOfMember(member);
324 task.interceptorEmitter.recordMangledNameOfMemberMethod(member, name); 598 if (backend.isInterceptedMethod(member)) {
325 FunctionSignature parameters = member.computeSignature(compiler); 599 task.interceptorEmitter.interceptorInvocationNames.add(name);
326 bool needsStubs = !parameters.optionalParameters.isEmpty; 600 }
327 bool canTearOff = false; 601 code = task.metadataEmitter.extendWithMetadata(member, code);
328 bool isClosure = false; 602 builder.addProperty(name, code);
329 String tearOffName; 603 String reflectionName = task.getReflectionName(member, name);
330 if (!member.isFunction() || member.isConstructor() || member.isAccessor()) { 604 if (reflectionName != null) {
331 canTearOff = false; 605 var reflectable =
332 } else if (member.isInstanceMember()) { 606 js(backend.isAccessibleByReflection(member) ? '1' : '0');
333 if (member.getEnclosingClass().isClosure()) { 607 builder.addProperty('+$reflectionName', reflectable);
334 canTearOff = false; 608 jsAst.Node defaultValues =
335 isClosure = true; 609 task.metadataEmitter.reifyDefaultArguments(member);
336 } else { 610 if (defaultValues != null) {
337 // Careful with operators. 611 String unmangledName = member.name;
338 canTearOff = compiler.codegenWorld.hasInvokedGetter(member, compiler); 612 builder.addProperty('*$unmangledName', defaultValues);
339 tearOffName = namer.getterName(member);
340 } 613 }
341 } else {
342 canTearOff =
343 compiler.codegenWorld.staticFunctionsNeedingGetter.contains(member);
344 tearOffName = namer.getStaticClosureName(member);
345 } 614 }
346 615 if (member.isInstanceMember()) {
347 bool canBeReflected = backend.isAccessibleByReflection(member); 616 // TODO(ahe): Where is this done for static/top-level methods?
348 bool needStructuredInfo = 617 FunctionSignature parameters = member.computeSignature(compiler);
349 canTearOff || canBeReflected || compiler.enabledFunctionApply; 618 if (!parameters.optionalParameters.isEmpty) {
350 if (!needStructuredInfo) { 619 addParameterStubs(member, builder.addProperty);
351 builder.addProperty(name, code);
352 if (needsStubs) {
353 addParameterStubs(
354 member,
355 (Selector selector, jsAst.Fun function) {
356 builder.addProperty(namer.invocationName(selector), function);
357 });
358 } 620 }
359 return;
360 } 621 }
361
362 if (canTearOff) {
363 assert(invariant(member, !member.isGenerativeConstructor()));
364 assert(invariant(member, !member.isGenerativeConstructorBody()));
365 assert(invariant(member, !member.isConstructor()));
366 }
367
368 // This element is needed for reflection or needs additional stubs. So we
369 // need to retain additional information.
370
371 // The information is stored in an array with this format:
372 //
373 // 1. The JS function for this member.
374 // 2. First stub.
375 // 3. Name of first stub.
376 // ...
377 // M. Call name of this member.
378 // M+1. Call name of first stub.
379 // ...
380 // N. Getter name for tearOff.
381 // N+1. (Required parameter count << 1) + (member.isAccessor() ? 1 : 0).
382 // N+2. (Optional parameter count << 1) +
383 // (parameters.optionalParametersAreNamed ? 1 : 0).
384 // N+3. Index to function type in constant pool.
385 // N+4. First default argument.
386 // ...
387 // O. First parameter name (if needed for reflection or Function.apply).
388 // ...
389 // P. Unmangled name (if reflectable).
390 // P+1. First metadata (if reflectable).
391 // ...
392
393 List expressions = [];
394
395 String callSelectorString = 'null';
396 if (member.isFunction()) {
397 Selector callSelector =
398 new Selector.fromElement(member, compiler).toCallSelector();
399 callSelectorString = '"${namer.invocationName(callSelector)}"';
400 }
401
402 // On [requiredParameterCount], the lower bit is set if this method can be
403 // called reflectively.
404 int requiredParameterCount = parameters.requiredParameterCount << 1;
405 if (member.isAccessor()) requiredParameterCount++;
406
407 int optionalParameterCount = parameters.optionalParameterCount << 1;
408 if (parameters.optionalParametersAreNamed) optionalParameterCount++;
409
410 expressions.add(code);
411
412 // TODO(ahe): Remove comments from output.
413 List tearOffInfo =
414 [new jsAst.LiteralString('$callSelectorString /* tearOffInfo */')];
415
416 if (needsStubs || canTearOff) {
417 addParameterStubs(member, (Selector selector, jsAst.Fun function) {
418 expressions.add(function);
419 if (member.isInstanceMember()) {
420 Set invokedSelectors =
421 compiler.codegenWorld.invokedNames[member.name];
422 if (invokedSelectors != null && invokedSelectors.contains(selector)) {
423 expressions.add(js.string(namer.invocationName(selector)));
424 } else {
425 // Don't add a stub for calling this as a regular instance method,
426 // we only need the "call" stub for implicit closures of this
427 // method.
428 expressions.add("null");
429 }
430 } else {
431 // Static methods don't need "named" stubs as the default arguments
432 // are inlined at call sites. But static methods might need "call"
433 // stubs for implicit closures.
434 expressions.add("null");
435 // TOOD(ahe): Since we know when reading static data versus instance
436 // data, we can eliminate this element.
437 }
438 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[
439 namer.closureInvocationSelectorName];
440 Selector callSelector = selector.toCallSelector();
441 String callSelectorString = 'null';
442 if (canTearOff && callSelectors != null &&
443 callSelectors.contains(callSelector)) {
444 callSelectorString = '"${namer.invocationName(callSelector)}"';
445 }
446 tearOffInfo.add(
447 new jsAst.LiteralString('$callSelectorString /* tearOffInfo */'));
448 }, canTearOff);
449 }
450
451 jsAst.Expression memberTypeExpression;
452 if ((canTearOff || canBeReflected) &&
453 !member.isGenerativeConstructorBody()) {
454 DartType memberType = member.computeType(compiler);
455 if (memberType.containsTypeVariables) {
456 jsAst.Expression thisAccess = js(r'this.$receiver');
457 memberTypeExpression =
458 backend.rti.getSignatureEncoding(memberType, thisAccess);
459 } else {
460 memberTypeExpression =
461 js.toExpression(task.metadataEmitter.reifyType(memberType));
462 }
463 } else {
464 memberTypeExpression = js('null');
465 }
466
467 expressions
468 ..addAll(tearOffInfo)
469 ..add((tearOffName == null || member.isAccessor())
470 ? js("null") : js.string(tearOffName))
471 ..add(requiredParameterCount)
472 ..add(optionalParameterCount)
473 ..add(memberTypeExpression)
474 ..addAll(task.metadataEmitter.reifyDefaultArguments(member));
475
476 if (canBeReflected || compiler.enabledFunctionApply) {
477 parameters.orderedForEachParameter((Element parameter) {
478 expressions.add(task.metadataEmitter.reifyName(parameter.name));
479 });
480 }
481 if (canBeReflected) {
482 jsAst.LiteralString reflectionName;
483 if (member.isConstructor()) {
484 String reflectionNameString = task.getReflectionName(member, name);
485 reflectionName =
486 new jsAst.LiteralString(
487 '"new ${Elements.reconstructConstructorName(member)}"'
488 ' /* $reflectionNameString */');
489 } else {
490 reflectionName = js.string(member.name);
491 }
492 expressions
493 ..add(reflectionName)
494 ..addAll(task.metadataEmitter.computeMetadata(member));
495 } else if (isClosure && compiler.enabledFunctionApply) {
496 expressions.add(js.string(member.name));
497 }
498
499 builder.addProperty(name, js.toExpression(expressions));
500 } 622 }
501 623
502 void addMemberField(VariableElement member, ClassBuilder builder) { 624 void addMemberField(VariableElement member, ClassBuilder builder) {
503 // For now, do nothing. 625 // For now, do nothing.
504 } 626 }
505 } 627 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698