| Index: pkg/compiler/lib/src/js_model/locals.dart
|
| diff --git a/pkg/compiler/lib/src/js_model/locals.dart b/pkg/compiler/lib/src/js_model/locals.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a79037a6dafd542530dc1c04c95a039af44e85f7
|
| --- /dev/null
|
| +++ b/pkg/compiler/lib/src/js_model/locals.dart
|
| @@ -0,0 +1,278 @@
|
| +// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +library dart2js.js_model.locals;
|
| +
|
| +import 'package:kernel/ast.dart' as ir;
|
| +
|
| +import '../closure.dart';
|
| +import '../common.dart';
|
| +import '../elements/entities.dart';
|
| +import '../elements/jumps.dart';
|
| +import '../kernel/element_map.dart';
|
| +
|
| +class GlobalLocalsMap {
|
| + Map<MemberEntity, KernelToLocalsMap> _localsMaps =
|
| + <MemberEntity, KernelToLocalsMap>{};
|
| +
|
| + KernelToLocalsMap getLocalsMap(MemberEntity member) {
|
| + return _localsMaps.putIfAbsent(
|
| + member, () => new KernelToLocalsMapImpl(member));
|
| + }
|
| +}
|
| +
|
| +class KernelToLocalsMapImpl implements KernelToLocalsMap {
|
| + final List<MemberEntity> _members = <MemberEntity>[];
|
| + Map<ir.VariableDeclaration, JLocal> _map = <ir.VariableDeclaration, JLocal>{};
|
| + Map<ir.TreeNode, JJumpTarget> _jumpTargetMap;
|
| + Set<ir.BreakStatement> _breaksAsContinue;
|
| +
|
| + MemberEntity get currentMember => _members.last;
|
| +
|
| + // TODO(johnniwinther): Compute this eagerly from the root of the member.
|
| + void _ensureJumpMap(ir.TreeNode node) {
|
| + if (_jumpTargetMap == null) {
|
| + JumpVisitor visitor = new JumpVisitor(currentMember);
|
| +
|
| + // Find the root node for the current member.
|
| + while (node is! ir.Member) {
|
| + node = node.parent;
|
| + }
|
| +
|
| + node.accept(visitor);
|
| + _jumpTargetMap = visitor.jumpTargetMap;
|
| + _breaksAsContinue = visitor.breaksAsContinue;
|
| + }
|
| + }
|
| +
|
| + KernelToLocalsMapImpl(MemberEntity member) {
|
| + _members.add(member);
|
| + }
|
| +
|
| + @override
|
| + void enterInlinedMember(MemberEntity member) {
|
| + _members.add(member);
|
| + }
|
| +
|
| + @override
|
| + void leaveInlinedMember(MemberEntity member) {
|
| + assert(member == currentMember);
|
| + _members.removeLast();
|
| + }
|
| +
|
| + @override
|
| + JumpTarget getJumpTargetForBreak(ir.BreakStatement node) {
|
| + _ensureJumpMap(node.target);
|
| + JumpTarget target = _jumpTargetMap[node];
|
| + assert(target != null, failedAt(currentMember, 'No target for $node.'));
|
| + return target;
|
| + }
|
| +
|
| + @override
|
| + bool generateContinueForBreak(ir.BreakStatement node) {
|
| + return _breaksAsContinue.contains(node);
|
| + }
|
| +
|
| + @override
|
| + JumpTarget getJumpTargetForContinueSwitch(ir.ContinueSwitchStatement node) {
|
| + _ensureJumpMap(node.target);
|
| + throw new UnimplementedError(
|
| + 'KernelToLocalsMapImpl.getJumpTargetForContinueSwitch');
|
| + }
|
| +
|
| + @override
|
| + JumpTarget getJumpTargetForSwitchCase(ir.SwitchCase node) {
|
| + _ensureJumpMap(node);
|
| + throw new UnimplementedError(
|
| + 'KernelToLocalsMapImpl.getJumpTargetForSwitchCase');
|
| + }
|
| +
|
| + @override
|
| + JumpTarget getJumpTargetForDo(ir.DoStatement node) {
|
| + _ensureJumpMap(node);
|
| + return _jumpTargetMap[node.parent];
|
| + }
|
| +
|
| + @override
|
| + JumpTarget getJumpTargetForLabel(ir.LabeledStatement node) {
|
| + _ensureJumpMap(node);
|
| + return _jumpTargetMap[node];
|
| + }
|
| +
|
| + @override
|
| + JumpTarget getJumpTargetForSwitch(ir.SwitchStatement node) {
|
| + _ensureJumpMap(node);
|
| + throw new UnimplementedError(
|
| + 'KernelToLocalsMapImpl.getJumpTargetForSwitch');
|
| + }
|
| +
|
| + @override
|
| + JumpTarget getJumpTargetForFor(ir.ForStatement node) {
|
| + _ensureJumpMap(node);
|
| + return _jumpTargetMap[node];
|
| + }
|
| +
|
| + @override
|
| + JumpTarget getJumpTargetForForIn(ir.ForInStatement node) {
|
| + _ensureJumpMap(node);
|
| + return _jumpTargetMap[node];
|
| + }
|
| +
|
| + @override
|
| + JumpTarget getJumpTargetForWhile(ir.WhileStatement node) {
|
| + _ensureJumpMap(node);
|
| + return _jumpTargetMap[node];
|
| + }
|
| +
|
| + @override
|
| + Local getLocal(ir.VariableDeclaration node) {
|
| + return _map.putIfAbsent(node, () {
|
| + return new JLocal(node.name, currentMember);
|
| + });
|
| + }
|
| +
|
| + @override
|
| + LoopClosureScope getLoopClosureScope(
|
| + ClosureDataLookup closureLookup, ir.TreeNode node) {
|
| + return closureLookup.getLoopClosureScope(node);
|
| + }
|
| +}
|
| +
|
| +class JumpVisitor extends ir.Visitor {
|
| + int index = 0;
|
| + final MemberEntity member;
|
| + final Map<ir.TreeNode, JJumpTarget> jumpTargetMap =
|
| + <ir.TreeNode, JJumpTarget>{};
|
| + final Set<ir.BreakStatement> breaksAsContinue = new Set<ir.BreakStatement>();
|
| +
|
| + JumpVisitor(this.member);
|
| +
|
| + JJumpTarget _getJumpTarget(ir.TreeNode node) {
|
| + return jumpTargetMap.putIfAbsent(node, () {
|
| + return new JJumpTarget(member, index++);
|
| + });
|
| + }
|
| +
|
| + @override
|
| + defaultNode(ir.Node node) => node.visitChildren(this);
|
| +
|
| + bool _canBeBreakTarget(ir.TreeNode node) {
|
| + // TODO(johnniwinther): Add more.
|
| + return node is ir.ForStatement ||
|
| + node is ir.ForInStatement ||
|
| + node is ir.WhileStatement;
|
| + }
|
| +
|
| + bool _canBeContinueTarget(ir.TreeNode node) {
|
| + // TODO(johnniwinther): Add more.
|
| + return node is ir.ForStatement ||
|
| + node is ir.ForInStatement ||
|
| + node is ir.WhileStatement;
|
| + }
|
| +
|
| + @override
|
| + visitBreakStatement(ir.BreakStatement node) {
|
| + // TODO(johnniwinther): Add labels if the enclosing loop is not the implicit
|
| + // break target.
|
| + JJumpTarget target;
|
| + ir.TreeNode body = node.target.body;
|
| + ir.TreeNode parent = node.target.parent;
|
| + if (_canBeBreakTarget(body)) {
|
| + // We have code like
|
| + //
|
| + // l1: for (int i = 0; i < 10; i++) {
|
| + // break l1:
|
| + // }
|
| + //
|
| + // and can therefore use the for loop as the break target.
|
| + target = _getJumpTarget(body);
|
| + target.isBreakTarget = true;
|
| + } else if (_canBeContinueTarget(parent)) {
|
| + // We have code like
|
| + //
|
| + // for (int i = 0; i < 10; i++) l1: {
|
| + // break l1:
|
| + // }
|
| + //
|
| + // and can therefore use the for loop as a continue target.
|
| + target = _getJumpTarget(parent);
|
| + target.isContinueTarget = true;
|
| + breaksAsContinue.add(node);
|
| + } else {
|
| + target = _getJumpTarget(node.target);
|
| + target.isBreakTarget = true;
|
| + }
|
| + jumpTargetMap[node] = target;
|
| + super.visitBreakStatement(node);
|
| + }
|
| +}
|
| +
|
| +class JJumpTarget extends JumpTarget<ir.Node> {
|
| + final MemberEntity memberContext;
|
| + final int nestingLevel;
|
| +
|
| + JJumpTarget(this.memberContext, this.nestingLevel);
|
| +
|
| + bool isBreakTarget = false;
|
| + bool isContinueTarget = false;
|
| + bool isSwitch = false;
|
| +
|
| + @override
|
| + Entity get executableContext => memberContext;
|
| +
|
| + @override
|
| + LabelDefinition<ir.Node> addLabel(ir.Node label, String labelName,
|
| + {bool isBreakTarget: false}) {
|
| + throw new UnimplementedError('KJumpTarget.addLabel');
|
| + }
|
| +
|
| + @override
|
| + List<LabelDefinition<ir.Node>> get labels {
|
| + return const <LabelDefinition<ir.Node>>[];
|
| + }
|
| +
|
| + @override
|
| + ir.Node get statement {
|
| + throw new UnimplementedError('KJumpTarget.statement');
|
| + }
|
| +
|
| + String toString() {
|
| + StringBuffer sb = new StringBuffer();
|
| + sb.write('KJumpTarget[');
|
| + sb.write('memberContext=');
|
| + sb.write(memberContext);
|
| + sb.write(',nestingLevel=');
|
| + sb.write(nestingLevel);
|
| + sb.write(',isBreakTarget=');
|
| + sb.write(isBreakTarget);
|
| + sb.write(',isContinueTarget=');
|
| + sb.write(isContinueTarget);
|
| + sb.write(']');
|
| + return sb.toString();
|
| + }
|
| +}
|
| +
|
| +class JLocal implements Local {
|
| + final String name;
|
| + final MemberEntity memberContext;
|
| +
|
| + JLocal(this.name, this.memberContext);
|
| +
|
| + @override
|
| + Entity get executableContext => memberContext;
|
| +
|
| + String toString() {
|
| + StringBuffer sb = new StringBuffer();
|
| + sb.write('local(');
|
| + if (memberContext.enclosingClass != null) {
|
| + sb.write(memberContext.enclosingClass.name);
|
| + sb.write('.');
|
| + }
|
| + sb.write(memberContext.name);
|
| + sb.write('#');
|
| + sb.write(name);
|
| + sb.write(')');
|
| + return sb.toString();
|
| + }
|
| +}
|
|
|