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

Side by Side Diff: pkg/compiler/lib/src/ssa/locals_handler.dart

Issue 2283443002: Make LocalsHandler depend on GraphBuilder instead of SsaBuilder. (Closed)
Patch Set: Created 4 years, 3 months 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
OLDNEW
(Empty)
1 // Copyright (c) 2016, 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 import '../closure.dart';
6 import '../common.dart';
7 import '../compiler.dart' show Compiler;
8 import '../dart_types.dart';
9 import '../elements/elements.dart';
10 import '../io/source_information.dart';
11 import '../js/js.dart' as js;
12 import '../js_backend/js_backend.dart';
13 import '../native/native.dart' as native;
14 import '../tree/tree.dart' as ast;
15 import '../types/types.dart';
16 import '../world.dart' show ClassWorld;
17 import 'builder.dart' show SyntheticLocal;
18 import 'graph_builder.dart';
19 import 'nodes.dart';
20 import 'types.dart';
21
22 /**
Siggi Cherem (dart-lang) 2016/08/25 19:12:53 feel free to convert to /// comment style if you s
Harry Terkelsen 2016/08/25 20:12:57 Done.
23 * Keeps track of locals (including parameters and phis) when building. The
24 * 'this' reference is treated as parameter and hence handled by this class,
25 * too.
26 */
27 class LocalsHandler {
28 /**
29 * The values of locals that can be directly accessed (without redirections
30 * to boxes or closure-fields).
31 *
32 * [directLocals] is iterated, so it is "insertion ordered" to make the
33 * iteration order a function only of insertions and not a function of
34 * e.g. Element hash codes. I'd prefer to use a SortedMap but some elements
35 * don't have source locations for [Elements.compareByPosition].
36 */
37 Map<Local, HInstruction> directLocals = new Map<Local, HInstruction>();
38 Map<Local, CapturedVariable> redirectionMapping =
39 new Map<Local, CapturedVariable>();
40 final GraphBuilder builder;
41 ClosureClassMap closureData;
42 Map<TypeVariableType, TypeVariableLocal> typeVariableLocals =
43 new Map<TypeVariableType, TypeVariableLocal>();
44 final ExecutableElement executableContext;
45
46 /// The class that defines the current type environment or null if no type
47 /// variables are in scope.
48 ClassElement get contextClass => executableContext.contextClass;
49
50 /// The type of the current instance, if concrete.
51 ///
52 /// This allows for handling fixed type argument in case of inlining. For
53 /// instance, checking `'foo'` against `String` instead of `T` in `main`:
54 ///
55 /// class Foo<T> {
56 /// T field;
57 /// Foo(this.field);
58 /// }
59 /// main() {
60 /// new Foo<String>('foo');
61 /// }
62 ///
63 /// [instanceType] is not used if it contains type variables, since these
64 /// might not be in scope or from the current instance.
65 ///
66 final InterfaceType instanceType;
67
68 final Compiler compiler;
69
70 final SourceInformationBuilder sourceInformationBuilder;
71
72 LocalsHandler(this.builder, this.executableContext,
73 InterfaceType instanceType, this.compiler, this.sourceInformationBuilder)
74 : this.instanceType =
75 instanceType == null || instanceType.containsTypeVariables
76 ? null
77 : instanceType;
78
79 /// Substituted type variables occurring in [type] into the context of
80 /// [contextClass].
81 DartType substInContext(DartType type) {
82 if (contextClass != null) {
83 ClassElement typeContext = Types.getClassContext(type);
84 if (typeContext != null) {
85 type = type.substByContext(contextClass.asInstanceOf(typeContext));
86 }
87 }
88 if (instanceType != null) {
89 type = type.substByContext(instanceType);
90 }
91 return type;
92 }
93
94 get typesTask => compiler.typesTask;
95
96 /**
97 * Creates a new [LocalsHandler] based on [other]. We only need to
98 * copy the [directLocals], since the other fields can be shared
99 * throughout the AST visit.
100 */
101 LocalsHandler.from(LocalsHandler other)
102 : directLocals = new Map<Local, HInstruction>.from(other.directLocals),
103 redirectionMapping = other.redirectionMapping,
104 executableContext = other.executableContext,
105 instanceType = other.instanceType,
106 builder = other.builder,
107 closureData = other.closureData,
108 compiler = other.compiler,
109 sourceInformationBuilder = other.sourceInformationBuilder;
110
111 /**
112 * Redirects accesses from element [from] to element [to]. The [to] element
113 * must be a boxed variable or a variable that is stored in a closure-field.
114 */
115 void redirectElement(Local from, CapturedVariable to) {
116 assert(redirectionMapping[from] == null);
117 redirectionMapping[from] = to;
118 assert(isStoredInClosureField(from) || isBoxed(from));
119 }
120
121 HInstruction createBox() {
122 // TODO(floitsch): Clean up this hack. Should we create a box-object by
123 // just creating an empty object literal?
124 JavaScriptBackend backend = compiler.backend;
125 HInstruction box = new HForeignCode(
126 js.js.parseForeignJS('{}'), backend.nonNullType, <HInstruction>[],
127 nativeBehavior: native.NativeBehavior.PURE_ALLOCATION);
128 builder.add(box);
129 return box;
130 }
131
132 /**
133 * If the scope (function or loop) [node] has captured variables then this
134 * method creates a box and sets up the redirections.
135 */
136 void enterScope(ast.Node node, Element element) {
Siggi Cherem (dart-lang) 2016/08/25 19:12:53 one idea (feel free to defer for later): we could
Harry Terkelsen 2016/08/25 20:12:57 Acknowledged.
137 // See if any variable in the top-scope of the function is captured. If yes
138 // we need to create a box-object.
139 ClosureScope scopeData = closureData.capturingScopes[node];
140 if (scopeData == null) return;
141 HInstruction box;
142 // The scope has captured variables.
143 if (element != null && element.isGenerativeConstructorBody) {
144 // The box is passed as a parameter to a generative
145 // constructor body.
146 JavaScriptBackend backend = compiler.backend;
147 box = builder.addParameter(scopeData.boxElement, backend.nonNullType);
148 } else {
149 box = createBox();
150 }
151 // Add the box to the known locals.
152 directLocals[scopeData.boxElement] = box;
153 // Make sure that accesses to the boxed locals go into the box. We also
154 // need to make sure that parameters are copied into the box if necessary.
155 scopeData.forEachCapturedVariable(
156 (LocalVariableElement from, BoxFieldElement to) {
157 // The [from] can only be a parameter for function-scopes and not
158 // loop scopes.
159 if (from.isRegularParameter && !element.isGenerativeConstructorBody) {
160 // Now that the redirection is set up, the update to the local will
161 // write the parameter value into the box.
162 // Store the captured parameter in the box. Get the current value
163 // before we put the redirection in place.
164 // We don't need to update the local for a generative
165 // constructor body, because it receives a box that already
166 // contains the updates as the last parameter.
167 HInstruction instruction = readLocal(from);
168 redirectElement(from, to);
169 updateLocal(from, instruction);
170 } else {
171 redirectElement(from, to);
172 }
173 });
174 }
175
176 /**
177 * Replaces the current box with a new box and copies over the given list
178 * of elements from the old box into the new box.
179 */
180 void updateCaptureBox(
181 BoxLocal boxElement, List<LocalVariableElement> toBeCopiedElements) {
182 // Create a new box and copy over the values from the old box into the
183 // new one.
184 HInstruction oldBox = readLocal(boxElement);
185 HInstruction newBox = createBox();
186 for (LocalVariableElement boxedVariable in toBeCopiedElements) {
187 // [readLocal] uses the [boxElement] to find its box. By replacing it
188 // behind its back we can still get to the old values.
189 updateLocal(boxElement, oldBox);
190 HInstruction oldValue = readLocal(boxedVariable);
191 updateLocal(boxElement, newBox);
192 updateLocal(boxedVariable, oldValue);
193 }
194 updateLocal(boxElement, newBox);
195 }
196
197 /**
198 * Documentation wanted -- johnniwinther
199 *
200 * Invariant: [function] must be an implementation element.
201 */
202 void startFunction(AstElement element, ast.Node node) {
203 assert(invariant(element, element.isImplementation));
204 closureData = compiler.closureToClassMapper
205 .computeClosureToClassMapping(element.resolvedAst);
206
207 if (element is FunctionElement) {
208 FunctionElement functionElement = element;
209 FunctionSignature params = functionElement.functionSignature;
210 ClosureScope scopeData = closureData.capturingScopes[node];
211 params.orderedForEachParameter((ParameterElement parameterElement) {
212 if (element.isGenerativeConstructorBody) {
213 if (scopeData != null &&
214 scopeData.isCapturedVariable(parameterElement)) {
215 // The parameter will be a field in the box passed as the
216 // last parameter. So no need to have it.
217 return;
218 }
219 }
220 HInstruction parameter = builder.addParameter(parameterElement,
221 TypeMaskFactory.inferredTypeForElement(parameterElement, compiler));
222 builder.parameters[parameterElement] = parameter;
223 directLocals[parameterElement] = parameter;
224 });
225 }
226
227 enterScope(node, element);
228
229 // If the freeVariableMapping is not empty, then this function was a
230 // nested closure that captures variables. Redirect the captured
231 // variables to fields in the closure.
232 closureData.forEachFreeVariable((Local from, CapturedVariable to) {
233 redirectElement(from, to);
234 });
235 JavaScriptBackend backend = compiler.backend;
236 if (closureData.isClosure) {
237 // Inside closure redirect references to itself to [:this:].
238 HThis thisInstruction =
239 new HThis(closureData.thisLocal, backend.nonNullType);
240 builder.graph.thisInstruction = thisInstruction;
241 builder.graph.entry.addAtEntry(thisInstruction);
242 updateLocal(closureData.closureElement, thisInstruction);
243 } else if (element.isInstanceMember) {
244 // Once closures have been mapped to classes their instance members might
245 // not have any thisElement if the closure was created inside a static
246 // context.
247 HThis thisInstruction = new HThis(closureData.thisLocal, getTypeOfThis());
248 builder.graph.thisInstruction = thisInstruction;
249 builder.graph.entry.addAtEntry(thisInstruction);
250 directLocals[closureData.thisLocal] = thisInstruction;
251 }
252
253 // If this method is an intercepted method, add the extra
254 // parameter to it, that is the actual receiver for intercepted
255 // classes, or the same as [:this:] for non-intercepted classes.
256 ClassElement cls = element.enclosingClass;
257
258 // When the class extends a native class, the instance is pre-constructed
259 // and passed to the generative constructor factory function as a parameter.
260 // Instead of allocating and initializing the object, the constructor
261 // 'upgrades' the native subclass object by initializing the Dart fields.
262 bool isNativeUpgradeFactory =
263 element.isGenerativeConstructor && backend.isNativeOrExtendsNative(cls);
264 if (backend.isInterceptedMethod(element)) {
265 bool isInterceptorClass = backend.isInterceptorClass(cls.declaration);
266 String name = isInterceptorClass ? 'receiver' : '_';
267 SyntheticLocal parameter = new SyntheticLocal(name, executableContext);
268 HParameterValue value = new HParameterValue(parameter, getTypeOfThis());
269 builder.graph.explicitReceiverParameter = value;
270 builder.graph.entry.addAfter(directLocals[closureData.thisLocal], value);
271 if (builder.lastAddedParameter == null) {
272 // If this is the first parameter inserted, make sure it stays first.
273 builder.lastAddedParameter = value;
274 }
275 if (isInterceptorClass) {
276 // Only use the extra parameter in intercepted classes.
277 directLocals[closureData.thisLocal] = value;
278 }
279 } else if (isNativeUpgradeFactory) {
280 SyntheticLocal parameter =
281 new SyntheticLocal('receiver', executableContext);
282 // Unlike `this`, receiver is nullable since direct calls to generative
283 // constructor call the constructor with `null`.
284 ClassWorld classWorld = compiler.world;
285 HParameterValue value =
286 new HParameterValue(parameter, new TypeMask.exact(cls, classWorld));
287 builder.graph.explicitReceiverParameter = value;
288 builder.graph.entry.addAtEntry(value);
289 }
290 }
291
292 /**
293 * Returns true if the local can be accessed directly. Boxed variables or
294 * captured variables that are stored in the closure-field return [:false:].
295 */
296 bool isAccessedDirectly(Local local) {
297 assert(local != null);
298 return !redirectionMapping.containsKey(local) &&
299 !closureData.variablesUsedInTryOrGenerator.contains(local);
300 }
301
302 bool isStoredInClosureField(Local local) {
303 assert(local != null);
304 if (isAccessedDirectly(local)) return false;
305 CapturedVariable redirectTarget = redirectionMapping[local];
306 if (redirectTarget == null) return false;
307 return redirectTarget is ClosureFieldElement;
308 }
309
310 bool isBoxed(Local local) {
311 if (isAccessedDirectly(local)) return false;
312 if (isStoredInClosureField(local)) return false;
313 return redirectionMapping.containsKey(local);
314 }
315
316 bool isUsedInTryOrGenerator(Local local) {
317 return closureData.variablesUsedInTryOrGenerator.contains(local);
318 }
319
320 /**
321 * Returns an [HInstruction] for the given element. If the element is
322 * boxed or stored in a closure then the method generates code to retrieve
323 * the value.
324 */
325 HInstruction readLocal(Local local, {SourceInformation sourceInformation}) {
326 if (isAccessedDirectly(local)) {
327 if (directLocals[local] == null) {
328 if (local is TypeVariableElement) {
329 compiler.reporter.internalError(compiler.currentElement,
330 "Runtime type information not available for $local.");
331 } else {
332 compiler.reporter.internalError(
333 local, "Cannot find value $local in ${directLocals.keys}.");
334 }
335 }
336 HInstruction value = directLocals[local];
337 if (sourceInformation != null) {
338 value = new HRef(value, sourceInformation);
339 builder.add(value);
340 }
341 return value;
342 } else if (isStoredInClosureField(local)) {
343 ClosureFieldElement redirect = redirectionMapping[local];
344 HInstruction receiver = readLocal(closureData.closureElement);
345 TypeMask type = local is BoxLocal
346 ? (compiler.backend as JavaScriptBackend).nonNullType
347 : getTypeOfCapturedVariable(redirect);
348 HInstruction fieldGet = new HFieldGet(redirect, receiver, type);
349 builder.add(fieldGet);
350 return fieldGet..sourceInformation = sourceInformation;
351 } else if (isBoxed(local)) {
352 BoxFieldElement redirect = redirectionMapping[local];
353 // In the function that declares the captured variable the box is
354 // accessed as direct local. Inside the nested closure the box is
355 // accessed through a closure-field.
356 // Calling [readLocal] makes sure we generate the correct code to get
357 // the box.
358 HInstruction box = readLocal(redirect.box);
359 HInstruction lookup =
360 new HFieldGet(redirect, box, getTypeOfCapturedVariable(redirect));
361 builder.add(lookup);
362 return lookup..sourceInformation = sourceInformation;
363 } else {
364 assert(isUsedInTryOrGenerator(local));
365 HLocalValue localValue = getLocal(local);
366 HInstruction instruction = new HLocalGet(
367 local,
368 localValue,
369 (compiler.backend as JavaScriptBackend).dynamicType,
370 sourceInformation);
371 builder.add(instruction);
372 return instruction;
373 }
374 }
375
376 HInstruction readThis() {
377 HInstruction res = readLocal(closureData.thisLocal);
378 if (res.instructionType == null) {
379 res.instructionType = getTypeOfThis();
380 }
381 return res;
382 }
383
384 HLocalValue getLocal(Local local, {SourceInformation sourceInformation}) {
385 // If the element is a parameter, we already have a
386 // HParameterValue for it. We cannot create another one because
387 // it could then have another name than the real parameter. And
388 // the other one would not know it is just a copy of the real
389 // parameter.
390 if (local is ParameterElement) {
391 assert(invariant(local, builder.parameters.containsKey(local),
392 message: "No local value for parameter $local in "
393 "${builder.parameters}."));
394 return builder.parameters[local];
395 }
396
397 return activationVariables.putIfAbsent(local, () {
398 JavaScriptBackend backend = compiler.backend;
399 HLocalValue localValue = new HLocalValue(local, backend.nonNullType)
400 ..sourceInformation = sourceInformation;
401 builder.graph.entry.addAtExit(localValue);
402 return localValue;
403 });
404 }
405
406 Local getTypeVariableAsLocal(TypeVariableType type) {
407 return typeVariableLocals.putIfAbsent(type, () {
408 return new TypeVariableLocal(type, executableContext);
409 });
410 }
411
412 /**
413 * Sets the [element] to [value]. If the element is boxed or stored in a
414 * closure then the method generates code to set the value.
415 */
416 void updateLocal(Local local, HInstruction value,
417 {SourceInformation sourceInformation}) {
418 if (value is HRef) {
419 HRef ref = value;
420 value = ref.value;
421 }
422 assert(!isStoredInClosureField(local));
423 if (isAccessedDirectly(local)) {
424 directLocals[local] = value;
425 } else if (isBoxed(local)) {
426 BoxFieldElement redirect = redirectionMapping[local];
427 // The box itself could be captured, or be local. A local variable that
428 // is captured will be boxed, but the box itself will be a local.
429 // Inside the closure the box is stored in a closure-field and cannot
430 // be accessed directly.
431 HInstruction box = readLocal(redirect.box);
432 builder.add(new HFieldSet(redirect, box, value)
433 ..sourceInformation = sourceInformation);
434 } else {
435 assert(isUsedInTryOrGenerator(local));
436 HLocalValue localValue = getLocal(local);
437 builder.add(new HLocalSet(local, localValue, value)
438 ..sourceInformation = sourceInformation);
439 }
440 }
441
442 /**
443 * This function, startLoop, must be called before visiting any children of
444 * the loop. In particular it needs to be called before executing the
445 * initializers.
446 *
447 * The [LocalsHandler] will make the boxes and updates at the right moment.
448 * The builder just needs to call [enterLoopBody] and [enterLoopUpdates]
449 * (for [ast.For] loops) at the correct places. For phi-handling
450 * [beginLoopHeader] and [endLoop] must also be called.
451 *
452 * The correct place for the box depends on the given loop. In most cases
453 * the box will be created when entering the loop-body: while, do-while, and
454 * for-in (assuming the call to [:next:] is inside the body) can always be
455 * constructed this way.
456 *
457 * Things are slightly more complicated for [ast.For] loops. If no declared
458 * loop variable is boxed then the loop-body approach works here too. If a
459 * loop-variable is boxed we need to introduce a new box for the
460 * loop-variable before we enter the initializer so that the initializer
461 * writes the values into the box. In any case we need to create the box
462 * before the condition since the condition could box the variable.
463 * Since the first box is created outside the actual loop we have a second
464 * location where a box is created: just before the updates. This is
465 * necessary since updates are considered to be part of the next iteration
466 * (and can again capture variables).
467 *
468 * For example the following Dart code prints 1 3 -- 3 4.
469 *
470 * var fs = [];
471 * for (var i = 0; i < 3; (f() { fs.add(f); print(i); i++; })()) {
472 * i++;
473 * }
474 * print("--");
475 * for (var i = 0; i < 2; i++) fs[i]();
476 *
477 * We solve this by emitting the following code (only for [ast.For] loops):
478 * <Create box> <== move the first box creation outside the loop.
479 * <initializer>;
480 * loop-entry:
481 * if (!<condition>) goto loop-exit;
482 * <body>
483 * <update box> // create a new box and copy the captured loop-variables.
484 * <updates>
485 * goto loop-entry;
486 * loop-exit:
487 */
488 void startLoop(ast.Node node) {
489 ClosureScope scopeData = closureData.capturingScopes[node];
490 if (scopeData == null) return;
491 if (scopeData.hasBoxedLoopVariables()) {
492 // If there are boxed loop variables then we set up the box and
493 // redirections already now. This way the initializer can write its
494 // values into the box.
495 // For other loops the box will be created when entering the body.
496 enterScope(node, null);
497 }
498 }
499
500 /**
501 * Create phis at the loop entry for local variables (ready for the values
502 * from the back edge). Populate the phis with the current values.
503 */
504 void beginLoopHeader(HBasicBlock loopEntry) {
505 // Create a copy because we modify the map while iterating over it.
506 Map<Local, HInstruction> savedDirectLocals =
507 new Map<Local, HInstruction>.from(directLocals);
508
509 JavaScriptBackend backend = compiler.backend;
510 // Create phis for all elements in the definitions environment.
511 savedDirectLocals.forEach((Local local, HInstruction instruction) {
512 if (isAccessedDirectly(local)) {
513 // We know 'this' cannot be modified.
514 if (local != closureData.thisLocal) {
515 HPhi phi =
516 new HPhi.singleInput(local, instruction, backend.dynamicType);
517 loopEntry.addPhi(phi);
518 directLocals[local] = phi;
519 } else {
520 directLocals[local] = instruction;
521 }
522 }
523 });
524 }
525
526 void enterLoopBody(ast.Node node) {
527 ClosureScope scopeData = closureData.capturingScopes[node];
528 if (scopeData == null) return;
529 // If there are no declared boxed loop variables then we did not create the
530 // box before the initializer and we have to create the box now.
531 if (!scopeData.hasBoxedLoopVariables()) {
532 enterScope(node, null);
533 }
534 }
535
536 void enterLoopUpdates(ast.Node node) {
537 // If there are declared boxed loop variables then the updates might have
538 // access to the box and we must switch to a new box before executing the
539 // updates.
540 // In all other cases a new box will be created when entering the body of
541 // the next iteration.
542 ClosureScope scopeData = closureData.capturingScopes[node];
543 if (scopeData == null) return;
544 if (scopeData.hasBoxedLoopVariables()) {
545 updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables);
546 }
547 }
548
549 /**
550 * Goes through the phis created in beginLoopHeader entry and adds the
551 * input from the back edge (from the current value of directLocals) to them.
552 */
553 void endLoop(HBasicBlock loopEntry) {
554 // If the loop has an aborting body, we don't update the loop
555 // phis.
556 if (loopEntry.predecessors.length == 1) return;
557 loopEntry.forEachPhi((HPhi phi) {
558 Local element = phi.sourceElement;
559 HInstruction postLoopDefinition = directLocals[element];
560 phi.addInput(postLoopDefinition);
561 });
562 }
563
564 /**
565 * Merge [otherLocals] into this locals handler, creating phi-nodes when
566 * there is a conflict.
567 * If a phi node is necessary, it will use this handler's instruction as the
568 * first input, and the otherLocals instruction as the second.
569 */
570 void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) {
571 // If an element is in one map but not the other we can safely
572 // ignore it. It means that a variable was declared in the
573 // block. Since variable declarations are scoped the declared
574 // variable cannot be alive outside the block. Note: this is only
575 // true for nodes where we do joins.
576 Map<Local, HInstruction> joinedLocals = new Map<Local, HInstruction>();
577 JavaScriptBackend backend = compiler.backend;
578 otherLocals.directLocals.forEach((Local local, HInstruction instruction) {
579 // We know 'this' cannot be modified.
580 if (local == closureData.thisLocal) {
581 assert(directLocals[local] == instruction);
582 joinedLocals[local] = instruction;
583 } else {
584 HInstruction mine = directLocals[local];
585 if (mine == null) return;
586 if (identical(instruction, mine)) {
587 joinedLocals[local] = instruction;
588 } else {
589 HInstruction phi = new HPhi.manyInputs(
590 local, <HInstruction>[mine, instruction], backend.dynamicType);
591 joinBlock.addPhi(phi);
592 joinedLocals[local] = phi;
593 }
594 }
595 });
596 directLocals = joinedLocals;
597 }
598
599 /**
600 * When control flow merges, this method can be used to merge several
601 * localsHandlers into a new one using phis. The new localsHandler is
602 * returned. Unless it is also in the list, the current localsHandler is not
603 * used for its values, only for its declared variables. This is a way to
604 * exclude local values from the result when they are no longer in scope.
605 */
606 LocalsHandler mergeMultiple(
607 List<LocalsHandler> localsHandlers, HBasicBlock joinBlock) {
608 assert(localsHandlers.length > 0);
609 if (localsHandlers.length == 1) return localsHandlers[0];
610 Map<Local, HInstruction> joinedLocals = new Map<Local, HInstruction>();
611 HInstruction thisValue = null;
612 JavaScriptBackend backend = compiler.backend;
613 directLocals.forEach((Local local, HInstruction instruction) {
614 if (local != closureData.thisLocal) {
615 HPhi phi = new HPhi.noInputs(local, backend.dynamicType);
616 joinedLocals[local] = phi;
617 joinBlock.addPhi(phi);
618 } else {
619 // We know that "this" never changes, if it's there.
620 // Save it for later. While merging, there is no phi for "this",
621 // so we don't have to special case it in the merge loop.
622 thisValue = instruction;
623 }
624 });
625 for (LocalsHandler handler in localsHandlers) {
626 handler.directLocals.forEach((Local local, HInstruction instruction) {
627 HPhi phi = joinedLocals[local];
628 if (phi != null) {
629 phi.addInput(instruction);
630 }
631 });
632 }
633 if (thisValue != null) {
634 // If there was a "this" for the scope, add it to the new locals.
635 joinedLocals[closureData.thisLocal] = thisValue;
636 }
637
638 // Remove locals that are not in all handlers.
639 directLocals = new Map<Local, HInstruction>();
640 joinedLocals.forEach((Local local, HInstruction instruction) {
641 if (local != closureData.thisLocal &&
642 instruction.inputs.length != localsHandlers.length) {
643 joinBlock.removePhi(instruction);
644 } else {
645 directLocals[local] = instruction;
646 }
647 });
648 return this;
649 }
650
651 TypeMask cachedTypeOfThis;
652
653 TypeMask getTypeOfThis() {
654 TypeMask result = cachedTypeOfThis;
655 if (result == null) {
656 ThisLocal local = closureData.thisLocal;
657 ClassElement cls = local.enclosingClass;
658 ClassWorld classWorld = compiler.world;
659 if (classWorld.isUsedAsMixin(cls)) {
660 // If the enclosing class is used as a mixin, [:this:] can be
661 // of the class that mixins the enclosing class. These two
662 // classes do not have a subclass relationship, so, for
663 // simplicity, we mark the type as an interface type.
664 result = new TypeMask.nonNullSubtype(cls.declaration, compiler.world);
665 } else {
666 result = new TypeMask.nonNullSubclass(cls.declaration, compiler.world);
667 }
668 cachedTypeOfThis = result;
669 }
670 return result;
671 }
672
673 Map<Element, TypeMask> cachedTypesOfCapturedVariables =
674 new Map<Element, TypeMask>();
675
676 TypeMask getTypeOfCapturedVariable(Element element) {
677 assert(element.isField);
678 return cachedTypesOfCapturedVariables.putIfAbsent(element, () {
679 return TypeMaskFactory.inferredTypeForElement(element, compiler);
680 });
681 }
682
683 /// Variables stored in the current activation. These variables are
684 /// being updated in try/catch blocks, and should be
685 /// accessed indirectly through [HLocalGet] and [HLocalSet].
686 Map<Local, HLocalValue> activationVariables = <Local, HLocalValue>{};
687 }
OLDNEW
« pkg/compiler/lib/src/ssa/builder_kernel.dart ('K') | « pkg/compiler/lib/src/ssa/graph_builder.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698