Skip to content
Snippets Groups Projects
Commit a3de5cb8 authored by Zoey76's avatar Zoey76
Browse files

Initial Commit

parents
No related branches found
No related tags found
No related merge requests found
Showing
with 775 additions and 0 deletions
/target/
/test-output/
/.classpath
/.project
/.settings/
This diff is collapsed.
README 0 → 100644
# L2J Java Script Engine
A JSR-223 implementation for Java.
## Getting Started
### Pre-requistites
To build this project you will need [Java JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html) 10.0+ and [Maven](https://maven.apache.org/) 3.5+.
### Build
```
$ mvn clean install
```
### Run tests
```
$ mvn test
```
### Versioning
We use [SemVer](https://semver.org/) for versioning.
### Auhtors
Zoey76
### License
The project is under GPLv3, see [LICENSE](LICENSE) file.
\ No newline at end of file
pom.xml 0 → 100644
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.l2jserver</groupId>
<artifactId>l2j-server-script-engine</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>L2J Server - Script Engine</name>
<inceptionYear>2018</inceptionYear>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>10</maven.compiler.source>
<maven.compiler.target>10</maven.compiler.target>
<InMemoryJavaCompiler.version>1.3.0</InMemoryJavaCompiler.version>
<slf4j.version>1.7.25</slf4j.version>
<testng.version>6.14.3</testng.version>
<!-- Plugins -->
<maven-source-plugin.version>3.0.1</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.0.1</maven-javadoc-plugin.version>
<maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
<jgitflow-maven-plugin.version>1.0-m5.1</jgitflow-maven-plugin.version>
</properties>
<developers>
<developer>
<id>Zoey76</id>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>org.mdkt.compiler</groupId>
<artifactId>InMemoryJavaCompiler</artifactId>
<version>${InMemoryJavaCompiler.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.apache-extras.beanshell</groupId>
<artifactId>bsh</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-source-plugin.version}</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<configuration>
<additionalJOption>-html5</additionalJOption>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
</plugin>
<plugin>
<groupId>external.atlassian.jgitflow</groupId>
<artifactId>jgitflow-maven-plugin</artifactId>
<version>${jgitflow-maven-plugin.version}</version>
<configuration>
<noDeploy>true</noDeploy>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
/*
* Copyright (C) 2018 L2J Server
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.l2jserver.script.engine;
import static com.l2jserver.script.engine.factory.JavaScriptEngineFactory.JAVA;
import static com.l2jserver.script.engine.factory.JavaScriptEngineFactory.JAVA_LANGUAGE_VERSION;
import static com.l2jserver.script.engine.tools.JavaEngineTools.evalScript;
import static com.l2jserver.script.engine.tools.JavaEngineTools.readerToString;
import java.io.Reader;
import javax.script.AbstractScriptEngine;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptContext;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import com.l2jserver.script.engine.factory.JavaScriptEngineFactory;
import com.l2jserver.script.engine.script.JavaCompiledScript;
/**
* Java Script Engine implementation.
*
* @author Zoey76
*/
public class JavaScriptEngine extends AbstractScriptEngine implements Compilable {
public static final String CLASSLOADER = "ClassLoader";
public static final String CLASSPATH = "classpath";
public static final String MAINCLASS = "mainClass";
public static final String SOURCEPATH = "sourcepath";
private final ScriptEngineFactory scriptEngineFactory;
private static final String ENGINE_DESCRIPTION = "Engine " + JAVA + " v" + JAVA_LANGUAGE_VERSION;
public JavaScriptEngine(ScriptEngineFactory scriptEngineFactory) {
this.scriptEngineFactory = scriptEngineFactory;
}
public JavaScriptEngine() {
this(new JavaScriptEngineFactory());
}
@Override
public Object eval(String script, ScriptContext context) throws ScriptException {
return evalScript(script, context);
}
@Override
public Object eval(Reader reader, ScriptContext context) throws ScriptException {
return eval(readerToString(reader), context);
}
@Override
public Bindings createBindings() {
return new SimpleBindings();
}
@Override
public ScriptEngineFactory getFactory() {
return scriptEngineFactory;
}
@Override
public CompiledScript compile(String script) throws ScriptException {
return new JavaCompiledScript(this, script);
}
@Override
public CompiledScript compile(Reader script) throws ScriptException {
return compile(readerToString(script));
}
@Override
public String toString() {
return ENGINE_DESCRIPTION;
}
}
/*
* Copyright (C) 2018 L2J Server
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.l2jserver.script.engine.factory;
import java.util.Collections;
import java.util.List;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import com.l2jserver.script.engine.JavaScriptEngine;
/**
* Java Script Engine Factory implementation.
*
* @author Zoey76
*/
public class JavaScriptEngineFactory implements ScriptEngineFactory {
public static final String JAVA = "Java";
public static final String JAVA_LANGUAGE_VERSION = "10.0.2";
private static final String THREADING = "THREADING";
private static final String MULTITHREADED = "MULTITHREADED";
private static final String EXTENSION = "java";
private static final String VERSION = "2.0";
private static final List<String> NAMES = List.of(JAVA);
private static final List<String> EXTENSIONS = List.of(EXTENSION);
private static final List<String> MIME_TYPES = Collections.emptyList();
private static long nextClassNum = 0L;
@Override
public String getEngineName() {
return JAVA;
}
@Override
public String getEngineVersion() {
return VERSION;
}
@Override
public List<String> getExtensions() {
return EXTENSIONS;
}
@Override
public String getLanguageName() {
return JAVA;
}
@Override
public String getLanguageVersion() {
return JAVA_LANGUAGE_VERSION;
}
@Override
public String getMethodCallSyntax(String obj, String m, String... args) {
final StringBuilder buf = new StringBuilder();
buf.append(obj);
buf.append('.');
buf.append(m);
buf.append('(');
if (args.length != 0) {
int i = 0;
for (; i < (args.length - 1); i++) {
buf.append(args[i] + ", ");
}
buf.append(args[i]);
}
buf.append(')');
return buf.toString();
}
@Override
public List<String> getMimeTypes() {
return MIME_TYPES;
}
@Override
public List<String> getNames() {
return NAMES;
}
@Override
public String getOutputStatement(String toDisplay) {
final StringBuilder buf = new StringBuilder();
buf.append("System.out.print(\"");
int len = toDisplay.length();
for (int i = 0; i < len; i++) {
char ch = toDisplay.charAt(i);
switch (ch) {
case '"': {
buf.append("\\\"");
break;
}
case '\\': {
buf.append("\\\\");
break;
}
default: {
buf.append(ch);
break;
}
}
}
buf.append("\");");
return buf.toString();
}
@Override
public String getParameter(String key) {
if (ScriptEngine.ENGINE.equals(key)) {
return getEngineName();
}
if (ScriptEngine.ENGINE_VERSION.equals(key)) {
return getEngineVersion();
}
if (ScriptEngine.NAME.equals(key)) {
return getEngineName();
}
if (ScriptEngine.LANGUAGE.equals(key)) {
return getLanguageName();
}
if (ScriptEngine.LANGUAGE_VERSION.equals(key)) {
return getLanguageVersion();
}
if (THREADING.equals(key)) {
return MULTITHREADED;
}
return null;
}
@Override
public String getProgram(String... statements) {
final StringBuilder buf = new StringBuilder();
buf.append("class ");
buf.append(getClassName());
buf.append(" {\n");
buf.append(" public static void main(String[] args) {\n");
if (statements.length != 0) {
for (String statement : statements) {
buf.append(" ");
buf.append(statement);
buf.append(";\n");
}
}
buf.append(" }\n");
buf.append("}\n");
return buf.toString();
}
@Override
public ScriptEngine getScriptEngine() {
return new JavaScriptEngine();
}
private String getClassName() {
return "com_sun_script_java_Main$" + getNextClassNumber();
}
private static synchronized long getNextClassNumber() {
return nextClassNum++;
}
}
/*
* Copyright (C) 2018 L2J Server
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.l2jserver.script.engine.script;
import javax.script.CompiledScript;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import com.l2jserver.script.engine.JavaScriptEngine;
/**
* Java Compiled Script implementation.
*
* @author Zoey76
*/
public class JavaCompiledScript extends CompiledScript {
private final JavaScriptEngine scriptEngine;
private final String script;
public JavaCompiledScript(JavaScriptEngine scriptEngine, String script) {
this.scriptEngine = scriptEngine;
this.script = script;
}
@Override
public Object eval(ScriptContext context) throws ScriptException {
return scriptEngine.eval(script, context);
}
@Override
public ScriptEngine getEngine() {
return scriptEngine;
}
}
\ No newline at end of file
/*
* Copyright (C) 2018 L2J Server
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.l2jserver.script.engine.tools;
import static com.l2jserver.script.engine.JavaScriptEngine.CLASSLOADER;
import static com.l2jserver.script.engine.JavaScriptEngine.CLASSPATH;
import static com.l2jserver.script.engine.JavaScriptEngine.MAINCLASS;
import static javax.script.ScriptContext.ENGINE_SCOPE;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import javax.script.ScriptContext;
import javax.script.ScriptException;
import org.mdkt.compiler.InMemoryJavaCompiler;
/**
* Java Engine Tools.
*
* @author Zoey76
*/
public class JavaEngineTools {
private static final String CONTEXT = "context";
private static final String ARGUMENTS = "arguments";
private static final String SET_SCRIPT_CONTEXT = "setScriptContext";
private static final String MAIN = "main";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final Class<?>[] ARG_SCRIPT_CONTEXT = new Class[] { ScriptContext.class };
private static final Class<?>[] ARG_MAIN = new Class[] { String[].class };
private static final InMemoryJavaCompiler COMPILER = InMemoryJavaCompiler.newInstance();
public static Object evalScript(String script, ScriptContext context) throws ScriptException {
// JSR-223 requirement
context.setAttribute(CONTEXT, context, ENGINE_SCOPE);
try {
final Class<?> clazz = COMPILER
.useOptions("-" + CLASSPATH, context.getAttribute(CLASSPATH, ENGINE_SCOPE).toString()) //
.useParentClassLoader((ClassLoader) context.getAttribute(CLASSLOADER, ENGINE_SCOPE)) //
.addSource(context.getAttribute(MAINCLASS, ENGINE_SCOPE).toString(), script) //
.compile(context.getAttribute(MAINCLASS, ENGINE_SCOPE).toString(), script);
final boolean isPublicClazz = Modifier.isPublic(clazz.getModifiers());
final Method setCtxMethod = findMethod(clazz, SET_SCRIPT_CONTEXT, ARG_SCRIPT_CONTEXT);
if (setCtxMethod != null) {
if (!isPublicClazz) {
setCtxMethod.setAccessible(true);
}
setCtxMethod.invoke(null, new Object[] { context });
}
final Method mainMethod = findMethod(clazz, MAIN, ARG_MAIN);
if (mainMethod != null) {
if (!isPublicClazz) {
mainMethod.setAccessible(true);
}
final String args[] = getArguments(context);
mainMethod.invoke(null, new Object[] { args });
}
return clazz;
} catch (Exception ex) {
throw new ScriptException(ex);
}
}
public static String readerToString(Reader reader) throws ScriptException {
try (BufferedReader in = new BufferedReader(reader)) {
final StringBuilder result = new StringBuilder();
String line = null;
while ((line = in.readLine()) != null) {
result.append(line).append(System.lineSeparator());
}
return result.toString();
} catch (IOException ex) {
throw new ScriptException(ex);
}
}
private static Method findMethod(Class<?> clazz, String methodName, Class<?>[] args) {
try {
final Method mainMethod = clazz.getMethod(methodName, args);
final int modifiers = mainMethod.getModifiers();
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) {
return mainMethod;
}
} catch (NoSuchMethodException nsme) {
}
return null;
}
private static String[] getArguments(ScriptContext ctx) {
int scope = ctx.getAttributesScope(ARGUMENTS);
if (scope != -1) {
Object obj = ctx.getAttribute(ARGUMENTS, scope);
if (obj instanceof String[]) {
return (String[]) obj;
}
}
return EMPTY_STRING_ARRAY;
}
}
com.l2jserver.script.engine.factory.JavaScriptEngineFactory
\ No newline at end of file
/*
* Copyright (C) 2018 L2J Server
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.l2jserver.script.compiler;
import static com.l2jserver.script.engine.JavaScriptEngine.CLASSLOADER;
import static com.l2jserver.script.engine.JavaScriptEngine.CLASSPATH;
import static com.l2jserver.script.engine.JavaScriptEngine.MAINCLASS;
import static javax.script.ScriptContext.ENGINE_SCOPE;
import static javax.script.ScriptEngine.FILENAME;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* Java Compiler test.
*
* @author Zoey76
*/
public class JavaCompilerTest {
private static final String CLASS_PATH = "src/test/resources/";
private static final String CLASS = "TestClass";
private static final String EXTENSION = "java";
private static final String PACKAGE_PATH = "com/l2jserver/script/java/";
private static final String PACKAGE = "com.l2jserver.script.java";
private static final String METHOD_NAME = "main";
@Test
void testJavaCompiler() throws Exception {
final ScriptEngineManager sem = new ScriptEngineManager();
final ScriptEngine scriptEngine = sem.getEngineByExtension(EXTENSION);
scriptEngine.getContext().setAttribute(CLASSPATH, CLASS_PATH, ENGINE_SCOPE);
scriptEngine.getContext().setAttribute(MAINCLASS, PACKAGE + "." + CLASS, ENGINE_SCOPE);
scriptEngine.getContext().setAttribute(FILENAME, CLASS + "." + EXTENSION, ENGINE_SCOPE);
scriptEngine.getContext().setAttribute(CLASSLOADER, getClass().getClassLoader(), ENGINE_SCOPE);
try (FileInputStream fis = new FileInputStream(CLASS_PATH + PACKAGE_PATH + CLASS + "." + EXTENSION);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr)) {
final Compilable compilableEngine = (Compilable) scriptEngine;
final CompiledScript compiledScript = compilableEngine.compile(br);
final Class<?> scriptClass = (Class<?>) compiledScript.eval(scriptEngine.getContext());
Assert.assertEquals(scriptClass.getName(), PACKAGE + "." + CLASS);
Assert.assertEquals(scriptClass.getPackageName(), PACKAGE);
final Method main = scriptClass.getMethod(METHOD_NAME, String[].class);
final Object script = scriptClass.getDeclaredConstructor().newInstance();
main.invoke(script, (Object) (String[]) null);
}
}
}
/*
* Copyright (C) 2018 L2J Server
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.l2jserver.script.java;
import java.lang.reflect.Method;
import com.l2jserver.script.java.child.ChildTestClass;
/**
* Test class example.
*
* @author Zoey76
*/
public class TestClass {
public static void main(String[] args) throws Exception {
final Method m = ChildTestClass.class.getMethod("getCountries");
final ChildTestClass child = ChildTestClass.class.getDeclaredConstructor().newInstance();
System.out.println(m.invoke(child));
}
}
/*
* Copyright (C) 2018 L2J Server
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.l2jserver.script.java.child;
import java.util.List;
/**
* Child test class example.
*
* @author Zoey76
*/
public class ChildTestClass extends ParentChildTestClass {
@Override
public String getCountries() {
// Java 10/9/8
var countries = List.of("France", "Bulgaria", "Germany");
return String.join(", ", countries);
}
}
/*
* Copyright (C) 2018 L2J Server
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.l2jserver.script.java.child;
/**
* Child test class example.
*
* @author Zoey76
*/
public abstract class ParentChildTestClass {
abstract String getCountries();
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment