Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite the Script Engine API #2107

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

madoar
Copy link
Collaborator

@madoar madoar commented Oct 12, 2019

This PR is still work in progress and at the current time only meant as a preview for discussion purposes!

Copy link
Collaborator Author

@madoar madoar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the following I've added some comments about important parts of the PR. Feel free to take a look at the proposed changes so that we can improve the API!

Comment on lines +21 to 42
/**
* A script engine used by Phoenicis
*
* @param <R> The internal script result type
*/
public interface PhoenicisScriptEngine<R> {
/**
* Sets a global value for the given key (variable) in the script context
*
* @param key The variable name in the script context
* @param value The corresponding value
*/
void put(String key, Object value);

/**
* Evaluates the given script and returns its result
*
* @param script The script
* @return The result of the evaluated script
*/
R evaluate(String script);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A script engine now provides a blocking method evaluate which returns the result of the evaluated script in a blocking manner. The result is of a generic type which depends on the concrete script interpreter used underneath (e.g. Value for Graal)

Comment on lines +3 to +16
/**
* A typed script engine used by Phoenicis
*
* @param <T> The result type of the script
*/
public interface TypedScriptEngine<T> {
/**
* Evaluates the script with the given id and returns its result cast to the given type
*
* @param scriptId The id of the evaluated script
* @return The cast result of the evaluated script
*/
T evaluate(String scriptId);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class functions as an intermediate class for direct usage. It returns the result of an evaluated script (this time given by the script id) as an application type (e.g. EngineSetting, Installer etc.)

Comment on lines +3 to +15
/**
* A factory to create new {@link TypedScriptEngine} instances
*/
public interface TypedScriptEngineFactory {
/**
* Creates a new {@link TypedScriptEngine} instance for the given `type` input
*
* @param type The result type {@link Class} for the created script engine
* @param <T> The result type
* @return The created script engine
*/
<T> TypedScriptEngine<T> createScriptEngine(final Class<T> type);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is a factory for TypedScriptEngine instances. At the end I plan to have a single implementation of the factory class which is able to return TypedScriptEngine for all application classes, but because the application classes are currently split into multiple modules we currently need to provide multiple TypedScriptEngineFactory implementations, one for each application class.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "application class"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By application class/type I mean interfaces like Installer, Engine, EngineSetting, Verb etc.


void put(String name, Object object, Consumer<Exception> errorCallback);

void addErrorHandler(Consumer<Exception> errorHandler);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I'm removing the error handlers contained in the script engine we need to provide them nearer to where they are needed in specific try/catch blocks. I think this is a better approach because it is currently very hard to know what error handlers are called for a script because they are often specified in an entirely different code region.

Comment on lines +8 to +20
public class GraalEngineType implements ScriptEngineType<Value> {
@Override
public PhoenicisScriptEngine<Value> createScriptEngine() {
return new PolyglotScriptEngine("js",
Map.of("js.nashorn-compat", "true",
"js.experimental-foreign-object-prototype", "true"));
}

@Override
public String toString() {
return "graal.js";
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the graal.js specific implementation of the ScriptEngineType

*/
public EngineSettingsManager(PhoenicisScriptEngineFactory phoenicisScriptEngineFactory,
ExecutorService executorService) {
public EngineSettingsManager(TypedScriptEngineFactory typedScriptEngineFactory,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class shows an example how I intend to use the script engine in the future.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it mean that we cannot access e.g. applications and engines via the same script engine?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. We will need to provide a dedicated TypedScriptEngine implementation for every application type (i.e. Installer, Engine, etc)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. So this is also because we want to keep the APIs in the modules and not centralize them in phoenicis-scripts (we discussed this somewhere else).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is also because we want to keep the APIs in the modules and not centralize them in phoenicis-scripts (we discussed this somewhere else).

No the reason why I want to implement a dedicated TypedScriptEngine class for every application type is because it divides the functionality better. It follows the approach divide and conquer in that we implement independent behavior in different modules/classes. For example what have the processing of Installer script and Verb script in common except that they are both executed with graaljs and use the module system? Therefore we divide the corresponding functionality in different classes.

I still plan to move all application types (and ideally also the TypeScriptEngine implementations) to the phoenicis-scripts module. I didn't do this yet because I think the main changes of this PR are easier comprehensible if I leave as much of the related refactoring as possible for later.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still plan to move all application types

I thought @qparis doesn't like this?

Comment on lines +7 to +22
public class EngineSettingScriptEngine implements TypedScriptEngine<EngineSetting> {
private final PhoenicisScriptEngine<Value> scriptEngine;

public EngineSettingScriptEngine(PhoenicisScriptEngine<Value> scriptEngine) {
super();

this.scriptEngine = scriptEngine;
}

@Override
public EngineSetting evaluate(String scriptId) {
final String script = String.format("include(\"%s\")", scriptId);

return scriptEngine.evaluate(script).newInstance().as(EngineSetting.class);
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the TypedScriptEngine for the EngineSetting application class

@madoar madoar requested review from plata and qparis October 12, 2019 12:55
@plata
Copy link
Collaborator

plata commented Oct 13, 2019

Is this something you want to do before alpha-3?

@madoar
Copy link
Collaborator Author

madoar commented Oct 13, 2019

Ideally yes, but I'm not sure about our schedule for alpha-3.

@plata
Copy link
Collaborator

plata commented Oct 14, 2019

I have no clue regarding the schedule. There are some packaging issue which cannot fix.

@madoar
Copy link
Collaborator Author

madoar commented Oct 14, 2019

I think we will need to wait until @qparis has more time before we can fix those.

I'm also currently a bit limited on time I can invest in this PR. Therefore feel free to give the PR an in-depth look so that we have a stable and durable solution when we finally merge the PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants