| // Copyright (c) 2018, 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 vm.bytecode.local_vars; |
| |
| import 'dart:math' show max; |
| |
| import 'package:kernel/ast.dart'; |
| import 'package:vm/bytecode/dbc.dart'; |
| |
| class LocalVariables extends RecursiveVisitor<Null> { |
| final Map<VariableDeclaration, int> _vars = <VariableDeclaration, int>{}; |
| final Map<TreeNode, int> _temps = <TreeNode, int>{}; |
| final List<int> _scopes = <int>[]; |
| int _localVars = 0; |
| int _frameSize = 0; |
| int _numParameters = 0; |
| bool _hasOptionalParameters = false; |
| int _thisVarIndex; |
| int _functionTypeArgsVarIndex; |
| |
| int get thisVarIndex => |
| _thisVarIndex ?? (throw '\'this\' variable is not allocated'); |
| |
| int get functionTypeArgsVarIndex => |
| _functionTypeArgsVarIndex ?? |
| (throw '\'functionTypeArgs\' variable is not allocated'); |
| |
| int get frameSize => _frameSize; |
| |
| int get numParameters => _numParameters; |
| |
| bool get hasOptionalParameters => _hasOptionalParameters; |
| |
| int varIndex(VariableDeclaration variable) => |
| _vars[variable] ?? (throw '\'$variable\' variable is not allocated'); |
| |
| int tempIndex(TreeNode node) => |
| _temps[node] ?? |
| (throw 'Temp is not allocated for node ${node.runtimeType} $node'); |
| |
| int _allocateVar(VariableDeclaration node, {int index}) { |
| if (index == null) { |
| index = _localVars++; |
| } else { |
| // Should be a parameter. |
| assert(index < 0 || (_hasOptionalParameters && index < _numParameters)); |
| } |
| _frameSize = max(_frameSize, _localVars); |
| if (node != null) { |
| assert(_vars[node] == null); |
| _vars[node] = index; |
| } |
| return index; |
| } |
| |
| // TODO(alexmarkov): allocate temporaries more efficiently. |
| void _allocateTemp(TreeNode node) { |
| _temps[node] = _allocateVar(null); |
| } |
| |
| int _allocateParameter(VariableDeclaration node, int i) { |
| assert(0 <= i && i < _numParameters); |
| int paramSlotIndex = |
| _hasOptionalParameters ? i : -kParamEndSlotFromFp - _numParameters + i; |
| return _allocateVar(node, index: paramSlotIndex); |
| } |
| |
| void _enterScope() { |
| _scopes.add(_localVars); |
| } |
| |
| void _leaveScope() { |
| final int enclosingScopeLocalVars = _scopes.removeLast(); |
| assert(_localVars >= enclosingScopeLocalVars); |
| _localVars = enclosingScopeLocalVars; |
| } |
| |
| @override |
| visitField(Field node) { |
| if (node.initializer != null) { |
| assert(_vars.isEmpty); |
| assert(_localVars == 0); |
| |
| _enterScope(); |
| node.initializer.accept(this); |
| _leaveScope(); |
| |
| assert(_scopes.isEmpty); |
| } |
| } |
| |
| @override |
| defaultMember(Member node) { |
| assert(_vars.isEmpty); |
| assert(_localVars == 0); |
| |
| final function = node.function; |
| final bool hasTypeArgs = function.typeParameters.isNotEmpty; |
| final bool hasReceiver = |
| node is Constructor || ((node is Procedure) && !node.isStatic); |
| _numParameters = function.positionalParameters.length + |
| function.namedParameters.length + |
| (hasTypeArgs ? 1 : 0) + |
| (hasReceiver ? 1 : 0); |
| _hasOptionalParameters = function.requiredParameterCount < |
| function.positionalParameters.length || |
| function.namedParameters.isNotEmpty; |
| int count = 0; |
| |
| if (hasTypeArgs) { |
| _functionTypeArgsVarIndex = _allocateParameter(null, count++); |
| } |
| |
| if (hasReceiver) { |
| _thisVarIndex = _allocateParameter(null, count++); |
| } |
| |
| for (var param in function.positionalParameters) { |
| _allocateParameter(param, count++); |
| } |
| |
| List<VariableDeclaration> namedParams = function.namedParameters; |
| namedParams.sort((VariableDeclaration a, VariableDeclaration b) => |
| a.name.compareTo(b.name)); |
| for (var param in namedParams) { |
| _allocateParameter(param, count++); |
| } |
| |
| if (_hasOptionalParameters) { |
| _localVars = _numParameters; |
| _frameSize = _numParameters; |
| } |
| |
| _enterScope(); |
| if (node is Constructor) { |
| _enterScope(); |
| for (var field in node.enclosingClass.fields) { |
| if (!field.isStatic && field.initializer != null) { |
| field.initializer.accept(this); |
| } |
| } |
| visitList(node.initializers, this); |
| _leaveScope(); |
| } |
| function.body?.accept(this); |
| _leaveScope(); |
| |
| assert(_scopes.isEmpty); |
| } |
| |
| @override |
| visitBlock(Block node) { |
| _enterScope(); |
| node.visitChildren(this); |
| _leaveScope(); |
| } |
| |
| @override |
| visitVariableDeclaration(VariableDeclaration node) { |
| _allocateVar(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| visitLet(Let node) { |
| _enterScope(); |
| node.variable.accept(this); |
| node.body.accept(this); |
| _leaveScope(); |
| } |
| |
| // -------------- Allocation of temporaries -------------- |
| |
| @override |
| visitConstructorInvocation(ConstructorInvocation node) { |
| if (node.isConst) { |
| return; |
| } |
| _allocateTemp(node); |
| super.visitConstructorInvocation(node); |
| } |
| |
| @override |
| visitListLiteral(ListLiteral node) { |
| if (node.isConst) { |
| return; |
| } |
| _allocateTemp(node); |
| super.visitListLiteral(node); |
| } |
| |
| @override |
| visitMapLiteral(MapLiteral node) { |
| if (node.isConst) { |
| return; |
| } |
| _allocateTemp(node); |
| super.visitMapLiteral(node); |
| } |
| |
| @override |
| visitStringConcatenation(StringConcatenation node) { |
| _allocateTemp(node); |
| super.visitStringConcatenation(node); |
| } |
| |
| @override |
| visitConditionalExpression(ConditionalExpression node) { |
| _allocateTemp(node); |
| super.visitConditionalExpression(node); |
| } |
| |
| @override |
| visitLogicalExpression(LogicalExpression node) { |
| _allocateTemp(node); |
| super.visitLogicalExpression(node); |
| } |
| |
| @override |
| visitPropertySet(PropertySet node) { |
| _allocateTemp(node); |
| super.visitPropertySet(node); |
| } |
| |
| @override |
| visitForInStatement(ForInStatement node) { |
| _allocateTemp(node); |
| super.visitForInStatement(node); |
| } |
| |
| @override |
| visitSwitchStatement(SwitchStatement node) { |
| _allocateTemp(node); |
| super.visitSwitchStatement(node); |
| } |
| } |