/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.compiler.assembler;
import java.util.Arrays;
import java.util.BitSet;
/**
*
*/
public final class Variables {
private static final int INITIAL_SIZE = 8;
private final BitSet variables = new BitSet();
private final BitSet active = new BitSet();
private Type[] types = new Type[INITIAL_SIZE];
private VariableScope varScope = null;
private int varCount = 0;
private static Type[] grow(Type[] types) {
int newLength = types.length + (types.length >>> 1);
return Arrays.copyOf(types, newLength, Type[].class);
}
private void assign(int slot, Type type) {
if (type.getSize() == 1) {
types[slot] = type;
variables.set(slot);
} else {
assert type.getSize() == 2;
types[slot] = type;
types[slot + 1] = Type.RESERVED;
variables.set(slot, slot + 2);
}
}
/**
* Returns the type information for the requested local variable.
*
* @param slot
* the variable index slot
* @return the variable type
*/
Type getVariable(int slot) {
assert 0 <= slot && slot <= types.length : String.format("slot=%d not in [%d, %d]", slot, 0, types.length);
assert variables.get(slot) && types[slot] != null && types[slot] != Type.RESERVED : String
.format("slot=%d, used=%b, type=%s", slot, variables.get(slot), types[slot]);
return types[slot];
}
VariablesSnapshot snapshot() {
return snapshot(0);
}
VariablesSnapshot snapshot(int startSlot) {
BitSet vars = (BitSet) variables.clone();
vars.clear(0, startSlot);
BitSet actv = (BitSet) active.clone();
actv.clear(0, startSlot);
Type[] typs = new Type[types.length];
System.arraycopy(types, startSlot, typs, startSlot, types.length - startSlot);
return new VariablesSnapshot(startSlot, vars, actv, typs);
}
void restore(VariablesSnapshot snapshot) {
int start = snapshot.getStartSlot();
variables.clear(start, variables.length());
variables.or(snapshot.getVariables());
active.clear(start, active.length());
active.or(snapshot.getActive());
Type[] savedTypes = snapshot.getTypes();
Type[] newTypes = new Type[savedTypes.length];
System.arraycopy(types, 0, newTypes, 0, Math.min(start, types.length));
System.arraycopy(savedTypes, start, newTypes, start, savedTypes.length - start);
types = newTypes;
}
VariableScope enter() {
return varScope = new VariableScope(varScope, variables.length());
}
VariableScope exit() {
VariableScope scope = varScope;
varScope = scope.parent;
// clear stored variable type info
for (Variable<?> v : scope) {
v.free();
}
Arrays.fill(types, scope.firstSlot, types.length, null);
variables.clear(scope.firstSlot, variables.size());
active.clear(scope.firstSlot, active.size());
return scope;
}
void close() {
assert varScope == null : "unclosed variable scopes";
}
void activate(int slot) {
active.set(slot);
}
boolean isActive(int slot) {
return active.get(slot);
}
/**
* Adds a new entry to the variable map.
*
* @param <T>
* the variable class type
* @param name
* the variable name
* @param type
* the variable type
* @param slot
* the variable slot
* @return the new variable object
*/
<T> Variable<T> addVariable(String name, Type type, int slot) {
assert variables.get(slot) && types[slot].equals(type);
return varScope.add(name, type, slot);
}
/**
* Sets the variable information for a fixed slot.
*
* @param type
* the variable type
* @param slot
* the variable slot
*/
void reserveSlot(Type type, int slot) {
assert slot >= 0;
final int size = type.getSize();
assert size == 1 || size == 2;
if (slot + size > types.length) {
types = grow(types);
}
assert types[slot] == null && (size == 1 || types[slot + 1] == null);
assign(slot, type);
activate(slot);
}
/**
* Sets the variable information for a fixed slot and adds an entry to the variable map.
*
* @param <T>
* the variable class type
* @param name
* the variable name
* @param type
* the variable type
* @param slot
* the variable slot
* @return the variable object
*/
<T> Variable<T> reserveSlot(String name, Type type, int slot) {
reserveSlot(type, slot);
return varScope.add(name, type, slot);
}
/**
* Creates a new variable and adds it to the variable map.
*
* @param <T>
* the variable class type
* @param name
* the variable name
* @param type
* the variable type
* @return the new variable object
*/
<T> Variable<T> newVariable(String name, Type type) {
int slot = newVariable(type);
if (slot < 0) {
// reusing an existing slot
return varScope.add("<shared>", type, slot);
}
if (name == null) {
// null-name indicates scratch variable, don't create variable map entry
return varScope.add("<scratch>", type, -slot);
}
String uniqueName = name + "$" + varCount++;
return varScope.add(uniqueName, type, slot);
}
/**
* Marks the given variable as no longer being used, only applicable for scratch variables.
*
* @param variable
* the variable to be freed
*/
void freeVariable(Variable<?> variable) {
int slot = variable.getSlot();
assert variable.isAlive();
assert "<scratch>".equals(variable.getName()) || "<shared>".equals(variable.getName());
assert variables.get(slot);
assert types[slot].equals(variable.getType());
variables.clear(slot);
active.clear(slot);
variable.free();
}
private int newVariable(Type type) {
final int size = type.getSize();
assert size == 1 || size == 2;
for (int slot = varScope.firstSlot;;) {
slot = variables.nextClearBit(slot);
if (slot + size > types.length) {
types = grow(types);
}
Type old = types[slot];
if (old == null) {
if (size == 1 || types[slot + 1] == null) {
assign(slot, type);
return slot;
}
} else if (old.equals(type)) {
assert size == 1 || types[slot + 1] == Type.RESERVED;
assign(slot, type);
return -slot;
}
// try next index
slot += 1;
}
}
}