/*
* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.tele.interpreter;
import java.lang.reflect.*;
import java.util.*;
import com.sun.max.tele.*;
import com.sun.max.tele.object.*;
import com.sun.max.tele.reference.*;
import com.sun.max.tele.util.*;
import com.sun.max.tele.value.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.classfile.constant.*;
import com.sun.max.vm.layout.*;
import com.sun.max.vm.reference.*;
import com.sun.max.vm.type.*;
import com.sun.max.vm.value.*;
/**
* The Interpreter's interface to the VM. Encapsulates all the state of the VM.
* Can run without VM for testing.
*/
public final class Machine extends AbstractVmHolder {
private ExecutionThread currentThread;
Machine(TeleVM vm) {
super(vm);
final ExecutionThread mainThread = newThread(java.lang.Thread.NORM_PRIORITY, ExecutionThread.ThreadType.NORMAL_THREAD);
//JavaThreads.initialize(mainThread);
activate(mainThread);
}
public ReferenceValue toReferenceValue(RemoteReference reference) {
if (vm() == null) {
return ObjectReferenceValue.from(reference.toJava());
} else {
return referenceManager().createReferenceValue(reference);
}
}
public ExecutionFrame pushFrame(ClassMethodActor method) {
return currentThread.pushFrame(method);
}
public ExecutionFrame popFrame() {
return currentThread.popFrame();
}
public void activate(ExecutionThread thread) {
//active_threads.insertElementAt(this_thread, 0);
currentThread = thread;
}
public ExecutionThread newThread(int prio, ExecutionThread.ThreadType threadType) {
return new ExecutionThread(prio, threadType);
}
public MethodActor currentMethod() {
return currentThread.frame().method();
}
public ExecutionThread currentThread() {
return currentThread;
}
public void jump(int offset) {
currentThread.frame().jump(offset);
}
public int readOpcode() {
return currentThread.frame().readOpcode();
}
public void setLocal(int index, Value value) {
currentThread.frame().setLocal(index, value);
}
public Value getLocal(int index) {
return currentThread.frame().getLocal(index);
}
public void push(Value value) {
currentThread.frame().stack().push(value);
}
public Value pop() {
return currentThread.frame().stack().pop();
}
public Value peek() {
return currentThread.frame().stack().peek();
}
public Value peek(int n) {
final Stack<Value> operands = currentThread.frame().stack();
return operands.elementAt(operands.size() - n);
}
public byte readByte() {
return currentThread.frame().readByte();
}
public short readShort() {
return currentThread.frame().readShort();
}
public int readInt() {
return currentThread.frame().readInt();
}
public void skipBytes(int n) {
currentThread.frame().skipBytes(n);
}
public void alignInstructionPosition() {
currentThread.frame().alignInstructionPosition();
}
public Value widenIfNecessary(Value value) {
if (value.kind().stackKind == Kind.INT) {
return IntValue.from(value.toInt());
}
return value;
}
public Value resolveConstantReference(int cpIndex) {
Value constant = currentThread.frame().constantPool().valueAt(cpIndex);
if (constant instanceof ObjectReferenceValue) {
constant = toReferenceValue((RemoteReference) Reference.fromJava(constant.unboxObject()));
}
return widenIfNecessary(constant);
}
/**
* Converts a given reference to an object to a {@link Throwable} instance.
*
* @param vm the VM to be used if {@code throwableReference} is a reference in a VM's address space
* @param throwableReference the reference to be converted to a {@code Throwable instance}
* @return a {@code Throwable instance} converted from {@code throwableReference}
*/
private static Throwable toThrowable(TeleVM vm, ReferenceValue throwableReference) {
if (throwableReference instanceof TeleReferenceValue) {
try {
return (Throwable) vm.objects().makeTeleObject((RemoteReference) throwableReference.asReference()).deepCopy();
} catch (Exception e1) {
throw TeleError.unexpected("Could not make a local copy of a remote Throwable", e1);
}
} else {
return (Throwable) throwableReference.asBoxedJavaValue();
}
}
public TeleInterpreterException raiseException(ReferenceValue throwableReference) throws TeleInterpreterException {
throw new TeleInterpreterException(toThrowable(vm(), throwableReference), this);
}
public TeleInterpreterException raiseException(Throwable throwable) throws TeleInterpreterException {
throw new TeleInterpreterException(throwable, this);
}
/**
* Looks for an exception handler in the current execution scope that handles a given exception. If one is found,
* the execution context is adjusted appropriately so that execution will resume at the discovered handler.
* If no handler is found, the execution context is not modified.
*
* @param throwableReference the reference value representing the exception to be handled
* @return {@code true} if an appropriate exception handler was found, {@code false} otherwise
*/
public boolean handleException(ReferenceValue throwableReference) {
if (currentThread.handleException(throwableReference.getClassActor())) {
push(throwableReference);
return true;
}
return false;
}
public int depth() {
return currentThread.frame().stack().size();
}
public Value getStatic(int cpIndex) {
final ConstantPool constantPool = currentThread.frame().constantPool();
final FieldRefConstant fieldRef = constantPool.fieldAt(cpIndex);
if (vm() != null) {
final FieldActor fieldActor = fieldRef.resolve(constantPool, cpIndex);
final TeleClassActor teleClassActor = classes().findTeleClassActor(fieldActor.holder().typeDescriptor);
final TeleStaticTuple teleStaticTuple = teleClassActor.getTeleStaticTuple();
final RemoteReference staticTupleReference = teleStaticTuple.reference();
switch (fieldActor.kind.asEnum) {
case BOOLEAN:
case BYTE:
case CHAR:
case SHORT:
case INT: {
final int intValue = fieldActor.kind.readValue(staticTupleReference, fieldActor.offset()).toInt();
return IntValue.from(intValue);
}
case FLOAT: {
return FloatValue.from(staticTupleReference.readFloat(fieldActor.offset()));
}
case LONG: {
return LongValue.from(staticTupleReference.readLong(fieldActor.offset()));
}
case DOUBLE: {
return DoubleValue.from(staticTupleReference.readDouble(fieldActor.offset()));
}
case WORD: {
return new WordValue(staticTupleReference.readWord(fieldActor.offset()));
}
case REFERENCE: {
final RemoteReference reference = staticTupleReference.readFieldAsRemoteReference(fieldActor);
return referenceManager().createReferenceValue(reference);
}
}
} else {
final FieldActor fieldActor = fieldRef.resolve(constantPool, cpIndex);
return widenIfNecessary(fieldActor.readValue(Reference.fromJava(fieldActor.holder().staticTuple())));
}
return null;
}
public void putStatic(int cpIndex, Value value) {
if (vm() != null) {
TeleError.unexpected("Cannot run putstatic remotely!");
} else {
final ConstantPool cp = currentThread.frame().constantPool();
final FieldActor fieldActor = cp.fieldAt(cpIndex).resolve(cp, cpIndex);
fieldActor.writeValue(fieldActor.holder().staticTuple(), fieldActor.kind.convert(value));
}
}
public Value getField(RemoteReference instanceRef, int cpIndex) throws TeleInterpreterException {
if (instanceRef.isZero()) {
raiseException(new NullPointerException());
}
final ConstantPool constantPool = currentThread.frame().constantPool();
final FieldRefConstant fieldRef = constantPool.fieldAt(cpIndex);
final FieldActor fieldActor = fieldRef.resolve(constantPool, cpIndex);
final Kind kind = fieldActor.kind;
if (kind.isExtendedPrimitiveValue()) {
return widenIfNecessary(fieldActor.readValue(instanceRef));
} else {
assert kind.isReference;
if (!instanceRef.isLocal()) {
final RemoteReference reference = instanceRef.readFieldAsRemoteReference(fieldActor);
return referenceManager().createReferenceValue(reference);
} else {
return fieldActor.readValue(instanceRef);
}
}
}
public void putField(Object instance, int cpIndex, Value value) {
if (instance instanceof RemoteReference && !((RemoteReference) instance).isLocal()) {
TeleError.unexpected("Cannot run putfield remotely!");
} else {
final ConstantPool cp = currentThread.frame().constantPool();
final FieldActor fieldActor = cp.fieldAt(cpIndex).resolve(cp, cpIndex);
if (value instanceof TeleReferenceValue) {
fieldActor.writeValue(instance, TeleReferenceValue.from(vm(), makeLocalReference((RemoteReference) value.asReference())));
} else {
final Value val = fieldActor.kind.convert(value);
fieldActor.writeValue(instance, val);
}
}
}
public MethodActor resolveMethod(int cpIndex) {
final ConstantPool cp = currentThread.frame().constantPool();
final MethodRefConstant methodRef = cp.methodAt(cpIndex);
return methodRef.resolve(cp, cpIndex);
}
private Object readRemoteArray(RemoteReference remoteArray, int length, TypeDescriptor type) {
Object localArray = null;
// TODO: this could probably ask the kind to perform the operation
if (type == JavaTypeDescriptor.BOOLEAN) {
final boolean[] array = new boolean[length];
for (int i = 0; i < length; i++) {
array[i] = Layout.getBoolean(remoteArray, i);
}
localArray = array;
} else if (type == JavaTypeDescriptor.BYTE) {
final byte[] array = new byte[length];
for (int i = 0; i < length; i++) {
array[i] = Layout.getByte(remoteArray, i);
}
localArray = array;
} else if (type == JavaTypeDescriptor.CHAR) {
final char[] array = new char[length];
for (int i = 0; i < length; i++) {
array[i] = Layout.getChar(remoteArray, i);
}
localArray = array;
} else if (type == JavaTypeDescriptor.DOUBLE) {
final double[] array = new double[length];
for (int i = 0; i < length; i++) {
array[i] = Layout.getDouble(remoteArray, i);
}
localArray = array;
} else if (type == JavaTypeDescriptor.FLOAT) {
final float[] array = new float[length];
for (int i = 0; i < length; i++) {
array[i] = Layout.getFloat(remoteArray, i);
}
localArray = array;
} else if (type == JavaTypeDescriptor.INT) {
final int[] array = new int[length];
for (int i = 0; i < length; i++) {
array[i] = Layout.getInt(remoteArray, i);
}
localArray = array;
} else if (type == JavaTypeDescriptor.LONG) {
final long[] array = new long[length];
for (int i = 0; i < length; i++) {
array[i] = Layout.getLong(remoteArray, i);
}
localArray = array;
} else if (type == JavaTypeDescriptor.SHORT) {
final short[] array = new short[length];
for (int i = 0; i < length; i++) {
array[i] = Layout.getShort(remoteArray, i);
}
localArray = array;
} else {
TeleError.unexpected("readRemoteArray called without a primitive array type");
}
return localArray;
}
Reference makeLocalReference(RemoteReference remoteReference) {
if (remoteReference.isLocal()) {
return remoteReference;
}
final ClassActor remoteReferenceClassActor = classes().makeClassActorForTypeOf(remoteReference);
if (remoteReferenceClassActor.typeDescriptor.equals(JavaTypeDescriptor.STRING)) {
return Reference.fromJava(vm().getString(remoteReference));
} else if (remoteReferenceClassActor.isArrayClass() && remoteReferenceClassActor.componentClassActor().isPrimitiveClassActor()) {
final int arrayLength = Layout.readArrayLength(remoteReference);
return Reference.fromJava(readRemoteArray(remoteReference, arrayLength, remoteReferenceClassActor.componentClassActor().typeDescriptor));
} else {
//should put some tracing error message here
return remoteReference;
}
}
private void invertOperands(Stack<Value> argumentStack, Value[] arguments) {
for (int i = 0; i < arguments.length; i++) {
arguments[i] = argumentStack.pop();
if (arguments[i] instanceof TeleReferenceValue) {
final TeleReferenceValue inspectorReferenceArgument = (TeleReferenceValue) arguments[i];
final RemoteReference reference = inspectorReferenceArgument.asReference();
if (!reference.isLocal()) {
arguments[i] = TeleReferenceValue.from(vm(), makeLocalReference(reference));
}
}
}
}
public void invokeMethod(ClassMethodActor method) throws TeleInterpreterException {
final ExecutionFrame oldFrame = currentThread.frame();
final Stack<Value> argumentStack = new Stack<Value>();
final Stack<Value> oldOperands = oldFrame.stack();
int numberOfParameters = method.descriptor().numberOfParameters();
int i;
if (!method.isStatic()) {
numberOfParameters++;
}
//inverting the operands
for (i = 0; i < numberOfParameters; i++) {
argumentStack.push(oldOperands.pop());
}
if (method.isNative()) {
final Value[] arguments = new Value[numberOfParameters];
invertOperands(argumentStack, arguments);
try {
push(widenIfNecessary(method.invoke(arguments)));
} catch (InvocationTargetException e) {
throw new TeleInterpreterException(e.getCause(), this);
} catch (IllegalAccessException e) {
throw new TeleInterpreterException(e, this);
}
} else if (method.codeAttribute() == null || Word.class.isAssignableFrom(method.holder().toJava())) {
final Value[] arguments = new Value[numberOfParameters];
invertOperands(argumentStack, arguments);
try {
Value result = method.invoke(arguments);
if (result.kind().isReference) {
result = toReferenceValue((RemoteReference) Reference.fromJava(result.asObject()));
}
push(widenIfNecessary(result));
} catch (InvocationTargetException e) {
throw new TeleInterpreterException(e.getCause(), this);
} catch (IllegalAccessException e) {
throw new TeleInterpreterException(e, this);
}
} else {
final ExecutionFrame newFrame = currentThread.pushFrame(method);
i = 0;
while (i < numberOfParameters) {
final Value argument = argumentStack.pop();
newFrame.setLocal(i, argument);
if (argument instanceof DoubleValue || argument instanceof LongValue) {
i++;
numberOfParameters++;
}
i++;
}
}
}
public ClassActor resolveClassReference(int constantPoolIndex) {
final ConstantPool constantPool = currentThread.frame().constantPool();
return constantPool.classAt(constantPoolIndex).resolve(constantPool, constantPoolIndex);
}
}