blob: 9a3a2b51ba28469a6b18548c508702d3f7cda0be [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.debug.eval.ast.engine;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.jdt.debug.core.IJavaObject;
import org.eclipse.jdt.debug.core.IJavaType;
import org.eclipse.jdt.debug.core.IJavaValue;
import org.eclipse.jdt.debug.core.IJavaVariable;
import org.eclipse.jdt.debug.core.JDIDebugModel;
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
import org.eclipse.jdt.internal.debug.eval.ast.instructions.Instruction;
import org.eclipse.jdt.internal.debug.eval.ast.instructions.InstructionSequence;
import com.sun.jdi.VMDisconnectedException;
public class Interpreter {
private Instruction[] fInstructions;
private int fInstructionCounter;
private IRuntimeContext fContext;
private Stack<Object> fStack;
private IJavaValue fLastValue;
/**
* The list of internal variables
*/
private Map<String, IVariable> fInternalVariables;
/**
* List of objects for which GC has been disabled
*/
private List<IJavaObject> fPermStorage = null;
private boolean fStopped = false;
public Interpreter(InstructionSequence instructions, IRuntimeContext context) {
fInstructions = instructions.getInstructions();
fContext = context;
fInternalVariables = new HashMap<>();
}
public void execute(boolean disableGcOnResult) throws CoreException {
try {
reset();
while (fInstructionCounter < fInstructions.length && !fStopped) {
Instruction instruction = fInstructions[fInstructionCounter++];
instruction.setInterpreter(this);
instruction.execute();
instruction.setInterpreter(null);
}
} catch (CoreException e) {
throw e;
} catch (Exception e) {
throw new CoreException(new Status(IStatus.ERROR,
JDIDebugModel.getPluginIdentifier(), e.getMessage(), e));
} finally {
releaseObjects(disableGcOnResult);
}
}
public void stop() {
fStopped = true;
}
private void reset() {
fStack = new Stack<>();
fInstructionCounter = 0;
}
/**
* Jumps to a given address
*/
public void jump(int offset) {
fInstructionCounter += offset;
}
/**
* Pushes an object onto the stack. Disables garbage collection for any
* interim object pushed onto the stack. Objects are released after the
* evaluation completes.
*/
public void push(Object object) {
fStack.push(object);
if (object instanceof IJavaObject) {
disableCollection((IJavaObject) object);
}
}
/**
* Avoid garbage collecting interim results.
*
* @param value
* object to disable garbage collection for
*/
private void disableCollection(IJavaObject value) {
if (fPermStorage == null) {
fPermStorage = new ArrayList<>(5);
}
try {
value.disableCollection();
fPermStorage.add(value);
} catch (CoreException e) {
// don't worry about GC if the VM has terminated
if ((e.getStatus().getException() instanceof VMDisconnectedException)) {
return;
}
JDIDebugPlugin.log(e);
}
}
/**
* Re-enable garbage collection if interim results.
*/
private void releaseObjects(boolean disableGcOnResult) {
if (fPermStorage != null) {
IJavaValue result = getResult();
Iterator<IJavaObject> iterator = fPermStorage.iterator();
while (iterator.hasNext()) {
IJavaObject object = iterator.next();
try {
if (!disableGcOnResult || object != result) {
object.enableCollection();
}
} catch (CoreException e) {
// don't worry about GC if the VM has terminated
if ((e.getStatus().getException() instanceof VMDisconnectedException)) {
break;
}
JDIDebugPlugin.log(e);
}
}
fPermStorage = null;
}
}
/**
* Peeks at the top object of the stack
*/
public Object peek() {
return fStack.peek();
}
/**
* Pops an object off of the stack
*/
public Object pop() {
return fStack.pop();
}
/**
* Answers the context for the interpreter
*/
public IRuntimeContext getContext() {
return fContext;
}
public IJavaValue getResult() {
if (fStack == null || fStack.isEmpty()) {
if (fLastValue == null) {
return getContext().getVM().voidValue();
}
return fLastValue;
}
Object top = fStack.peek();
if (top instanceof IJavaVariable) {
try {
return (IJavaValue) ((IJavaVariable) top).getValue();
} catch (CoreException exception) {
return getContext().getVM().newValue(
exception.getStatus().getMessage());
}
}
if (top instanceof IJavaValue) {
return (IJavaValue) top;
}
// XXX: exception
return null;
}
public void setLastValue(IJavaValue value) {
fLastValue = value;
}
/**
* Create a new variable in the interpreter with the given name and the
* given type.
*
* @param name
* the name of the variable to create.
* @param type
* the type of the variable to create.
* @return the created variable.
*/
public IVariable createInternalVariable(String name, IJavaType referencType) {
IVariable var = new InterpreterVariable(name, referencType,
fContext.getVM());
fInternalVariables.put(name, var);
return var;
}
/**
* Return the variable with the given name. This method only looks in the
* list of internal variable (i.e. created by
* Interpreter#createInternalVariable(String, IJavaType))
*
* @param name
* the name of the variable to retrieve.
* @return the corresponding variable, or <code>null</code> if there is
* none.
*/
public IVariable getInternalVariable(String name) {
return fInternalVariables.get(name);
}
}