/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.mmtk.harness.lang.runtime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.mmtk.harness.lang.Declaration;
import org.mmtk.harness.lang.Trace;
import org.mmtk.harness.lang.Trace.Item;
import org.mmtk.harness.lang.pcode.PseudoOp;
import org.mmtk.harness.lang.type.Type;
import org.mmtk.harness.vm.ObjectModel;
import org.mmtk.harness.vm.ReferenceProcessor;
import org.mmtk.plan.TraceLocal;
import org.vmmagic.unboxed.ObjectReference;
/**
* A stack frame. Currently assumes each slot contains exactly one
* variable, and that all variables are live all the time.
*/
public class StackFrame {
/**
* Enable the assertion that objects won't move after being traced.
* This is notably not true for MC, but can be useful for debugging other
* collectors.
*/
public static final boolean ASSERT_WILL_NOT_MOVE = false;
/** A sentinel for slots that have no value */
public static final int NO_SUCH_SLOT = Integer.MAX_VALUE;
/** The values of variables and temporaries */
private final Value[] values;
/** (for debugging) the names of the value slots */
private String[] names = null;
/** The saved program counter during a method call */
private int savedPc;
/** The saved instruction array for a method call */
private PseudoOp[] savedCode;
/** The slot for the return value of a method call */
private int resultSlot = NO_SUCH_SLOT;
/**
* Create a stack frame, given a list of declarations and a quantity of temporaries
* @param decls Variables declared in this stack frame
* @param nTemp Number of temporaries
*/
public StackFrame(List<Declaration> decls, int nTemp) {
int size = decls.size()+nTemp;
this.values = new Value[size];
if (Trace.isEnabled(Item.ENV) || Trace.isEnabled(Item.ROOTS)) {
this.names = new String[size];
for (Declaration d : decls) {
declare(d);
}
for (int i=decls.size(); i < size; i++) {
names[i] = "t"+i;
}
}
}
/**
* Declare a variable in a given slot. Only used when tracing.
* @param d The variable declaration
*/
public void declare(Declaration d) {
values[d.getSlot()] = d.getInitial();
names[d.getSlot()] = d.getName();
}
/**
* Return the variable at the given slot in the current stack frame
* @param slot The stack frame slot
* @return The value in the slot
*/
public Value get(int slot) {
if (slot >= 0) {
return values[slot];
}
return ConstantPool.get(slot);
}
/**
* Return the type of the variable at the given slot.
* @param slot The stack frame slot
* @return The type of the value in the slot
*/
public Type getType(int slot) {
return values[slot].type();
}
/**
* Assign a new value to the given slot
* @param slot Stack-frame slot to modify
* @param value New value
*/
public void set(int slot, Value value) {
assert value != null : "Unexpected null value";
if (Trace.isEnabled(Item.EVAL)) {
Trace.printf(Item.EVAL, "%s %s = %s",value.type().toString(),getSlotName(slot),value.toString());
}
values[slot] = value;
}
private String getSlotName(int slot) {
if (names != null && names[slot] != null) {
return names[slot];
}
return "t" + slot;
}
/**
* GC support: trace this stack frame.
* @param trace The MMTk trace object to receive the roots
* @return The number of roots found
*/
public int computeRoots(TraceLocal trace) {
int rootCount = 0;
for (ObjectValue object : getRoots()) {
if (!object.getObjectValue().isNull()) {
if (Trace.isEnabled(Item.ROOTS)) {
Trace.trace(Item.ROOTS, "Tracing root %s", ObjectModel.getString(object.getObjectValue()));
}
object.traceObject(trace);
if (ASSERT_WILL_NOT_MOVE) {
assert trace.willNotMoveInCurrentCollection(object.getObjectValue()) :
object.getObjectValue()+" has been traced but willNotMoveInCurrentCollection is still false";
}
if (Trace.isEnabled(Item.ROOTS)) {
Trace.trace(Item.ROOTS, "new value of %s", ObjectModel.getString(object.getObjectValue()));
}
// We would like to assert the sanity of the new root value, but can't in collectors
// like MC that update roots before actually moving the objects.
rootCount++;
}
}
Trace.trace(Item.REFERENCES, "Discovering references");
for (ReferenceValue reference : getReferences()) {
ReferenceProcessor.discover(reference);
}
return rootCount;
}
/**
*
* @return The root ObjectValues for this stack frame
*/
public Collection<ObjectValue> getRoots() {
List<ObjectValue> roots = new ArrayList<ObjectValue>();
for (Value value : values) {
if (value != null && value instanceof ObjectValue) {
roots.add((ObjectValue)value);
}
}
return roots;
}
/**
*
* @return The root ReferenceValues for this stack frame
*/
private Collection<ReferenceValue> getReferences() {
List<ReferenceValue> roots = new ArrayList<ReferenceValue>();
for (Value value : values) {
if (value != null && value instanceof ReferenceValue) {
roots.add((ReferenceValue)value);
}
}
return roots;
}
/**
* Debug printing support: dump this stack frame and return roots.
* @param width Output field width
* @return The collection of roots in this frame
*/
public Collection<ObjectReference> dumpRoots(int width) {
List<ObjectReference> roots = new ArrayList<ObjectReference>();
for (int i=0; i < values.length; i++) {
Value value = values[i];
String name;
if (Trace.isEnabled(Item.ROOTS)) {
name = names != null && i < names.length ? getSlotName(i) : "t"+i;
} else {
name = "slot["+i+"]";
}
if (value != null && value instanceof ObjectValue) {
ObjectReference ref = ((ObjectValue)value).getObjectValue();
System.err.printf(" %s=%s", name, ObjectModel.formatObject(width, ref));
if (!ref.isNull()) roots.add(ref);
}
}
return roots;
}
/**
* Save a program counter value
* @param pc The program counter
*/
public void savePc(int pc) {
savedPc = pc;
}
/** @return the saved program counter */
public int getSavedPc() {
return savedPc;
}
/**
* Save a method code array
* @param code The code array
*/
public void saveMethod(PseudoOp[] code) {
savedCode = code;
}
/**
* @return the saved code array
*/
public PseudoOp[] getSavedMethod() {
return savedCode;
}
/**
* Set the slot for the return value
* @param slot The slot in which to store the return value
*/
public void setResultSlot(int slot) {
resultSlot = slot;
}
/**
* Clear the return value slot
*/
public void clearResultSlot() {
resultSlot = NO_SUCH_SLOT;
}
/**
* Set the return value
* @param returnValue The procedure return value
*/
public void setResult(Value returnValue) {
assert resultSlot != NO_SUCH_SLOT : "Attempt to return a value to a method call with no result slot";
set(resultSlot,returnValue);
}
}