This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.
Summary: | Add API for ability to plug-in evaluator engine. | ||
---|---|---|---|
Product: | debugger | Reporter: | Martin Entlicher <mentlicher> |
Component: | Java | Assignee: | Martin Entlicher <mentlicher> |
Status: | CLOSED FIXED | ||
Severity: | blocker | CC: | ivan, non_migrated_user, phejl, pjiricka |
Priority: | P3 | Keywords: | API, API_REVIEW_FAST |
Version: | 6.x | ||
Hardware: | All | ||
OS: | All | ||
Issue Type: | ENHANCEMENT | Exception Reporter: | |
Bug Depends on: | |||
Bug Blocks: | 167254 | ||
Attachments: |
Proposed API
Reworked Evaluator API |
Description
Martin Entlicher
2009-09-02 17:27:21 UTC
The idea is to introduce an abstract class Evaluator with two methods: public Variable evaluate (String expression, CallStackFrame csf, ObjectVariable var) and public Value evaluate (String expression, StackFrame csf, int stackDepth, ObjectReference var, boolean canInvokeMethods, Runnable methodInvokePreprocessor) The first one should be overridden if the evaluator implementation can perform the evaluation using JPDADebugger APIs. The implementation does not have to deal with JDI exceptions and it should be more safe. The second method should be overridden if the evaluator implementation needs to use all functionality that JDI provides. In that case it needs to count with JDI exceptions and when it's going to invoke methods, it's required to call methodInvokePreprocessor.run() so that JPDA implementation is aware of running threads. The implementation needs to override at least one of these two methods. It's expected that clients will register one implementation of Evaluator class per language. Created attachment 87061 [details]
Proposed API
Please review the API change and let me know whether this is suitable for JavaFX (and eventually other) language evaluator. There will always be a default Evaluator implementation for Java language. That default implementation also acts as a test of the Evaluator registration mechanism and Evaluator usage. question 1: This is an api for debugging JVM based languages (javafx, jruby etc) right? It's not connected to any truly generic evaluation? question 2 (a bit of-topic): native debugging would like to utilize EvaluateCode and the corresponding window with a History node ... is any of that up and coming? Y01 typo in "fro the desired" Y02 what exactly the Registration.language() can return? Mime type? Y03 Eliminate magic or test it. What exactly is the purpose of "class uoev extends UnsupportedOperationException implements Value"? Y04 Split the Evaluator class into two interfaces JPDAEvaluator and JDIEvaluator each with single method. Possibly allow a single implementor to implement both (if that is a valid use case, is it?) Y05 Get ready for growing parameters. Replace the three and six argument method with request/response pattern. http://wiki.apidesign.org/wiki/RequestResponse It will simplify your life when you find out you need fourth and seventh argument in future. question 1: Yes, this API is for JVM based languages that are compiled into bytecode. We do not have a truly generic API (with representation of a stack frame and variables) that would allow to specify truly generic evaluation. And it does not seem to be required so far. question 2: I've expected such question... We first try new UI in JPDA debugger and after they evolve into stable state, we can move it into debuggercore. Please file a separate issue for this. Y01: will fix, thanks. Y02: Registration.language() returns what you specify in the annotation of the Evaluator implementation. E.g. "Java" for default Java evaluator: @Evaluator.Registration(language="Java") It's the stratum of the language, or file name extension if the stratum is not defined. Y03: Since the implementor does not have to override both methods, originally I planned them to just throw UnsupportedOperationException. The calling code would try the second if the first one threw UnsupportedOperationException. But since throwing an exception hurts performance, we just return it. It's an implementation detail - private contract between Evaluator class and the calling code. This serves as a test of which method is implemented. Y04: Possible, but I would like to keep it as an abstract class so that I may add more methods in the future (though it's not very likely we'll need more methods). It does not make much sense to implement both methods, if the first one is implemented, the second is not called. Y05: Good idea, perhaps both methods could be replaced with: public interface Evaluator { Result evaluate(String expression, Context context); public final class Result { Variable var; Value v; public Result(Variable var) { this.var = var; } public Result(Value v) { this.v = v; } public Variable getVariable() { return var; } public Value getValue() { return v; } } /** Warning that clients must not implement this, new methods can be added. */ // Clients receive implementation of this interface, created by debugger.jpda module. // This interface resides in api.debugger.jpda, so it can not be a final class, // since all the logic is in implementation module - debugger.jpda. public interface Context { CallStackFrame getCallStackFrame(); ObjectVariable getContextVariable(); StackFrame getStackFrame(); int getStackDepth(); ObjectReference getContextObject(); boolean canInvokeMethods(); void notifyMethodInvoke(); } } The two separate methods are more clear IMHO, but this allows more flexibility. Re. Y05. public interface Evaluator { void evaluate(Context context, Result res); }, would imho be even more flexible (context would need getExpression() getter too). Of course Context and Result shall be final classes. I know that the Context is created by other module, but that shall be easy resolve with a factory method like Context create(CallStackFrame, ObjectVariable, StackFrame, int, ObjectReference, boolean, Runnable), or (if you need lazy implementation you could even have Context create(Callable<CallStackFrame>, Callable<ObjectVariable>, Callable<StackFrame>, Callable<Integer>, Callable<ObjectReference>, boolean, Runnable). In spite of ugliness of such method, it does not hurt the API usability (if you hide it enough), as the only one who is supposed to call it is you. Re. Y04, with Y05 you are unlikely to need to add new methods into the interface(s). You will be adding the methods to Context or Result classes which are easily extensible (as they are final). Y06 I am not sure if you considered such solution, but with @EvaluatorRegistration you do not need the (public) Evaluator interface much. People could annotate any static method and you could call it via reflection: @EvaluatorRegistration public static void evaluateLookup(Context c, Result r) { ... } Re. Y03 Oh, the exception is returned, not thrown! That is really inventive (but I don't like it). In fact the throwing of exception is not that slow, the slowest operation (as far as I know) is exception's constructor and its fillInStackTrace() method. Anyway this will not be needed if you implement Y05 and/or Y04. Created attachment 87277 [details]
Reworked Evaluator API
Reworked Evaluator API attached. It uses Y05 approach and therefore a single evaluate() method takes parameters through Expression and Context final classes and produces Result. This adds more flexibility. If there are no more comments on this, I'd like to integrate it tomorrow so that we can continue the work on JavaFX evaluator... Thanks. Thanks for the review. The Evaluator API together with the default implementation for Java language is implemented in changeset: 144697:a3b3a7c43bed http://hg.netbeans.org/main/rev/a3b3a7c43bed Integrated into 'main-golden', will be available in build *200909181401* on http://bits.netbeans.org/dev/nightly/ (upload may still be in progress) Changeset: http://hg.netbeans.org/main-golden/rev/a3b3a7c43bed User: mentlicher@netbeans.org Log: #171342 - Evaluator API for languages compiled into bytecode. verified closed |