blob: 93789ad549b1be29c54c8477a7114ca61f9ed893 [file] [log] [blame]
// 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);
}
@override
visitStaticSet(StaticSet node) {
_allocateTemp(node);
super.visitStaticSet(node);
}
}