blob: 1d0f17af1e8f6da015b31b005f2d2c41a9e2effc [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.spi.debugger.jpda;
import com.sun.jdi.ClassType;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.StackFrame;
import com.sun.jdi.Value;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.netbeans.api.debugger.jpda.CallStackFrame;
import org.netbeans.api.debugger.jpda.InvalidExpressionException;
import org.netbeans.api.debugger.jpda.ObjectVariable;
import org.netbeans.api.debugger.jpda.Variable;
import org.netbeans.spi.debugger.ContextProvider;
import org.openide.util.Lookup;
/**
* Evaluator service for a language that compiles into bytecode.
* Implementation class should register using the annotation {@link Registration} for the desired language.
*
* @author Martin Entlicher
*
* @since 2.21
*/
public interface Evaluator<PreprocessedInfo> {
/**
* Evaluates given expression and provide the result.
*
* @param expression the expression to be evaluated
* @param context the context in which the expression is evaluated
* @return value of evaluated expression
* @throws InvalidExpressionException when the expression is invalid or other
* error occurs during the evaluation process.
*/
Result evaluate(Expression<PreprocessedInfo> expression, Context context) throws InvalidExpressionException;
/**
* Representation of an expression that is a subject of evaluation.
* String expressions are evaluated. But this class allows to attach
* a custom preprocessed structure to the expression,
* which can be used on subsequent evaluations of the same expression.
* Clients can, but does not have to, set a pre-processed object and
* during repeated evaluations of the same expression use that
* pre-processed object to speed up the evaluation. The preprocessed
* object should not be {@link Context}-sensitive.
*/
public static final class Expression<PreprocessedInfo> {
private String expression;
private PreprocessedInfo preprocessed;
/**
* Creates a new expression from the string representation.
* @param expression The string expression.
*/
public Expression(String expression) {
this.expression = expression;
}
/**
* Get the string representation of this expression.
* @return string expression
*/
public String getExpression() {
return expression;
}
/**
* Set a pre-processed object of the string expression.
* A custom, client-specific object is expected, which represents
* the expression. This can be a syntax tree structure or whatever
* that speeds up repeated evaluations of the same expression.
*
* @param preprocessed object holding the information about pre-processed
* expression
*/
public void setPreprocessedObject(PreprocessedInfo preprocessed) {
this.preprocessed = preprocessed;
}
/**
* Get the pre-processed object of the string expression.
* The object set by {@link #setPreprocessedObject(java.lang.Object)} is
* returned.
*
* @return the preprocessed object or <code>null</code>.
*/
public PreprocessedInfo getPreprocessedObject() {
return preprocessed;
}
}
/**
* Context of the evaluation.
* This class provides the evaluation context - stack frame and context variable.
* Two sets of APIs can be used during the evaluation:
* <ul>
* <li>JPDA Debugger API (which is a safe abstraction of JDI,
* but with limited functionality)<br>
* Provided {@link CallStackFrame} and {@link ObjectVariable}
* can be used to compute the resulting {@link Variable}.
* </li>
* <li>JDI API (providing full access, but can throw unexpected exceptions
* and clients must notify the context about method invocations)<br>
* Provided {@link StackFrame} and {@link ObjectReference} can be used
* to compute the resulting {@link Value}. When a method invocation
* is necessary, {@link Context#notifyMethodToBeInvoked()} must be called
* before the method invocation.
* </li>
* </ul>
*/
public static final class Context {
private CallStackFrame callStackFrame;
private ObjectVariable contextVariable;
private StackFrame stackFrame;
private int stackDepth;
private ObjectReference contextObject;
private Runnable methodToBeInvokedNotifier;
/** Creates the context, do not call directly */
public Context(Lookup context) {
this.callStackFrame = context.lookup(CallStackFrame.class);
this.contextVariable = context.lookup(ObjectVariable.class);
this.stackFrame = context.lookup(StackFrame.class);
this.stackDepth = context.lookup(Integer.class);
this.contextObject = context.lookup(ObjectReference.class);
this.methodToBeInvokedNotifier = context.lookup(Runnable.class);
}
/**
* Get the context call stack frame.
* This frame corresonds to the JDI frame returned from {@link #getStackFrame()}.
* @return call stack frame in which the evaluation is performed
*/
public CallStackFrame getCallStackFrame() {
return callStackFrame;
}
/**
* Get an optional context variable. When non-null,
* all methods and fields should be treated relative to the variable
* instance.
* This variable corresonds to the JDI reference returned from {@link #getContextObject()}.
* @return optional context variable or <code>null</code>.
*/
public ObjectVariable getContextVariable() {
return contextVariable;
}
/**
* Get the context stack frame in JDI APIs.
* This frame corresonds to the JPDA frame returned from {@link #getCallStackFrame()}.
* @return stack frame in which the evaluation is performed
*/
public StackFrame getStackFrame() {
return stackFrame;
}
/**
* Get the depth of stack frame returned from {@link #getStackFrame()}.
* @return the depth of stack frame
*/
public int getStackDepth() {
return stackDepth;
}
/**
* Get an optional context object. When non-null,
* all methods and fields should be treated relative to the object
* instance.
* This object corresonds to the JPDA variable returned from {@link #getContextVariable()}.
* @return optional context object or <code>null</code>.
*/
public ObjectReference getContextObject() {
return contextObject;
}
/**
* This method is <b>required</b> to be called before a call to JDI
* that cause the current thread (<code>sf.thread()</code>) to resume - e.g.
* {@link ObjectReference#invokeMethod(com.sun.jdi.ThreadReference, com.sun.jdi.Method, java.util.List, int)},
* {@link ClassType#invokeMethod(com.sun.jdi.ThreadReference, com.sun.jdi.Method, java.util.List, int)},
* {@link ClassType#newInstance(com.sun.jdi.ThreadReference, com.sun.jdi.Method, java.util.List, int)}.
*/
public void notifyMethodToBeInvoked() {
methodToBeInvokedNotifier.run();
}
}
/**
* Evaluation result.
* Depending on the APIs used by the evaluation, result is either
* a {@link Variable} or {@link Value}.
*/
public static final class Result {
private Variable var;
private Value v;
/**
* Create result from {@link Variable}.
* @param var result variable
*/
public Result(Variable var) {
this.var = var;
}
/**
* Create result from {@link Value}.
* @param v result value
*/
public Result(Value v) {
this.v = v;
}
/**
* Get the result variable
* @return the variable or <code>null</code>.
*/
public Variable getVariable() {
return var;
}
/**
* Get the result value
* @return the value or <code>null</code>.
*/
public Value getValue() {
return v;
}
}
/**
* Declarative registration of Evaluator implementation.
* By marking the implementation class with this annotation,
* you automatically register that implementation for use by debugger.
* The class must be public and have a public constructor which takes
* no arguments or takes {@link ContextProvider} as an argument.
*/
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface Registration {
/**
* The language to register this evaluator for.
*/
String language();
}
}