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

Side by Side Diff: pkg/compiler/lib/src/js_backend/codegen/codegen.dart

Issue 2246623002: Delete CPS IR (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 4 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) 2014, 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 library code_generator;
6
7 import '../../closure.dart' show ClosureClassElement;
8 import '../../common/codegen.dart' show CodegenRegistry;
9 import '../../constants/values.dart';
10 import '../../dart_types.dart';
11 import '../../elements/elements.dart';
12 import '../../io/source_information.dart' show SourceInformation;
13 import '../../js/js.dart' as js;
14 import '../../tree_ir/tree_ir_nodes.dart' as tree_ir;
15 import '../../tree_ir/tree_ir_nodes.dart'
16 show BuiltinMethod, BuiltinOperator, isCompoundableOperator;
17 import '../../types/types.dart' show TypeMask;
18 import '../../universe/call_structure.dart' show CallStructure;
19 import '../../universe/selector.dart' show Selector;
20 import '../../universe/use.dart' show DynamicUse, StaticUse, TypeUse;
21 import '../../util/maplet.dart';
22 import 'glue.dart';
23
24 class CodegenBailout {
25 final tree_ir.Node node;
26 final String reason;
27 CodegenBailout(this.node, this.reason);
28 String get message {
29 return 'bailout${node != null ? " on $node" : ""}: $reason';
30 }
31 }
32
33 class CodeGenerator extends tree_ir.StatementVisitor
34 with tree_ir.ExpressionVisitor<js.Expression> {
35 final CodegenRegistry registry;
36
37 final Glue glue;
38
39 ExecutableElement currentFunction;
40
41 /// Maps variables to their name.
42 Map<tree_ir.Variable, String> variableNames = <tree_ir.Variable, String>{};
43
44 /// Maps local constants to their name.
45 Maplet<VariableElement, String> constantNames =
46 new Maplet<VariableElement, String>();
47
48 /// Variable names that have already been used. Used to avoid name clashes.
49 Set<String> usedVariableNames = new Set<String>();
50
51 final tree_ir.FallthroughStack fallthrough = new tree_ir.FallthroughStack();
52
53 /// Stacks whose top element is the current target of an unlabeled break
54 /// or continue. For continues, this is the loop node itself.
55 final tree_ir.FallthroughStack shortBreak = new tree_ir.FallthroughStack();
56 final tree_ir.FallthroughStack shortContinue = new tree_ir.FallthroughStack();
57
58 /// When the top element is true, [Unreachable] statements will be emitted
59 /// as [Return]s, otherwise they are emitted as empty because they are
60 /// followed by the end of the method.
61 ///
62 /// Note on why the [fallthrough] stack should not be used for this:
63 /// Ordinary statements may choose whether to use the [fallthrough] target,
64 /// and the choice to do so may disable an optimization in [visitIf].
65 /// But omitting an unreachable 'return' should have lower priority than
66 /// the optimizations in [visitIf], so [visitIf] will instead tell the
67 /// [Unreachable] statements whether they may use fallthrough or not.
68 List<bool> emitUnreachableAsReturn = <bool>[false];
69
70 final Map<tree_ir.Label, String> labelNames = <tree_ir.Label, String>{};
71
72 List<js.Statement> accumulator = new List<js.Statement>();
73
74 CodeGenerator(this.glue, this.registry);
75
76 /// Generates JavaScript code for the body of [function].
77 js.Fun buildFunction(tree_ir.FunctionDefinition function) {
78 registerDefaultParameterValues(function.element);
79 currentFunction = function.element;
80 tree_ir.Statement statement = function.body;
81 while (statement != null) {
82 statement = visitStatement(statement);
83 }
84
85 List<js.Parameter> parameters = new List<js.Parameter>();
86 Set<tree_ir.Variable> parameterSet = new Set<tree_ir.Variable>();
87 Set<String> declaredVariables = new Set<String>();
88
89 for (tree_ir.Variable parameter in function.parameters) {
90 String name = getVariableName(parameter);
91 parameters.add(new js.Parameter(name));
92 parameterSet.add(parameter);
93 declaredVariables.add(name);
94 }
95
96 List<js.VariableInitialization> jsVariables = <js.VariableInitialization>[];
97
98 // Declare variables with an initializer. Pull statements into the
99 // initializer until we find a statement that cannot be pulled in.
100 int accumulatorIndex = 0;
101 while (accumulatorIndex < accumulator.length) {
102 js.Node node = accumulator[accumulatorIndex];
103
104 // Check that node is an assignment to a local variable.
105 if (node is! js.ExpressionStatement) break;
106 js.ExpressionStatement stmt = node;
107 if (stmt.expression is! js.Assignment) break;
108 js.Assignment assign = stmt.expression;
109 if (assign.leftHandSide is! js.VariableUse) break;
110 if (assign.op != null) break; // Compound assignment.
111 js.VariableUse use = assign.leftHandSide;
112
113 // Do not touch non-local variables.
114 if (!usedVariableNames.contains(use.name)) break;
115
116 // We cannot declare a variable more than once.
117 if (!declaredVariables.add(use.name)) break;
118
119 js.VariableInitialization jsVariable = new js.VariableInitialization(
120 new js.VariableDeclaration(use.name), assign.value);
121 jsVariables.add(jsVariable);
122
123 ++accumulatorIndex;
124 }
125
126 // If the last statement is a for loop with an initializer expression, try
127 // to pull that expression into an initializer as well.
128 pullFromForLoop: if (accumulatorIndex < accumulator.length &&
129 accumulator[accumulatorIndex] is js.For) {
130 js.For forLoop = accumulator[accumulatorIndex];
131 if (forLoop.init is! js.Assignment) break pullFromForLoop;
132 js.Assignment assign = forLoop.init;
133 if (assign.leftHandSide is! js.VariableUse) break pullFromForLoop;
134 if (assign.op != null) break pullFromForLoop; // Compound assignment.
135 js.VariableUse use = assign.leftHandSide;
136
137 // Do not touch non-local variables.
138 if (!usedVariableNames.contains(use.name)) break pullFromForLoop;
139
140 // We cannot declare a variable more than once.
141 if (!declaredVariables.add(use.name)) break pullFromForLoop;
142
143 js.VariableInitialization jsVariable = new js.VariableInitialization(
144 new js.VariableDeclaration(use.name), assign.value);
145 jsVariables.add(jsVariable);
146
147 // Remove the initializer from the for loop.
148 accumulator[accumulatorIndex] =
149 new js.For(null, forLoop.condition, forLoop.update, forLoop.body);
150 }
151
152 // Discard the statements that were pulled in the initializer.
153 if (accumulatorIndex > 0) {
154 accumulator = accumulator.sublist(accumulatorIndex);
155 }
156
157 // Declare remaining variables.
158 for (tree_ir.Variable variable in variableNames.keys) {
159 String name = getVariableName(variable);
160 if (declaredVariables.contains(name)) continue;
161 js.VariableInitialization jsVariable =
162 new js.VariableInitialization(new js.VariableDeclaration(name), null);
163 jsVariables.add(jsVariable);
164 }
165
166 if (jsVariables.length > 0) {
167 // Would be nice to avoid inserting at the beginning of list.
168 accumulator.insert(
169 0,
170 new js.ExpressionStatement(new js.VariableDeclarationList(jsVariables)
171 .withSourceInformation(function.sourceInformation)));
172 }
173 return new js.Fun(parameters, new js.Block(accumulator));
174 }
175
176 @override
177 js.Expression visitExpression(tree_ir.Expression node) {
178 js.Expression result = node.accept(this);
179 if (result == null) {
180 glue.reportInternalError('$node did not produce code.');
181 }
182 return result;
183 }
184
185 /// Generates a name for the given variable. First trying with the name of
186 /// the [Variable.element] if it is non-null.
187 String getVariableName(tree_ir.Variable variable) {
188 // Functions are not nested in the JS backend.
189 assert(variable.host == currentFunction);
190
191 // Get the name if we already have one.
192 String name = variableNames[variable];
193 if (name != null) {
194 return name;
195 }
196
197 // Synthesize a variable name that isn't used elsewhere.
198 String prefix = variable.element == null ? 'v' : variable.element.name;
199 int counter = 0;
200 name = glue.safeVariableName(
201 variable.element == null ? '$prefix$counter' : variable.element.name);
202 while (!usedVariableNames.add(name)) {
203 ++counter;
204 name = '$prefix$counter';
205 }
206 variableNames[variable] = name;
207
208 return name;
209 }
210
211 List<js.Expression> visitExpressionList(
212 List<tree_ir.Expression> expressions) {
213 List<js.Expression> result = new List<js.Expression>(expressions.length);
214 for (int i = 0; i < expressions.length; ++i) {
215 result[i] = visitExpression(expressions[i]);
216 }
217 return result;
218 }
219
220 giveup(tree_ir.Node node,
221 [String reason = 'unimplemented in CodeGenerator']) {
222 throw new CodegenBailout(node, reason);
223 }
224
225 @override
226 js.Expression visitConditional(tree_ir.Conditional node) {
227 return new js.Conditional(
228 visitExpression(node.condition),
229 visitExpression(node.thenExpression),
230 visitExpression(node.elseExpression));
231 }
232
233 js.Expression buildConstant(ConstantValue constant,
234 {SourceInformation sourceInformation}) {
235 registry.registerCompileTimeConstant(constant);
236 return glue
237 .constantReference(constant)
238 .withSourceInformation(sourceInformation);
239 }
240
241 @override
242 js.Expression visitConstant(tree_ir.Constant node) {
243 return buildConstant(node.value, sourceInformation: node.sourceInformation);
244 }
245
246 js.Expression buildStaticInvoke(Element target, List<js.Expression> arguments,
247 {SourceInformation sourceInformation}) {
248 if (target.isConstructor) {
249 // TODO(johnniwinther): Avoid dependency on [isGenerativeConstructor] by
250 // using backend-specific [StatisUse] classes.
251 registry.registerStaticUse(new StaticUse.constructorInvoke(
252 target.declaration, new CallStructure.unnamed(arguments.length)));
253 } else {
254 registry.registerStaticUse(new StaticUse.staticInvoke(
255 target.declaration, new CallStructure.unnamed(arguments.length)));
256 }
257 js.Expression elementAccess = glue.staticFunctionAccess(target);
258 return new js.Call(elementAccess, arguments,
259 sourceInformation: sourceInformation);
260 }
261
262 @override
263 js.Expression visitInvokeConstructor(tree_ir.InvokeConstructor node) {
264 if (node.constant != null) return giveup(node);
265
266 registry.registerInstantiation(node.type);
267 FunctionElement target = node.target;
268 List<js.Expression> arguments = visitExpressionList(node.arguments);
269 return buildStaticInvoke(target, arguments,
270 sourceInformation: node.sourceInformation);
271 }
272
273 void registerMethodInvoke(Selector selector, TypeMask receiverType) {
274 registry.registerDynamicUse(new DynamicUse(selector, receiverType));
275 if (!selector.isGetter && !selector.isSetter) {
276 // TODO(sigurdm): We should find a better place to register the call.
277 Selector call = new Selector.callClosureFrom(selector);
278 registry.registerDynamicUse(new DynamicUse(call, null));
279 }
280 }
281
282 @override
283 js.Expression visitInvokeMethod(tree_ir.InvokeMethod node) {
284 TypeMask mask = glue.extendMaskIfReachesAll(node.selector, node.mask);
285 registerMethodInvoke(node.selector, mask);
286 return js
287 .propertyCall(
288 visitExpression(node.receiver),
289 glue.invocationName(node.selector),
290 visitExpressionList(node.arguments))
291 .withSourceInformation(node.sourceInformation);
292 }
293
294 @override
295 js.Expression visitInvokeStatic(tree_ir.InvokeStatic node) {
296 FunctionElement target = node.target;
297 List<js.Expression> arguments = visitExpressionList(node.arguments);
298 return buildStaticInvoke(target, arguments,
299 sourceInformation: node.sourceInformation);
300 }
301
302 @override
303 js.Expression visitInvokeMethodDirectly(tree_ir.InvokeMethodDirectly node) {
304 if (node.isTearOff) {
305 // If this is a tear-off, register the fact that a tear-off closure
306 // will be created, and that this tear-off must bypass ordinary
307 // dispatch to ensure the super method is invoked.
308 registry.registerStaticUse(new StaticUse.staticInvoke(
309 glue.closureFromTearOff,
310 new CallStructure.unnamed(
311 glue.closureFromTearOff.parameters.length)));
312 registry.registerStaticUse(new StaticUse.superTearOff(node.target));
313 }
314 if (node.target is ConstructorBodyElement) {
315 registry.registerStaticUse(new StaticUse.constructorBodyInvoke(
316 node.target.declaration,
317 new CallStructure.unnamed(node.arguments.length)));
318 // A constructor body cannot be overriden or intercepted, so we can
319 // use the short form for this invocation.
320 return js.js('#.#(#)', [
321 visitExpression(node.receiver),
322 glue.instanceMethodName(node.target),
323 visitExpressionList(node.arguments)
324 ]).withSourceInformation(node.sourceInformation);
325 }
326 registry.registerStaticUse(new StaticUse.superInvoke(
327 node.target.declaration,
328 new CallStructure.unnamed(node.arguments.length)));
329 return js.js('#.#.call(#, #)', [
330 glue.prototypeAccess(node.target.enclosingClass),
331 glue.invocationName(node.selector),
332 visitExpression(node.receiver),
333 visitExpressionList(node.arguments)
334 ]).withSourceInformation(node.sourceInformation);
335 }
336
337 @override
338 js.Expression visitOneShotInterceptor(tree_ir.OneShotInterceptor node) {
339 registerMethodInvoke(node.selector, node.mask);
340 registry.registerUseInterceptor();
341 return js.js('#.#(#)', [
342 glue.getInterceptorLibrary(),
343 glue.registerOneShotInterceptor(node.selector),
344 visitExpressionList(node.arguments)
345 ]).withSourceInformation(node.sourceInformation);
346 }
347
348 @override
349 js.Expression visitLiteralList(tree_ir.LiteralList node) {
350 registry.registerInstantiatedClass(glue.listClass);
351 List<js.Expression> entries = visitExpressionList(node.values);
352 return new js.ArrayInitializer(entries);
353 }
354
355 @override
356 js.Expression visitLogicalOperator(tree_ir.LogicalOperator node) {
357 return new js.Binary(
358 node.operator, visitExpression(node.left), visitExpression(node.right));
359 }
360
361 @override
362 js.Expression visitNot(tree_ir.Not node) {
363 return new js.Prefix("!", visitExpression(node.operand));
364 }
365
366 @override
367 js.Expression visitThis(tree_ir.This node) {
368 return new js.This();
369 }
370
371 /// Ensure that 'instanceof' checks may be performed against [class_].
372 ///
373 /// Even if the class is never instantiated, a JS constructor must be emitted
374 /// so the 'instanceof' expression does not throw an exception at runtime.
375 bool tryRegisterInstanceofCheck(ClassElement class_) {
376 if (glue.classWorld.isInstantiated(class_)) {
377 // Ensure the class remains instantiated during backend tree-shaking.
378 // TODO(asgerf): We could have a more precise hook to inform the emitter
379 // that the JS constructor function is needed, without the class being
380 // instantiated.
381 registry.registerInstantiatedClass(class_);
382 return true;
383 }
384 // Will throw if the JS constructor is not emitted, so do not allow the
385 // instanceof check. This should only happen when certain optimization
386 // passes are disabled, as the type check itself is trivial.
387 return false;
388 }
389
390 @override
391 js.Expression visitTypeOperator(tree_ir.TypeOperator node) {
392 js.Expression value = visitExpression(node.value);
393 List<js.Expression> typeArguments = visitExpressionList(node.typeArguments);
394 DartType type = node.type;
395 if (type is InterfaceType) {
396 registry.registerTypeUse(new TypeUse.isCheck(type));
397 ClassElement clazz = type.element;
398
399 if (glue.isStringClass(clazz)) {
400 if (node.isTypeTest) {
401 return js.js(r'typeof # === "string"', <js.Expression>[value]);
402 }
403 // TODO(sra): Implement fast cast via calling 'stringTypeCast'.
404 } else if (glue.isBoolClass(clazz)) {
405 if (node.isTypeTest) {
406 return js.js(r'typeof # === "boolean"', <js.Expression>[value]);
407 }
408 // TODO(sra): Implement fast cast via calling 'boolTypeCast'.
409 } else if (node.isTypeTest &&
410 node.typeArguments.isEmpty &&
411 glue.mayGenerateInstanceofCheck(type) &&
412 tryRegisterInstanceofCheck(clazz)) {
413 return js.js('# instanceof #', [value, glue.constructorAccess(clazz)]);
414 }
415
416 // The helper we use needs the JSArray class to exist, but for some
417 // reason the helper does not cause this dependency to be registered.
418 // TODO(asgerf): Most programs need List anyway, but we should fix this.
419 registry.registerInstantiatedClass(glue.listClass);
420
421 // We use one of the two helpers:
422 //
423 // checkSubtype(value, $isT, typeArgs, $asT)
424 // subtypeCast(value, $isT, typeArgs, $asT)
425 //
426 // Any of the last two arguments may be null if there are no type
427 // arguments, and/or if no substitution is required.
428 Element function =
429 node.isTypeTest ? glue.getCheckSubtype() : glue.getSubtypeCast();
430
431 js.Expression isT = js.quoteName(glue.getTypeTestTag(type));
432
433 js.Expression typeArgumentArray = typeArguments.isNotEmpty
434 ? new js.ArrayInitializer(typeArguments)
435 : new js.LiteralNull();
436
437 js.Expression asT = glue.hasStrictSubtype(clazz)
438 ? js.quoteName(glue.getTypeSubstitutionTag(clazz))
439 : new js.LiteralNull();
440
441 return buildStaticHelperInvocation(
442 function, <js.Expression>[value, isT, typeArgumentArray, asT]);
443 } else if (type is TypeVariableType || type is FunctionType) {
444 registry.registerTypeUse(new TypeUse.isCheck(type));
445
446 Element function = node.isTypeTest
447 ? glue.getCheckSubtypeOfRuntimeType()
448 : glue.getSubtypeOfRuntimeTypeCast();
449
450 // The only type argument is the type held in the type variable.
451 js.Expression typeValue = typeArguments.single;
452
453 return buildStaticHelperInvocation(
454 function, <js.Expression>[value, typeValue]);
455 }
456 return giveup(node, 'type check unimplemented for $type.');
457 }
458
459 @override
460 js.Expression visitGetTypeTestProperty(tree_ir.GetTypeTestProperty node) {
461 js.Expression object = visitExpression(node.object);
462 DartType dartType = node.dartType;
463 assert(dartType.isInterfaceType);
464 registry.registerTypeUse(new TypeUse.isCheck(dartType));
465 //glue.registerIsCheck(dartType, registry);
466 js.Expression property = glue.getTypeTestTag(dartType);
467 return js.js(r'#.#', [object, property]);
468 }
469
470 @override
471 js.Expression visitVariableUse(tree_ir.VariableUse node) {
472 return buildVariableAccess(node.variable)
473 .withSourceInformation(node.sourceInformation);
474 }
475
476 js.Expression buildVariableAccess(tree_ir.Variable variable) {
477 return new js.VariableUse(getVariableName(variable));
478 }
479
480 /// Returns the JS operator for the given built-in operator for use in a
481 /// compound assignment (not including the '=' sign).
482 String getAsCompoundOperator(BuiltinOperator operator) {
483 switch (operator) {
484 case BuiltinOperator.NumAdd:
485 case BuiltinOperator.StringConcatenate:
486 return '+';
487 case BuiltinOperator.NumSubtract:
488 return '-';
489 case BuiltinOperator.NumMultiply:
490 return '*';
491 case BuiltinOperator.NumDivide:
492 return '/';
493 case BuiltinOperator.NumRemainder:
494 return '%';
495 default:
496 throw 'Not a compoundable operator: $operator';
497 }
498 }
499
500 bool isCompoundableBuiltin(tree_ir.Expression exp) {
501 return exp is tree_ir.ApplyBuiltinOperator &&
502 exp.arguments.length == 2 &&
503 isCompoundableOperator(exp.operator);
504 }
505
506 bool isOneConstant(tree_ir.Expression exp) {
507 return exp is tree_ir.Constant && exp.value.isOne;
508 }
509
510 js.Expression makeAssignment(js.Expression leftHand, tree_ir.Expression value,
511 {SourceInformation sourceInformation, BuiltinOperator compound}) {
512 if (isOneConstant(value)) {
513 if (compound == BuiltinOperator.NumAdd) {
514 return new js.Prefix('++', leftHand)
515 .withSourceInformation(sourceInformation);
516 }
517 if (compound == BuiltinOperator.NumSubtract) {
518 return new js.Prefix('--', leftHand)
519 .withSourceInformation(sourceInformation);
520 }
521 }
522 if (compound != null) {
523 return new js.Assignment.compound(
524 leftHand, getAsCompoundOperator(compound), visitExpression(value))
525 .withSourceInformation(sourceInformation);
526 }
527 return new js.Assignment(leftHand, visitExpression(value))
528 .withSourceInformation(sourceInformation);
529 }
530
531 @override
532 js.Expression visitAssign(tree_ir.Assign node) {
533 js.Expression variable = buildVariableAccess(node.variable);
534 if (isCompoundableBuiltin(node.value)) {
535 tree_ir.ApplyBuiltinOperator rhs = node.value;
536 tree_ir.Expression left = rhs.arguments[0];
537 tree_ir.Expression right = rhs.arguments[1];
538 if (left is tree_ir.VariableUse && left.variable == node.variable) {
539 return makeAssignment(variable, right,
540 compound: rhs.operator, sourceInformation: node.sourceInformation);
541 }
542 }
543 return makeAssignment(variable, node.value,
544 sourceInformation: node.sourceInformation);
545 }
546
547 @override
548 void visitContinue(tree_ir.Continue node) {
549 tree_ir.Statement next = fallthrough.target;
550 if (node.target.binding == next ||
551 next is tree_ir.Continue && node.target == next.target) {
552 // Fall through to continue target or to equivalent continue.
553 fallthrough.use();
554 } else if (node.target.binding == shortContinue.target) {
555 // The target is the immediately enclosing loop.
556 shortContinue.use();
557 accumulator.add(new js.Continue(null));
558 } else {
559 accumulator.add(new js.Continue(makeLabel(node.target)));
560 }
561 }
562
563 /// True if [other] is the target of [node] or is a [Break] with the same
564 /// target. This means jumping to [other] is equivalent to executing [node].
565 bool isEffectiveBreakTarget(tree_ir.Break node, tree_ir.Statement other) {
566 return node.target.binding.next == other ||
567 other is tree_ir.Break && node.target == other.target;
568 }
569
570 /// True if the given break is equivalent to an unlabeled continue.
571 bool isShortContinue(tree_ir.Break node) {
572 tree_ir.Statement next = node.target.binding.next;
573 return next is tree_ir.Continue &&
574 next.target.binding == shortContinue.target;
575 }
576
577 @override
578 void visitBreak(tree_ir.Break node) {
579 if (isEffectiveBreakTarget(node, fallthrough.target)) {
580 // Fall through to break target or to equivalent break.
581 fallthrough.use();
582 } else if (isEffectiveBreakTarget(node, shortBreak.target)) {
583 // Unlabeled break to the break target or to an equivalent break.
584 shortBreak.use();
585 accumulator.add(new js.Break(null));
586 } else if (isShortContinue(node)) {
587 // An unlabeled continue is better than a labeled break.
588 shortContinue.use();
589 accumulator.add(new js.Continue(null));
590 } else {
591 accumulator.add(new js.Break(makeLabel(node.target)));
592 }
593 }
594
595 @override
596 visitExpressionStatement(tree_ir.ExpressionStatement node) {
597 js.Expression exp = visitExpression(node.expression);
598 if (node.next is tree_ir.Unreachable && emitUnreachableAsReturn.last) {
599 // Emit as 'return exp' to assist local analysis in the VM.
600 SourceInformation sourceInformation = node.expression.sourceInformation;
601 accumulator
602 .add(new js.Return(exp).withSourceInformation(sourceInformation));
603 return null;
604 } else {
605 accumulator.add(new js.ExpressionStatement(exp));
606 return node.next;
607 }
608 }
609
610 bool isNullReturn(tree_ir.Statement node) {
611 return node is tree_ir.Return && isNull(node.value);
612 }
613
614 bool isEndOfMethod(tree_ir.Statement node) {
615 return isNullReturn(node) ||
616 node is tree_ir.Break && isNullReturn(node.target.binding.next);
617 }
618
619 @override
620 visitIf(tree_ir.If node) {
621 js.Expression condition = visitExpression(node.condition);
622 int usesBefore = fallthrough.useCount;
623 // Unless the 'else' part ends the method. make sure to terminate any
624 // uncompletable code paths in the 'then' part.
625 emitUnreachableAsReturn.add(!isEndOfMethod(node.elseStatement));
626 js.Statement thenBody = buildBodyStatement(node.thenStatement);
627 emitUnreachableAsReturn.removeLast();
628 bool thenHasFallthrough = (fallthrough.useCount > usesBefore);
629 if (thenHasFallthrough) {
630 js.Statement elseBody = buildBodyStatement(node.elseStatement);
631 accumulator.add(new js.If(condition, thenBody, elseBody)
632 .withSourceInformation(node.sourceInformation));
633 return null;
634 } else {
635 // The 'then' body cannot complete normally, so emit a short 'if'
636 // and put the 'else' body after it.
637 accumulator.add(new js.If.noElse(condition, thenBody)
638 .withSourceInformation(node.sourceInformation));
639 return node.elseStatement;
640 }
641 }
642
643 @override
644 visitLabeledStatement(tree_ir.LabeledStatement node) {
645 fallthrough.push(node.next);
646 js.Statement body = buildBodyStatement(node.body);
647 fallthrough.pop();
648 accumulator.add(insertLabel(node.label, body));
649 return node.next;
650 }
651
652 /// Creates a name for [label] if it does not already have one.
653 ///
654 /// This also marks the label as being used.
655 String makeLabel(tree_ir.Label label) {
656 return labelNames.putIfAbsent(label, () => 'L${labelNames.length}');
657 }
658
659 /// Wraps a node in a labeled statement unless the label is unused.
660 js.Statement insertLabel(tree_ir.Label label, js.Statement node) {
661 String name = labelNames[label];
662 if (name == null) return node; // Label is unused.
663 return new js.LabeledStatement(name, node);
664 }
665
666 /// Returns the current [accumulator] wrapped in a block if neccessary.
667 js.Statement _bodyAsStatement() {
668 if (accumulator.length == 0) {
669 return new js.EmptyStatement();
670 }
671 if (accumulator.length == 1) {
672 return accumulator.single;
673 }
674 return new js.Block(accumulator);
675 }
676
677 /// Builds a nested statement.
678 js.Statement buildBodyStatement(tree_ir.Statement statement) {
679 List<js.Statement> savedAccumulator = accumulator;
680 accumulator = <js.Statement>[];
681 while (statement != null) {
682 statement = visitStatement(statement);
683 }
684 js.Statement result = _bodyAsStatement();
685 accumulator = savedAccumulator;
686 return result;
687 }
688
689 js.Block buildBodyBlock(tree_ir.Statement statement) {
690 List<js.Statement> savedAccumulator = accumulator;
691 accumulator = <js.Statement>[];
692 while (statement != null) {
693 statement = visitStatement(statement);
694 }
695 js.Statement result = new js.Block(accumulator);
696 accumulator = savedAccumulator;
697 return result;
698 }
699
700 js.Expression makeSequence(List<tree_ir.Expression> list) {
701 return list.map(visitExpression).reduce((x, y) => new js.Binary(',', x, y));
702 }
703
704 @override
705 visitFor(tree_ir.For node) {
706 js.Expression condition = visitExpression(node.condition);
707 shortBreak.push(node.next);
708 shortContinue.push(node);
709 fallthrough.push(node);
710 emitUnreachableAsReturn.add(true);
711 js.Statement body = buildBodyStatement(node.body);
712 emitUnreachableAsReturn.removeLast();
713 fallthrough.pop();
714 shortContinue.pop();
715 shortBreak.pop();
716 js.Statement loopNode;
717 if (node.updates.isEmpty) {
718 loopNode = new js.While(condition, body);
719 } else {
720 // Compile as a for loop.
721 js.Expression init;
722 if (accumulator.isNotEmpty &&
723 accumulator.last is js.ExpressionStatement) {
724 // Take the preceding expression from the accumulator and use
725 // it as the initializer expression.
726 js.ExpressionStatement initStmt = accumulator.removeLast();
727 init = initStmt.expression;
728 }
729 js.Expression update = makeSequence(node.updates);
730 loopNode = new js.For(init, condition, update, body);
731 }
732 accumulator.add(insertLabel(node.label, loopNode));
733 return node.next;
734 }
735
736 @override
737 void visitWhileTrue(tree_ir.WhileTrue node) {
738 // A short break in the while will jump to the current fallthrough target.
739 shortBreak.push(fallthrough.target);
740 shortContinue.push(node);
741 fallthrough.push(node);
742 emitUnreachableAsReturn.add(true);
743 js.Statement jsBody = buildBodyStatement(node.body);
744 emitUnreachableAsReturn.removeLast();
745 fallthrough.pop();
746 shortContinue.pop();
747 if (shortBreak.useCount > 0) {
748 // Short breaks use the current fallthrough target.
749 fallthrough.use();
750 }
751 shortBreak.pop();
752 accumulator
753 .add(insertLabel(node.label, new js.For(null, null, null, jsBody)));
754 }
755
756 bool isNull(tree_ir.Expression node) {
757 return node is tree_ir.Constant && node.value.isNull;
758 }
759
760 @override
761 void visitReturn(tree_ir.Return node) {
762 if (isNull(node.value) && fallthrough.target == null) {
763 // Do nothing. Implicitly return JS undefined by falling over the end.
764 registry.registerCompileTimeConstant(new NullConstantValue());
765 fallthrough.use();
766 } else {
767 accumulator.add(new js.Return(visitExpression(node.value))
768 .withSourceInformation(node.sourceInformation));
769 }
770 }
771
772 @override
773 void visitThrow(tree_ir.Throw node) {
774 accumulator.add(new js.Throw(visitExpression(node.value)));
775 }
776
777 @override
778 void visitUnreachable(tree_ir.Unreachable node) {
779 if (emitUnreachableAsReturn.last) {
780 // Emit a return to assist local analysis in the VM.
781 accumulator.add(new js.Return());
782 }
783 }
784
785 @override
786 void visitTry(tree_ir.Try node) {
787 js.Block tryBlock = buildBodyBlock(node.tryBody);
788 tree_ir.Variable exceptionVariable = node.catchParameters.first;
789 js.VariableDeclaration exceptionParameter =
790 new js.VariableDeclaration(getVariableName(exceptionVariable));
791 js.Block catchBlock = buildBodyBlock(node.catchBody);
792 js.Catch catchPart = new js.Catch(exceptionParameter, catchBlock);
793 accumulator.add(new js.Try(tryBlock, catchPart, null));
794 }
795
796 @override
797 js.Expression visitCreateBox(tree_ir.CreateBox node) {
798 return new js.ObjectInitializer(const <js.Property>[]);
799 }
800
801 @override
802 js.Expression visitCreateInstance(tree_ir.CreateInstance node) {
803 ClassElement classElement = node.classElement;
804 // TODO(asgerf): To allow inlining of InvokeConstructor, CreateInstance must
805 // carry a DartType so we can register the instantiated type
806 // with its type arguments. Otherwise dataflow analysis is
807 // needed to reconstruct the instantiated type.
808 registry.registerInstantiation(classElement.rawType);
809 if (classElement is ClosureClassElement) {
810 registry.registerInstantiatedClosure(classElement.methodElement);
811 }
812 js.Expression instance = new js.New(glue.constructorAccess(classElement),
813 visitExpressionList(node.arguments))
814 .withSourceInformation(node.sourceInformation);
815
816 tree_ir.Expression typeInformation = node.typeInformation;
817 if (typeInformation != null) {
818 FunctionElement helper = glue.getAddRuntimeTypeInformation();
819 js.Expression typeArguments = visitExpression(typeInformation);
820 return buildStaticHelperInvocation(
821 helper, <js.Expression>[instance, typeArguments],
822 sourceInformation: node.sourceInformation);
823 } else {
824 return instance;
825 }
826 }
827
828 @override
829 js.Expression visitCreateInvocationMirror(
830 tree_ir.CreateInvocationMirror node) {
831 js.Expression name = js.string(node.selector.name);
832 js.Expression internalName =
833 js.quoteName(glue.invocationName(node.selector));
834 js.Expression kind = js.number(node.selector.invocationMirrorKind);
835 js.Expression arguments =
836 new js.ArrayInitializer(visitExpressionList(node.arguments));
837 js.Expression argumentNames = new js.ArrayInitializer(
838 node.selector.namedArguments.map(js.string).toList(growable: false));
839 return buildStaticHelperInvocation(glue.createInvocationMirrorMethod,
840 <js.Expression>[name, internalName, kind, arguments, argumentNames]);
841 }
842
843 @override
844 js.Expression visitInterceptor(tree_ir.Interceptor node) {
845 registry.registerUseInterceptor();
846 // Default to all intercepted classes if they have not been computed.
847 // This is to ensure we can run codegen without prior optimization passes.
848 Set<ClassElement> interceptedClasses = node.interceptedClasses.isEmpty
849 ? glue.interceptedClasses
850 : node.interceptedClasses;
851 registry.registerSpecializedGetInterceptor(interceptedClasses);
852 js.Name helperName = glue.getInterceptorName(interceptedClasses);
853 js.Expression globalHolder = glue.getInterceptorLibrary();
854 return js.js('#.#(#)', [
855 globalHolder,
856 helperName,
857 visitExpression(node.input)
858 ]).withSourceInformation(node.sourceInformation);
859 }
860
861 @override
862 js.Expression visitGetField(tree_ir.GetField node) {
863 registry.registerStaticUse(new StaticUse.fieldGet(node.field));
864 return new js.PropertyAccess(visitExpression(node.object),
865 glue.instanceFieldPropertyName(node.field))
866 .withSourceInformation(node.sourceInformation);
867 }
868
869 @override
870 js.Expression visitSetField(tree_ir.SetField node) {
871 registry.registerStaticUse(new StaticUse.fieldSet(node.field));
872 js.PropertyAccess field = new js.PropertyAccess(
873 visitExpression(node.object),
874 glue.instanceFieldPropertyName(node.field));
875 return makeAssignment(field, node.value,
876 compound: node.compound, sourceInformation: node.sourceInformation);
877 }
878
879 @override
880 js.Expression visitGetStatic(tree_ir.GetStatic node) {
881 assert(node.element is FieldElement || node.element is FunctionElement);
882 if (node.element is FunctionElement) {
883 // Tear off a method.
884 registry.registerStaticUse(
885 new StaticUse.staticTearOff(node.element.declaration));
886 return glue
887 .isolateStaticClosureAccess(node.element)
888 .withSourceInformation(node.sourceInformation);
889 }
890 if (node.useLazyGetter) {
891 // Read a lazily initialized field.
892 registry.registerStaticUse(
893 new StaticUse.staticInit(node.element.declaration));
894 js.Expression getter = glue.isolateLazyInitializerAccess(node.element);
895 return new js.Call(getter, <js.Expression>[],
896 sourceInformation: node.sourceInformation);
897 }
898 // Read an eagerly initialized field.
899 registry
900 .registerStaticUse(new StaticUse.staticGet(node.element.declaration));
901 return glue
902 .staticFieldAccess(node.element)
903 .withSourceInformation(node.sourceInformation);
904 }
905
906 @override
907 js.Expression visitSetStatic(tree_ir.SetStatic node) {
908 assert(node.element is FieldElement);
909 registry
910 .registerStaticUse(new StaticUse.staticSet(node.element.declaration));
911 js.Expression field = glue.staticFieldAccess(node.element);
912 return makeAssignment(field, node.value,
913 compound: node.compound, sourceInformation: node.sourceInformation);
914 }
915
916 @override
917 js.Expression visitGetLength(tree_ir.GetLength node) {
918 return new js.PropertyAccess.field(visitExpression(node.object), 'length');
919 }
920
921 @override
922 js.Expression visitGetIndex(tree_ir.GetIndex node) {
923 return new js.PropertyAccess(
924 visitExpression(node.object), visitExpression(node.index));
925 }
926
927 @override
928 js.Expression visitSetIndex(tree_ir.SetIndex node) {
929 js.Expression index = new js.PropertyAccess(
930 visitExpression(node.object), visitExpression(node.index));
931 return makeAssignment(index, node.value, compound: node.compound);
932 }
933
934 js.Expression buildStaticHelperInvocation(
935 FunctionElement helper, List<js.Expression> arguments,
936 {SourceInformation sourceInformation}) {
937 registry.registerStaticUse(new StaticUse.staticInvoke(
938 helper, new CallStructure.unnamed(arguments.length)));
939 return buildStaticInvoke(helper, arguments,
940 sourceInformation: sourceInformation);
941 }
942
943 @override
944 js.Expression visitReifyRuntimeType(tree_ir.ReifyRuntimeType node) {
945 js.Expression typeToString = buildStaticHelperInvocation(
946 glue.getRuntimeTypeToString(), [visitExpression(node.value)],
947 sourceInformation: node.sourceInformation);
948 return buildStaticHelperInvocation(
949 glue.getCreateRuntimeType(), [typeToString],
950 sourceInformation: node.sourceInformation);
951 }
952
953 @override
954 js.Expression visitReadTypeVariable(tree_ir.ReadTypeVariable node) {
955 ClassElement context = node.variable.element.enclosingClass;
956 js.Expression index = js.number(glue.getTypeVariableIndex(node.variable));
957 if (glue.needsSubstitutionForTypeVariableAccess(context)) {
958 js.Expression typeName = glue.getRuntimeTypeName(context);
959 return buildStaticHelperInvocation(glue.getRuntimeTypeArgument(),
960 [visitExpression(node.target), typeName, index],
961 sourceInformation: node.sourceInformation);
962 } else {
963 return buildStaticHelperInvocation(
964 glue.getTypeArgumentByIndex(), [visitExpression(node.target), index],
965 sourceInformation: node.sourceInformation);
966 }
967 }
968
969 @override
970 js.Expression visitTypeExpression(tree_ir.TypeExpression node) {
971 List<js.Expression> arguments = visitExpressionList(node.arguments);
972 switch (node.kind) {
973 case tree_ir.TypeExpressionKind.COMPLETE:
974 return glue.generateTypeRepresentation(
975 node.dartType, arguments, registry);
976 case tree_ir.TypeExpressionKind.INSTANCE:
977 // We expect only flat types for the INSTANCE representation.
978 assert(
979 node.dartType == (node.dartType.element as ClassElement).thisType);
980 registry.registerInstantiatedClass(glue.listClass);
981 return new js.ArrayInitializer(arguments);
982 }
983 }
984
985 js.Node handleForeignCode(tree_ir.ForeignCode node) {
986 if (node.dependency != null) {
987 // Dependency is only used if [node] calls a Dart function. Currently only
988 // through foreign function `RAW_DART_FUNCTION_REF`.
989 registry.registerStaticUse(new StaticUse.staticInvoke(
990 node.dependency, new CallStructure.unnamed(node.arguments.length)));
991 }
992 // TODO(sra,johnniwinther): Should this be in CodegenRegistry?
993 glue.registerNativeBehavior(node.nativeBehavior, node);
994 return node.codeTemplate
995 .instantiate(visitExpressionList(node.arguments))
996 .withSourceInformation(node.sourceInformation);
997 }
998
999 @override
1000 js.Expression visitForeignExpression(tree_ir.ForeignExpression node) {
1001 return handleForeignCode(node);
1002 }
1003
1004 @override
1005 void visitForeignStatement(tree_ir.ForeignStatement node) {
1006 accumulator.add(handleForeignCode(node));
1007 }
1008
1009 @override
1010 visitYield(tree_ir.Yield node) {
1011 js.Expression value = visitExpression(node.input);
1012 accumulator.add(new js.DartYield(value, node.hasStar));
1013 return node.next;
1014 }
1015
1016 @override
1017 visitReceiverCheck(tree_ir.ReceiverCheck node) {
1018 js.Expression value = visitExpression(node.value);
1019 // TODO(sra): Try to use the selector even when [useSelector] is false. The
1020 // reason we use 'toString' is that it is always defined so avoids a slow
1021 // lookup (in V8) of an absent property. We could use the property for the
1022 // selector if we knew it was present. The property is present if the
1023 // associated method was not inlined away, or if there is a noSuchMethod
1024 // hook for that selector. We don't know these things here, but the decision
1025 // could be deferred by creating a deferred property that was resolved after
1026 // codegen.
1027 js.Expression access = node.useSelector
1028 ? js.js('#.#', [value, glue.invocationName(node.selector)])
1029 : js.js('#.toString', [value]);
1030 if (node.useInvoke) {
1031 access = new js.Call(access, []);
1032 }
1033 if (node.condition != null) {
1034 js.Expression condition = visitExpression(node.condition);
1035 js.Statement body = isNullReturn(node.next)
1036 ? new js.ExpressionStatement(access)
1037 : new js.Return(access);
1038 accumulator.add(new js.If.noElse(condition, body));
1039 } else {
1040 accumulator.add(new js.ExpressionStatement(access));
1041 }
1042 return node.next;
1043 }
1044
1045 @override
1046 js.Expression visitApplyBuiltinOperator(tree_ir.ApplyBuiltinOperator node) {
1047 List<js.Expression> args = visitExpressionList(node.arguments);
1048
1049 js.Expression createExpression() {
1050 switch (node.operator) {
1051 case BuiltinOperator.NumAdd:
1052 return new js.Binary('+', args[0], args[1]);
1053 case BuiltinOperator.NumSubtract:
1054 return new js.Binary('-', args[0], args[1]);
1055 case BuiltinOperator.NumMultiply:
1056 return new js.Binary('*', args[0], args[1]);
1057 case BuiltinOperator.NumDivide:
1058 return new js.Binary('/', args[0], args[1]);
1059 case BuiltinOperator.NumRemainder:
1060 return new js.Binary('%', args[0], args[1]);
1061 case BuiltinOperator.NumTruncatingDivideToSigned32:
1062 return js.js('(# / #) | 0', args);
1063 case BuiltinOperator.NumAnd:
1064 return normalizeBitOp(js.js('# & #', args), node);
1065 case BuiltinOperator.NumOr:
1066 return normalizeBitOp(js.js('# | #', args), node);
1067 case BuiltinOperator.NumXor:
1068 return normalizeBitOp(js.js('# ^ #', args), node);
1069 case BuiltinOperator.NumLt:
1070 return new js.Binary('<', args[0], args[1]);
1071 case BuiltinOperator.NumLe:
1072 return new js.Binary('<=', args[0], args[1]);
1073 case BuiltinOperator.NumGt:
1074 return new js.Binary('>', args[0], args[1]);
1075 case BuiltinOperator.NumGe:
1076 return new js.Binary('>=', args[0], args[1]);
1077 case BuiltinOperator.NumShl:
1078 return normalizeBitOp(js.js('# << #', args), node);
1079 case BuiltinOperator.NumShr:
1080 // No normalization required since output is always uint32.
1081 return js.js('# >>> #', args);
1082 case BuiltinOperator.NumBitNot:
1083 return js.js('(~#) >>> 0', args);
1084 case BuiltinOperator.NumNegate:
1085 return js.js('-#', args);
1086 case BuiltinOperator.StringConcatenate:
1087 if (args.isEmpty) return js.string('');
1088 return args.reduce((e1, e2) => new js.Binary('+', e1, e2));
1089 case BuiltinOperator.CharCodeAt:
1090 return js.js('#.charCodeAt(#)', args);
1091 case BuiltinOperator.Identical:
1092 registry.registerStaticUse(new StaticUse.staticInvoke(
1093 glue.identicalFunction, new CallStructure.unnamed(args.length)));
1094 return buildStaticHelperInvocation(glue.identicalFunction, args);
1095 case BuiltinOperator.StrictEq:
1096 return new js.Binary('===', args[0], args[1]);
1097 case BuiltinOperator.StrictNeq:
1098 return new js.Binary('!==', args[0], args[1]);
1099 case BuiltinOperator.LooseEq:
1100 return new js.Binary('==', args[0], args[1]);
1101 case BuiltinOperator.LooseNeq:
1102 return new js.Binary('!=', args[0], args[1]);
1103 case BuiltinOperator.IsFalsy:
1104 return new js.Prefix('!', args[0]);
1105 case BuiltinOperator.IsNumber:
1106 return js.js('typeof # === "number"', args);
1107 case BuiltinOperator.IsNotNumber:
1108 return js.js('typeof # !== "number"', args);
1109 case BuiltinOperator.IsFloor:
1110 return js.js('Math.floor(#) === #', args);
1111 case BuiltinOperator.IsInteger:
1112 return js.js('typeof # === "number" && Math.floor(#) === #', args);
1113 case BuiltinOperator.IsNotInteger:
1114 return js.js('typeof # !== "number" || Math.floor(#) !== #', args);
1115 case BuiltinOperator.IsUnsigned32BitInteger:
1116 return js.js('# >>> 0 === #', args);
1117 case BuiltinOperator.IsNotUnsigned32BitInteger:
1118 return js.js('# >>> 0 !== #', args);
1119 case BuiltinOperator.IsFixedLengthJSArray:
1120 // TODO(sra): Remove boolify (i.e. !!).
1121 return js.js(r'!!#.fixed$length', args);
1122 case BuiltinOperator.IsExtendableJSArray:
1123 return js.js(r'!#.fixed$length', args);
1124 case BuiltinOperator.IsModifiableJSArray:
1125 return js.js(r'!#.immutable$list', args);
1126 case BuiltinOperator.IsUnmodifiableJSArray:
1127 // TODO(sra): Remove boolify (i.e. !!).
1128 return js.js(r'!!#.immutable$list', args);
1129 }
1130 }
1131
1132 return createExpression().withSourceInformation(node.sourceInformation);
1133 }
1134
1135 /// Add a uint32 normalization `op >>> 0` to [op] if it is not in 31-bit
1136 /// range.
1137 js.Expression normalizeBitOp(
1138 js.Expression op, tree_ir.ApplyBuiltinOperator node) {
1139 const MAX_UINT31 = 0x7fffffff;
1140 const MAX_UINT32 = 0xffffffff;
1141
1142 int constantValue(tree_ir.Expression e) {
1143 if (e is tree_ir.Constant) {
1144 ConstantValue value = e.value;
1145 if (!value.isInt) return null;
1146 IntConstantValue intConstant = value;
1147 if (intConstant.primitiveValue < 0) return null;
1148 if (intConstant.primitiveValue > MAX_UINT32) return null;
1149 return intConstant.primitiveValue;
1150 }
1151 return null;
1152 }
1153
1154 /// Returns a value of the form 0b0001xxxx to represent the highest bit set
1155 /// in the result. This represents the range [0, 0b00011111], up to 32
1156 /// bits. `null` represents a result possibly outside the uint32 range.
1157 int maxBitOf(tree_ir.Expression e) {
1158 if (e is tree_ir.Constant) {
1159 return constantValue(e);
1160 }
1161 if (e is tree_ir.ApplyBuiltinOperator) {
1162 if (e.operator == BuiltinOperator.NumAnd) {
1163 int left = maxBitOf(e.arguments[0]);
1164 int right = maxBitOf(e.arguments[1]);
1165 if (left == null && right == null) return MAX_UINT32;
1166 if (left == null) return right;
1167 if (right == null) return left;
1168 return (left < right) ? left : right;
1169 }
1170 if (e.operator == BuiltinOperator.NumOr ||
1171 e.operator == BuiltinOperator.NumXor) {
1172 int left = maxBitOf(e.arguments[0]);
1173 int right = maxBitOf(e.arguments[1]);
1174 if (left == null || right == null) return MAX_UINT32;
1175 return left | right;
1176 }
1177 if (e.operator == BuiltinOperator.NumShr) {
1178 int right = constantValue(e.arguments[1]);
1179 // NumShr is JavaScript '>>>' so always generates a uint32 result.
1180 if (right == null || right <= 0 || right > 31) return MAX_UINT32;
1181 int left = maxBitOf(e.arguments[0]);
1182 if (left == null) return MAX_UINT32;
1183 return left >> right;
1184 }
1185 if (e.operator == BuiltinOperator.NumShl) {
1186 int right = constantValue(e.arguments[1]);
1187 if (right == null || right <= 0 || right > 31) return MAX_UINT32;
1188 int left = maxBitOf(e.arguments[0]);
1189 if (left == null) return MAX_UINT32;
1190 if (left.bitLength + right > 31) return MAX_UINT32;
1191 return left << right;
1192 }
1193 }
1194 return null;
1195 }
1196
1197 int maxBit = maxBitOf(node);
1198 if (maxBit != null && maxBit <= MAX_UINT31) return op;
1199 return js.js('# >>> 0', [op]);
1200 }
1201
1202 @override
1203 js.Expression visitApplyBuiltinMethod(tree_ir.ApplyBuiltinMethod node) {
1204 js.Expression receiver = visitExpression(node.receiver);
1205 List<js.Expression> args = visitExpressionList(node.arguments);
1206 switch (node.method) {
1207 case BuiltinMethod.Push:
1208 return js.js('#.push(#)', [receiver, args]);
1209
1210 case BuiltinMethod.Pop:
1211 return js.js('#.pop()', [receiver]);
1212
1213 case BuiltinMethod.SetLength:
1214 return js.js('#.length = #', [receiver, args[0]]);
1215 }
1216 }
1217
1218 @override
1219 js.Expression visitAwait(tree_ir.Await node) {
1220 return new js.Await(visitExpression(node.input));
1221 }
1222
1223 /// Ensures that parameter defaults will be emitted.
1224 ///
1225 /// Ideally, this should be done when generating the relevant stub methods,
1226 /// since those are the ones that actually reference the constants, but those
1227 /// are created by the emitter when it is too late to register new constants.
1228 ///
1229 /// For non-static methods, we have no way of knowing if the defaults are
1230 /// actually used, so we conservatively register them all.
1231 void registerDefaultParameterValues(ExecutableElement element) {
1232 if (element is! FunctionElement) return;
1233 FunctionElement function = element;
1234 if (function.isStatic) return; // Defaults are inlined at call sites.
1235 function.functionSignature.forEachOptionalParameter((param) {
1236 ConstantValue constant = glue.getDefaultParameterValue(param);
1237 registry.registerCompileTimeConstant(constant);
1238 });
1239 }
1240 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js_backend/backend.dart ('k') | pkg/compiler/lib/src/js_backend/codegen/glue.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698