OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of dart2js.js_emitter; | |
6 | |
7 /// This class should morph into something that makes it easy to build | |
8 /// JavaScript representations of libraries, class-sides, and instance-sides. | |
9 /// Initially, it is just a placeholder for code that is moved from | |
10 /// [CodeEmitterTask]. | |
11 class ContainerBuilder extends CodeEmitterHelper { | |
12 final Map<Element, Element> staticGetters = new Map<Element, Element>(); | |
13 | |
14 bool needsSuperGetter(FunctionElement element) => | |
15 compiler.codegenWorld.methodsNeedingSuperGetter.contains(element); | |
16 | |
17 /** | |
18 * Generate stubs to handle invocation of methods with optional | |
19 * arguments. | |
20 * | |
21 * A method like [: foo([x]) :] may be invoked by the following | |
22 * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this | |
23 * function for detailed examples. | |
24 */ | |
25 void addParameterStub(FunctionElement member, | |
26 Selector selector, | |
27 AddStubFunction addStub, | |
28 Set<String> alreadyGenerated) { | |
29 FunctionSignature parameters = member.functionSignature; | |
30 int positionalArgumentCount = selector.positionalArgumentCount; | |
31 if (positionalArgumentCount == parameters.parameterCount) { | |
32 assert(selector.namedArgumentCount == 0); | |
33 return; | |
34 } | |
35 if (parameters.optionalParametersAreNamed | |
36 && selector.namedArgumentCount == parameters.optionalParameterCount) { | |
37 // If the selector has the same number of named arguments as the element, | |
38 // we don't need to add a stub. The call site will hit the method | |
39 // directly. | |
40 return; | |
41 } | |
42 JavaScriptConstantCompiler handler = backend.constants; | |
43 List<String> names = selector.getOrderedNamedArguments(); | |
44 | |
45 String invocationName = namer.invocationName(selector); | |
46 if (alreadyGenerated.contains(invocationName)) return; | |
47 alreadyGenerated.add(invocationName); | |
48 | |
49 bool isInterceptedMethod = backend.isInterceptedMethod(member); | |
50 | |
51 // If the method is intercepted, we need to also pass the actual receiver. | |
52 int extraArgumentCount = isInterceptedMethod ? 1 : 0; | |
53 // Use '$receiver' to avoid clashes with other parameter names. Using | |
54 // '$receiver' works because [:namer.safeName:] used for getting parameter | |
55 // names never returns a name beginning with a single '$'. | |
56 String receiverArgumentName = r'$receiver'; | |
57 | |
58 // The parameters that this stub takes. | |
59 List<jsAst.Parameter> parametersBuffer = | |
60 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); | |
61 // The arguments that will be passed to the real method. | |
62 List<jsAst.Expression> argumentsBuffer = | |
63 new List<jsAst.Expression>( | |
64 parameters.parameterCount + extraArgumentCount); | |
65 | |
66 int count = 0; | |
67 if (isInterceptedMethod) { | |
68 count++; | |
69 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); | |
70 argumentsBuffer[0] = js('#', receiverArgumentName); | |
71 emitter.interceptorEmitter.interceptorInvocationNames.add(invocationName); | |
72 } | |
73 | |
74 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; | |
75 // Includes extra receiver argument when using interceptor convention | |
76 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; | |
77 | |
78 int parameterIndex = 0; | |
79 parameters.orderedForEachParameter((ParameterElement element) { | |
80 String jsName = backend.namer.safeName(element.name); | |
81 assert(jsName != receiverArgumentName); | |
82 if (count < optionalParameterStart) { | |
83 parametersBuffer[count] = new jsAst.Parameter(jsName); | |
84 argumentsBuffer[count] = js('#', jsName); | |
85 } else { | |
86 int index = names.indexOf(element.name); | |
87 if (index != -1) { | |
88 indexOfLastOptionalArgumentInParameters = count; | |
89 // The order of the named arguments is not the same as the | |
90 // one in the real method (which is in Dart source order). | |
91 argumentsBuffer[count] = js('#', jsName); | |
92 parametersBuffer[optionalParameterStart + index] = | |
93 new jsAst.Parameter(jsName); | |
94 } else { | |
95 ConstantExpression constant = handler.getConstantForVariable(element); | |
96 if (constant == null) { | |
97 argumentsBuffer[count] = | |
98 emitter.constantReference(new NullConstantValue()); | |
99 } else { | |
100 ConstantValue value = constant.value; | |
101 if (!value.isNull) { | |
102 // If the value is the null constant, we should not pass it | |
103 // down to the native method. | |
104 indexOfLastOptionalArgumentInParameters = count; | |
105 } | |
106 argumentsBuffer[count] = emitter.constantReference(value); | |
107 } | |
108 } | |
109 } | |
110 count++; | |
111 }); | |
112 | |
113 var body; // List or jsAst.Statement. | |
114 if (member.hasFixedBackendName) { | |
115 body = emitter.nativeEmitter.generateParameterStubStatements( | |
116 member, isInterceptedMethod, invocationName, | |
117 parametersBuffer, argumentsBuffer, | |
118 indexOfLastOptionalArgumentInParameters); | |
119 } else if (member.isInstanceMember) { | |
120 if (needsSuperGetter(member)) { | |
121 ClassElement superClass = member.enclosingClass; | |
122 String methodName = namer.getNameOfInstanceMember(member); | |
123 // When redirecting, we must ensure that we don't end up in a subclass. | |
124 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. | |
125 // Instead we need to call the statically resolved target. | |
126 // `<class>.prototype.bar$1.call(this, argument0, ...)`. | |
127 body = js.statement( | |
128 'return #.prototype.#.call(this, #);', | |
129 [backend.namer.elementAccess(superClass), methodName, | |
130 argumentsBuffer]); | |
131 } else { | |
132 body = js.statement( | |
133 'return this.#(#);', | |
134 [namer.getNameOfInstanceMember(member), argumentsBuffer]); | |
135 } | |
136 } else { | |
137 body = js.statement('return #(#)', | |
138 [namer.elementAccess(member), argumentsBuffer]); | |
139 } | |
140 | |
141 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]); | |
142 | |
143 addStub(selector, function); | |
144 } | |
145 | |
146 void addParameterStubs(FunctionElement member, AddStubFunction defineStub, | |
147 [bool canTearOff = false]) { | |
148 if (member.enclosingElement.isClosure) { | |
149 ClosureClassElement cls = member.enclosingElement; | |
150 if (cls.supertype.element == backend.boundClosureClass) { | |
151 compiler.internalError(cls.methodElement, 'Bound closure1.'); | |
152 } | |
153 if (cls.methodElement.isInstanceMember) { | |
154 compiler.internalError(cls.methodElement, 'Bound closure2.'); | |
155 } | |
156 } | |
157 | |
158 // We fill the lists depending on the selector. For example, | |
159 // take method foo: | |
160 // foo(a, b, {c, d}); | |
161 // | |
162 // We may have multiple ways of calling foo: | |
163 // (1) foo(1, 2); | |
164 // (2) foo(1, 2, c: 3); | |
165 // (3) foo(1, 2, d: 4); | |
166 // (4) foo(1, 2, c: 3, d: 4); | |
167 // (5) foo(1, 2, d: 4, c: 3); | |
168 // | |
169 // What we generate at the call sites are: | |
170 // (1) foo$2(1, 2); | |
171 // (2) foo$3$c(1, 2, 3); | |
172 // (3) foo$3$d(1, 2, 4); | |
173 // (4) foo$4$c$d(1, 2, 3, 4); | |
174 // (5) foo$4$c$d(1, 2, 3, 4); | |
175 // | |
176 // The stubs we generate are (expressed in Dart): | |
177 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) | |
178 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); | |
179 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); | |
180 // (4) No stub generated, call is direct. | |
181 // (5) No stub generated, call is direct. | |
182 // | |
183 // We need to pay attention if this stub is for a function that has been | |
184 // invoked from a subclass. Then we cannot just redirect, since that | |
185 // would invoke the methods of the subclass. We have to compile to: | |
186 // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null) | |
187 // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null); | |
188 // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d); | |
189 | |
190 Set<Selector> selectors = member.isInstanceMember | |
191 ? compiler.codegenWorld.invokedNames[member.name] | |
192 : null; // No stubs needed for static methods. | |
193 | |
194 /// Returns all closure call selectors renamed to match this member. | |
195 Set<Selector> callSelectorsAsNamed() { | |
196 if (!canTearOff) return null; | |
197 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ | |
198 namer.closureInvocationSelectorName]; | |
199 if (callSelectors == null) return null; | |
200 return callSelectors.map((Selector callSelector) { | |
201 return new Selector.call( | |
202 member.name, member.library, | |
203 callSelector.argumentCount, callSelector.namedArguments); | |
204 }).toSet(); | |
205 } | |
206 if (selectors == null) { | |
207 selectors = callSelectorsAsNamed(); | |
208 if (selectors == null) return; | |
209 } else { | |
210 Set<Selector> callSelectors = callSelectorsAsNamed(); | |
211 if (callSelectors != null) { | |
212 selectors = selectors.union(callSelectors); | |
213 } | |
214 } | |
215 Set<Selector> untypedSelectors = new Set<Selector>(); | |
216 if (selectors != null) { | |
217 for (Selector selector in selectors) { | |
218 if (!selector.appliesUnnamed(member, compiler.world)) continue; | |
219 if (untypedSelectors.add(selector.asUntyped)) { | |
220 // TODO(ahe): Is the last argument to [addParameterStub] needed? | |
221 addParameterStub(member, selector, defineStub, new Set<String>()); | |
222 } | |
223 } | |
224 } | |
225 if (canTearOff) { | |
226 selectors = compiler.codegenWorld.invokedNames[ | |
227 namer.closureInvocationSelectorName]; | |
228 if (selectors != null) { | |
229 for (Selector selector in selectors) { | |
230 selector = new Selector.call( | |
231 member.name, member.library, | |
232 selector.argumentCount, selector.namedArguments); | |
233 if (!selector.appliesUnnamed(member, compiler.world)) continue; | |
234 if (untypedSelectors.add(selector)) { | |
235 // TODO(ahe): Is the last argument to [addParameterStub] needed? | |
236 addParameterStub(member, selector, defineStub, new Set<String>()); | |
237 } | |
238 } | |
239 } | |
240 } | |
241 } | |
242 | |
243 /** | |
244 * Documentation wanted -- johnniwinther | |
245 * | |
246 * Invariant: [member] must be a declaration element. | |
247 */ | |
248 void emitCallStubForGetter(Element member, | |
249 Set<Selector> selectors, | |
250 AddPropertyFunction addProperty) { | |
251 assert(invariant(member, member.isDeclaration)); | |
252 LibraryElement memberLibrary = member.library; | |
253 // If the method is intercepted, the stub gets the | |
254 // receiver explicitely and we need to pass it to the getter call. | |
255 bool isInterceptedMethod = backend.isInterceptedMethod(member); | |
256 bool isInterceptorClass = | |
257 backend.isInterceptorClass(member.enclosingClass); | |
258 | |
259 const String receiverArgumentName = r'$receiver'; | |
260 | |
261 jsAst.Expression buildGetter() { | |
262 jsAst.Expression receiver = | |
263 js(isInterceptorClass ? receiverArgumentName : 'this'); | |
264 if (member.isGetter) { | |
265 String getterName = namer.getterName(member); | |
266 if (isInterceptedMethod) { | |
267 return js('this.#(#)', [getterName, receiver]); | |
268 } | |
269 return js('#.#()', [receiver, getterName]); | |
270 } else { | |
271 String fieldName = namer.instanceFieldPropertyName(member); | |
272 return js('#.#', [receiver, fieldName]); | |
273 } | |
274 } | |
275 | |
276 // Two selectors may match but differ only in type. To avoid generating | |
277 // identical stubs for each we track untyped selectors which already have | |
278 // stubs. | |
279 Set<Selector> generatedSelectors = new Set<Selector>(); | |
280 for (Selector selector in selectors) { | |
281 if (selector.applies(member, compiler.world)) { | |
282 selector = selector.asUntyped; | |
283 if (generatedSelectors.contains(selector)) continue; | |
284 generatedSelectors.add(selector); | |
285 | |
286 String invocationName = namer.invocationName(selector); | |
287 Selector callSelector = new Selector.callClosureFrom(selector); | |
288 String closureCallName = namer.invocationName(callSelector); | |
289 | |
290 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; | |
291 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | |
292 if (isInterceptedMethod) { | |
293 parameters.add(new jsAst.Parameter(receiverArgumentName)); | |
294 } | |
295 | |
296 for (int i = 0; i < selector.argumentCount; i++) { | |
297 String name = 'arg$i'; | |
298 parameters.add(new jsAst.Parameter(name)); | |
299 arguments.add(js('#', name)); | |
300 } | |
301 | |
302 jsAst.Fun function = js( | |
303 'function(#) { return #.#(#); }', | |
304 [ parameters, buildGetter(), closureCallName, arguments]); | |
305 | |
306 compiler.dumpInfoTask.registerElementAst(member, | |
307 addProperty(invocationName, function)); | |
308 } | |
309 } | |
310 } | |
311 | |
312 /** | |
313 * Documentation wanted -- johnniwinther | |
314 * | |
315 * Invariant: [member] must be a declaration element. | |
316 */ | |
317 void emitExtraAccessors(Element member, ClassBuilder builder) { | |
318 assert(invariant(member, member.isDeclaration)); | |
319 if (member.isGetter || member.isField) { | |
320 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; | |
321 if (selectors != null && !selectors.isEmpty) { | |
322 emitCallStubForGetter(member, selectors, builder.addProperty); | |
323 } | |
324 } | |
325 } | |
326 | |
327 void addMember(Element member, ClassBuilder builder) { | |
328 assert(invariant(member, member.isDeclaration)); | |
329 | |
330 if (member.isField) { | |
331 addMemberField(member, builder); | |
332 } else if (member.isFunction || | |
333 member.isGenerativeConstructorBody || | |
334 member.isGenerativeConstructor || | |
335 member.isAccessor) { | |
336 addMemberMethod(member, builder); | |
337 } else { | |
338 compiler.internalError(member, | |
339 'Unexpected kind: "${member.kind}".'); | |
340 } | |
341 if (member.isInstanceMember) emitExtraAccessors(member, builder); | |
342 } | |
343 | |
344 void addMemberMethod(FunctionElement member, ClassBuilder builder) { | |
345 MemberInfo info = analyzeMemberMethod(member); | |
346 if (info != null) { | |
347 addMemberMethodFromInfo(info, builder); | |
348 } | |
349 } | |
350 | |
351 MemberInfo analyzeMemberMethod(FunctionElement member) { | |
352 if (member.isAbstract) return null; | |
353 jsAst.Expression code = backend.generatedCode[member]; | |
354 if (code == null) return null; | |
355 String name = namer.getNameOfMember(member); | |
356 | |
357 FunctionSignature parameters = member.functionSignature; | |
358 bool needsStubs = !parameters.optionalParameters.isEmpty; | |
359 bool canTearOff = false; | |
360 bool isClosure = false; | |
361 bool isNotApplyTarget = !member.isFunction || | |
362 member.isConstructor || | |
363 member.isAccessor; | |
364 String tearOffName; | |
365 | |
366 final bool canBeReflected = backend.isAccessibleByReflection(member); | |
367 | |
368 if (isNotApplyTarget) { | |
369 canTearOff = false; | |
370 } else if (member.isInstanceMember) { | |
371 if (member.enclosingClass.isClosure) { | |
372 canTearOff = false; | |
373 isClosure = true; | |
374 } else { | |
375 // Careful with operators. | |
376 canTearOff = | |
377 compiler.codegenWorld.hasInvokedGetter(member, compiler.world) || | |
378 (canBeReflected && !member.isOperator); | |
379 assert(!needsSuperGetter(member) || canTearOff); | |
380 tearOffName = namer.getterName(member); | |
381 } | |
382 } else { | |
383 canTearOff = | |
384 compiler.codegenWorld.staticFunctionsNeedingGetter.contains(member) || | |
385 canBeReflected; | |
386 tearOffName = namer.getStaticClosureName(member); | |
387 } | |
388 final bool canBeApplied = compiler.enabledFunctionApply && | |
389 compiler.world.getMightBePassedToApply(member); | |
390 | |
391 final bool needStructuredInfo = | |
392 canTearOff || canBeReflected || canBeApplied; | |
393 | |
394 if (canTearOff) { | |
395 assert(invariant(member, !member.isGenerativeConstructor)); | |
396 assert(invariant(member, !member.isGenerativeConstructorBody)); | |
397 assert(invariant(member, !member.isConstructor)); | |
398 } | |
399 | |
400 return new MemberInfo( | |
401 member, | |
402 name, | |
403 parameters, | |
404 code, | |
405 needsStubs: needsStubs, | |
406 canTearOff: canTearOff, | |
407 isClosure: isClosure, | |
408 tearOffName: tearOffName, | |
409 canBeReflected: canBeReflected, | |
410 canBeApplied: canBeApplied, | |
411 needStructuredInfo: needStructuredInfo); | |
412 | |
413 } | |
414 | |
415 void addMemberMethodFromInfo(MemberInfo info, ClassBuilder builder) { | |
416 final FunctionElement member = info.member; | |
417 final String name = info.name; | |
418 final FunctionSignature parameters = info.parameters; | |
419 jsAst.Expression code = info.code; | |
420 final bool needsStubs = info.needsStubs; | |
421 final bool canTearOff = info.canTearOff; | |
422 final bool isClosure = info.isClosure; | |
423 final String tearOffName = info.tearOffName; | |
424 final bool canBeReflected = info.canBeReflected; | |
425 final bool canBeApplied = info.canBeApplied; | |
426 final bool needStructuredInfo = info.needStructuredInfo; | |
427 | |
428 emitter.interceptorEmitter.recordMangledNameOfMemberMethod(member, name); | |
429 | |
430 if (!needStructuredInfo) { | |
431 compiler.dumpInfoTask.registerElementAst(member, | |
432 builder.addProperty(name, code)); | |
433 if (needsStubs) { | |
434 addParameterStubs( | |
435 member, | |
436 (Selector selector, jsAst.Fun function) { | |
437 compiler.dumpInfoTask.registerElementAst(member, | |
438 builder.addProperty(namer.invocationName(selector), function))
; | |
439 }); | |
440 } | |
441 return; | |
442 } | |
443 | |
444 | |
445 // This element is needed for reflection or needs additional stubs. So we | |
446 // need to retain additional information. | |
447 | |
448 // The information is stored in an array with this format: | |
449 // | |
450 // 1. The JS function for this member. | |
451 // 2. First stub. | |
452 // 3. Name of first stub. | |
453 // ... | |
454 // M. Call name of this member. | |
455 // M+1. Call name of first stub. | |
456 // ... | |
457 // N. Getter name for tearOff. | |
458 // N+1. (Required parameter count << 1) + (member.isAccessor ? 1 : 0). | |
459 // N+2. (Optional parameter count << 1) + | |
460 // (parameters.optionalParametersAreNamed ? 1 : 0). | |
461 // N+3. Index to function type in constant pool. | |
462 // N+4. First default argument. | |
463 // ... | |
464 // O. First parameter name (if needed for reflection or Function.apply). | |
465 // ... | |
466 // P. Unmangled name (if reflectable). | |
467 // P+1. First metadata (if reflectable). | |
468 // ... | |
469 // TODO(ahe): Consider one of the parameter counts can be replaced by the | |
470 // length property of the JavaScript function object. | |
471 | |
472 List<jsAst.Expression> expressions = <jsAst.Expression>[]; | |
473 | |
474 String callSelectorString = 'null'; | |
475 if (member.isFunction) { | |
476 Selector callSelector = new Selector.fromElement(member).toCallSelector(); | |
477 callSelectorString = '"${namer.invocationName(callSelector)}"'; | |
478 } | |
479 | |
480 // On [requiredParameterCount], the lower bit is set if this method can be | |
481 // called reflectively. | |
482 int requiredParameterCount = parameters.requiredParameterCount << 1; | |
483 if (member.isAccessor) requiredParameterCount++; | |
484 | |
485 int optionalParameterCount = parameters.optionalParameterCount << 1; | |
486 if (parameters.optionalParametersAreNamed) optionalParameterCount++; | |
487 | |
488 expressions.add(code); | |
489 | |
490 // TODO(sra): Don't use LiteralString for non-strings. | |
491 List tearOffInfo = [new jsAst.LiteralString(callSelectorString)]; | |
492 | |
493 if (needsStubs || canTearOff) { | |
494 addParameterStubs(member, (Selector selector, jsAst.Fun function) { | |
495 expressions.add(function); | |
496 if (member.isInstanceMember) { | |
497 Set invokedSelectors = | |
498 compiler.codegenWorld.invokedNames[member.name]; | |
499 expressions.add(js.string(namer.invocationName(selector))); | |
500 } else { | |
501 expressions.add(js('null')); | |
502 // TOOD(ahe): Since we know when reading static data versus instance | |
503 // data, we can eliminate this element. | |
504 } | |
505 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ | |
506 namer.closureInvocationSelectorName]; | |
507 Selector callSelector = selector.toCallSelector(); | |
508 String callSelectorString = 'null'; | |
509 if (canTearOff && callSelectors != null && | |
510 callSelectors.contains(callSelector)) { | |
511 callSelectorString = '"${namer.invocationName(callSelector)}"'; | |
512 } | |
513 tearOffInfo.add(new jsAst.LiteralString(callSelectorString)); | |
514 }, canTearOff); | |
515 } | |
516 | |
517 jsAst.Expression memberTypeExpression; | |
518 if (canTearOff || canBeReflected) { | |
519 DartType memberType; | |
520 if (member.isGenerativeConstructorBody) { | |
521 var body = member; | |
522 memberType = body.constructor.type; | |
523 } else { | |
524 memberType = member.type; | |
525 } | |
526 if (memberType.containsTypeVariables) { | |
527 jsAst.Expression thisAccess = js(r'this.$receiver'); | |
528 memberTypeExpression = | |
529 backend.rti.getSignatureEncoding(memberType, thisAccess); | |
530 } else { | |
531 memberTypeExpression = | |
532 js.number(emitter.metadataEmitter.reifyType(memberType)); | |
533 } | |
534 } else { | |
535 memberTypeExpression = js('null'); | |
536 } | |
537 | |
538 expressions | |
539 ..addAll(tearOffInfo) | |
540 ..add((tearOffName == null || member.isAccessor) | |
541 ? js("null") : js.string(tearOffName)) | |
542 ..add(js.number(requiredParameterCount)) | |
543 ..add(js.number(optionalParameterCount)) | |
544 ..add(memberTypeExpression) | |
545 ..addAll(emitter.metadataEmitter | |
546 .reifyDefaultArguments(member).map(js.number)); | |
547 | |
548 if (canBeReflected || canBeApplied) { | |
549 parameters.forEachParameter((Element parameter) { | |
550 expressions.add( | |
551 js.number(emitter.metadataEmitter.reifyName(parameter.name))); | |
552 if (backend.mustRetainMetadata) { | |
553 Iterable<int> metadataIndices = | |
554 parameter.metadata.map((MetadataAnnotation annotation) { | |
555 ConstantValue constant = | |
556 backend.constants.getConstantForMetadata(annotation).value; | |
557 backend.constants.addCompileTimeConstantForEmission(constant); | |
558 return emitter.metadataEmitter.reifyMetadata(annotation); | |
559 }); | |
560 expressions.add( | |
561 new jsAst.ArrayInitializer.from(metadataIndices.map(js.number))); | |
562 } | |
563 }); | |
564 } | |
565 if (canBeReflected) { | |
566 jsAst.LiteralString reflectionName; | |
567 if (member.isConstructor) { | |
568 String reflectionNameString = emitter.getReflectionName(member, name); | |
569 reflectionName = | |
570 new jsAst.LiteralString( | |
571 '"new ${Elements.reconstructConstructorName(member)}"'); | |
572 } else { | |
573 reflectionName = | |
574 js.string(namer.privateName(member.library, member.name)); | |
575 } | |
576 expressions | |
577 ..add(reflectionName) | |
578 ..addAll(emitter.metadataEmitter | |
579 .computeMetadata(member).map(js.number)); | |
580 } else if (isClosure && canBeApplied) { | |
581 expressions.add(js.string(namer.privateName(member.library, | |
582 member.name))); | |
583 } | |
584 jsAst.ArrayInitializer arrayInit = | |
585 new jsAst.ArrayInitializer.from(expressions); | |
586 compiler.dumpInfoTask.registerElementAst(member, | |
587 builder.addProperty(name, arrayInit)); | |
588 } | |
589 | |
590 void addMemberField(VariableElement member, ClassBuilder builder) { | |
591 // For now, do nothing. | |
592 } | |
593 } | |
594 | |
595 class MemberInfo { | |
596 final FunctionElement member; | |
597 | |
598 final String name; | |
599 | |
600 final FunctionSignature parameters; | |
601 | |
602 final jsAst.Expression code; | |
603 | |
604 final bool needsStubs; | |
605 | |
606 final bool canTearOff; | |
607 | |
608 final bool isClosure; | |
609 | |
610 final String tearOffName; | |
611 | |
612 final bool canBeReflected; | |
613 | |
614 final bool canBeApplied; | |
615 | |
616 final bool needStructuredInfo; | |
617 | |
618 MemberInfo( | |
619 this.member, | |
620 this.name, | |
621 this.parameters, | |
622 this.code, | |
623 {this.needsStubs, | |
624 this.canTearOff, | |
625 this.isClosure, | |
626 this.tearOffName, | |
627 this.canBeReflected, | |
628 this.canBeApplied, | |
629 this.needStructuredInfo}) { | |
630 assert(member != null); | |
631 assert(name != null); | |
632 assert(parameters != null); | |
633 assert(code != null); | |
634 assert(needsStubs != null); | |
635 assert(canTearOff != null); | |
636 assert(isClosure != null); | |
637 assert(tearOffName != null || !canTearOff); | |
638 assert(canBeReflected != null); | |
639 assert(canBeApplied != null); | |
640 assert(needStructuredInfo != null); | |
641 } | |
642 } | |
OLD | NEW |