Merge remote-tracking branch 'origin/2.0' into 2.0
This commit is contained in:
commit
d1746f323f
|
|
@ -45,7 +45,7 @@ public class ScriptPayloadParserBuilder implements PayloadParserBuilderStrategy
|
|||
String script = config.getString("script")
|
||||
.orElseThrow(() -> new IllegalArgumentException("script不能为空"));
|
||||
String lang = config.getString("lang")
|
||||
.orElseThrow(() -> new IllegalArgumentException("lang不能为空"));
|
||||
.orElse("js");
|
||||
|
||||
CompiledScript compiledScript = Scripts
|
||||
.getFactory(lang)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import java.util.stream.Collectors;
|
|||
|
||||
public abstract class AbstractScriptFactory implements ScriptFactory {
|
||||
|
||||
private final Utils utils = new Utils();
|
||||
|
||||
static Class<?>[] DEFAULT_DENIES = {
|
||||
System.class,
|
||||
File.class,
|
||||
|
|
@ -97,4 +99,20 @@ public abstract class AbstractScriptFactory implements ScriptFactory {
|
|||
return denies.contains("*") || denies.contains(typeName);
|
||||
}
|
||||
|
||||
|
||||
public Utils getUtils(){
|
||||
return utils;
|
||||
}
|
||||
|
||||
|
||||
public class Utils {
|
||||
|
||||
private Utils(){}
|
||||
|
||||
public Object toJavaType(Object obj) {
|
||||
return AbstractScriptFactory.this.convertToJavaType(obj);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ public abstract class JavaScriptFactory extends Jsr223ScriptFactory {
|
|||
"this.eval = function(e){};" +
|
||||
"function readFully(){};" +
|
||||
"function readLine(){};" +
|
||||
"const print = console.log;" +
|
||||
"const echo = console.log;");
|
||||
"const print = function(e){console.log(e)};" +
|
||||
"const echo = print;");
|
||||
|
||||
wrap.add("/* script start */");
|
||||
|
||||
|
|
@ -57,6 +57,7 @@ public abstract class JavaScriptFactory extends Jsr223ScriptFactory {
|
|||
Class<? super T> expose) {
|
||||
StringJoiner joiner = new StringJoiner("\n");
|
||||
Set<String> distinct = new HashSet<>();
|
||||
joiner.add("var _$this = $this;");
|
||||
joiner.add(
|
||||
Arrays.stream(expose.getMethods())
|
||||
.filter(method -> !ignoreMethod.contains(method))
|
||||
|
|
@ -70,10 +71,14 @@ public abstract class JavaScriptFactory extends Jsr223ScriptFactory {
|
|||
.append(method.getName())
|
||||
.append("(){");
|
||||
if (method.getParameterCount() == 0) {
|
||||
call.append("return $$__that.")
|
||||
call.append("return _$this.")
|
||||
.append(method.getName())
|
||||
.append("();");
|
||||
} else {
|
||||
} else if (method.getParameterCount() == 1 && method.getParameterTypes()[0].isArray()) {
|
||||
call.append("return _$this.")
|
||||
.append(method.getName())
|
||||
.append("(utils.toJavaType(arguments));");
|
||||
}else {
|
||||
|
||||
for (int i = 0; i <= method.getParameterCount(); i++) {
|
||||
String[] args = new String[i];
|
||||
|
|
@ -82,7 +87,7 @@ public abstract class JavaScriptFactory extends Jsr223ScriptFactory {
|
|||
}
|
||||
String arg = String.join(",", args);
|
||||
call.append("if(arguments.length==").append(i).append("){")
|
||||
.append("return $$__that.")
|
||||
.append("return _$this.")
|
||||
.append(method.getName())
|
||||
.append("(").append(arg).append(");")
|
||||
.append("}");
|
||||
|
|
@ -101,7 +106,7 @@ public abstract class JavaScriptFactory extends Jsr223ScriptFactory {
|
|||
CompiledScript compiledScript = compile(script.content(joiner.toString()));
|
||||
|
||||
return (instance, ctx) -> {
|
||||
ctx.setAttribute("$$__that", instance, ScriptContext.ENGINE_SCOPE);
|
||||
ctx.setAttribute("$this", instance, ScriptContext.ENGINE_SCOPE);
|
||||
return compiledScript.call(ctx);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ public abstract class Jsr223ScriptFactory extends AbstractScriptFactory {
|
|||
ctx.setAttribute("console", new Jsr223ScriptFactory.Console(
|
||||
LoggerFactory.getLogger("org.jetlinks.community.script." + script.getName())),
|
||||
ScriptContext.ENGINE_SCOPE);
|
||||
ctx.setAttribute("utils", getUtils(), ScriptContext.ENGINE_SCOPE);
|
||||
|
||||
ctx.setAttribute("engine", null, ScriptContext.ENGINE_SCOPE);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,281 @@
|
|||
package org.jetlinks.community.script;
|
||||
|
||||
import jdk.nashorn.internal.objects.Global;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.java.Log;
|
||||
import org.jetlinks.community.script.jsr223.JavaScriptFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public abstract class JavaScriptFactoryTest {
|
||||
|
||||
protected abstract JavaScriptFactory getFactory();
|
||||
|
||||
@Test
|
||||
void testBadAccess() {
|
||||
JavaScriptFactory factory = getFactory();
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "return this.engine"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "return global"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "return context"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "delete this.engine;return this.engine"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "delete quit;return quit()"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "return exit()"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "return exit(0)"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "delete this.eval; return eval('1')"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "return eval('1')"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "return eval('1')"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "return Function('return 1')()"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "return this.eval('1')"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "const func = function() { return eval(1); }; return func();"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
{
|
||||
CompiledScript script = factory.compile(Script.of("test", "return (function(){ return eval('return 1') })()"));
|
||||
Object resul = script.call(Collections.emptyMap());
|
||||
assertNull(resul);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testArray() {
|
||||
JavaScriptFactory factory = getFactory();
|
||||
Object val = factory.compile(Script.of("test", "var arr = []; arr.push({a:1,b:2}); return arr;"))
|
||||
.call(Collections.emptyMap());
|
||||
System.out.println(val);
|
||||
assertNotNull(val);
|
||||
assertTrue(val instanceof List);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPrint() {
|
||||
JavaScriptFactory factory = getFactory();
|
||||
factory.compile(Script
|
||||
.of("test", "print(123);console.log('test 123');"))
|
||||
.call(Collections.emptyMap());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTernary() {
|
||||
JavaScriptFactory factory = getFactory();
|
||||
Object val = factory.compile(Script.of("test", "return 1 + (1>0?2:1);"))
|
||||
.call(Collections.emptyMap());
|
||||
System.out.println(val);
|
||||
assertEquals(3, val);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullSafe() {
|
||||
JavaScriptFactory factory = getFactory();
|
||||
Object val = factory.compile(Script.of("test", "return temp1==null? 10 :0"))
|
||||
.call(Collections.emptyMap());
|
||||
assertEquals(10, val);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBenchmark() {
|
||||
JavaScriptFactory factory = getFactory();
|
||||
CompiledScript script = factory.compile(Script.of("test", "if(temp1==null) {return temp} else {return temp+1}"));
|
||||
script.call(Collections.singletonMap("temp", 10));
|
||||
|
||||
long time = System.currentTimeMillis();
|
||||
for (long i = 0; i < 10_0000; i++) {
|
||||
script.call(Collections.singletonMap("temp", 10));
|
||||
}
|
||||
System.out.println(System.currentTimeMillis() - time);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnonymous() {
|
||||
JavaScriptFactory factory = getFactory();
|
||||
factory.compile(Script.of("test", "parser.fixed(4)\n" +
|
||||
" .handler(function(buffer){\n" +
|
||||
" var len = buffer.getShort(2);\n" +
|
||||
" parser.fixed(len).result(buffer);\n" +
|
||||
" })\n" +
|
||||
" .handler(function(buffer){\n" +
|
||||
" parser.result(buffer)\n" +
|
||||
" .complete();\n" +
|
||||
" });"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVarNest() {
|
||||
JavaScriptFactory factory = getFactory();
|
||||
TestNest nest = new TestNest();
|
||||
|
||||
factory.compile(Script.of("test", "const test = this.test; test.setup(function(e){ return e+test.data() })"))
|
||||
.call(Collections.singletonMap("test", nest));
|
||||
|
||||
assertNotNull(nest.func);
|
||||
|
||||
System.out.println(nest.func.apply(10));
|
||||
}
|
||||
|
||||
public static class TestNest {
|
||||
Function<Object, Object> func;
|
||||
|
||||
public Object data() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public TestNest setup(Function<Object, Object> func) {
|
||||
this.func = func;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testUtils() {
|
||||
{
|
||||
JavaScriptFactory factory = getFactory();
|
||||
MyClazz utils = new MyClazz();
|
||||
|
||||
ExposedScript<MyClazz> script = factory.compileExpose(Script.of("test", "return $recent($recent(),$recent(temp))"), TestExtend.class);
|
||||
|
||||
assertEquals(utils.$recent(utils.$recent(), utils.$recent(2)), script.call(utils, Collections.singletonMap("temp", 2)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNestFunction() {
|
||||
JavaScriptFactory factory = getFactory();
|
||||
|
||||
Map<String, Object> ctx = new HashMap<>();
|
||||
ctx.put("val", 1);
|
||||
|
||||
ExposedScript<TestExtend> script = factory.compileExpose(
|
||||
Script.of("test", "var $val = val; function decode(){ return $recent()+$val }; return call(decode);"),
|
||||
TestExtend.class);
|
||||
|
||||
TestExtend extend = new TestExtend();
|
||||
script.call(extend, ctx);
|
||||
|
||||
|
||||
assertNotNull(extend.call);
|
||||
assertEquals(2.0, extend.call.get());
|
||||
|
||||
}
|
||||
|
||||
public static class MyClazz extends TestExtend {
|
||||
|
||||
}
|
||||
|
||||
public static class TestExtend {
|
||||
Supplier<Object> call;
|
||||
|
||||
public int max(int i, int j) {
|
||||
return Math.max(i, j);
|
||||
}
|
||||
|
||||
public int $recent() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int $recent(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
public int $recent(int i, int j) {
|
||||
return i + j;
|
||||
}
|
||||
|
||||
public Object call(Supplier<Object> call) {
|
||||
this.call = call;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMake() {
|
||||
|
||||
Api api = getFactory().bind(Script.of(Api.class.getName(), "function add(a,b){return a+b};"), Api.class);
|
||||
|
||||
assertEquals(3, api.add(1, 2));
|
||||
|
||||
assertNull(api.reduce(1, 2));
|
||||
|
||||
assertNull(api.reduce(1, 2));
|
||||
|
||||
}
|
||||
|
||||
public interface Api {
|
||||
int add(int a, int b);
|
||||
|
||||
Object reduce(int a, int b);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package org.jetlinks.community.script.nashorn;
|
||||
|
||||
import org.jetlinks.community.script.JavaScriptFactoryTest;
|
||||
import org.jetlinks.community.script.jsr223.JavaScriptFactory;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class NashornScriptFactoryTest extends JavaScriptFactoryTest {
|
||||
NashornScriptFactory factory = new NashornScriptFactory();
|
||||
|
||||
@Override
|
||||
protected JavaScriptFactory getFactory() {
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<script src="javascript-utils.js" type="application/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script type="text/javascript">
|
||||
var nodes = {
|
||||
CallExpression:{
|
||||
render:function (json){
|
||||
return `<span class="CallExpression">
|
||||
<div class="expression">
|
||||
<span>执行函数</span><span class="callee">${render(json.callee)}</span></div>
|
||||
<span>参数</span><span>${renderList(arguments)}</span>
|
||||
</span>`
|
||||
}
|
||||
},
|
||||
ExpressionStatement:{
|
||||
render:function (json){
|
||||
return `<span class="ExpressionStatement">
|
||||
<div class="expression">${render(json.expression)}</div>
|
||||
</span>`
|
||||
}
|
||||
},
|
||||
ThisExpression:{
|
||||
render:function (json){
|
||||
return "this";
|
||||
}
|
||||
}
|
||||
,
|
||||
MemberExpression:{
|
||||
render:function (json){
|
||||
return `<span class="MemberExpression">
|
||||
<span class="member-object">${render(json.object)}</span><span class="member-property">.${json.property}</span>
|
||||
</span>`
|
||||
}
|
||||
},
|
||||
Identifier:{
|
||||
render:function (json){
|
||||
return json.name;
|
||||
}
|
||||
},
|
||||
VariableDeclaration: {
|
||||
render: function (json) {
|
||||
return `<span class="VariableDeclaration">
|
||||
<span>声明变量</span>
|
||||
<span class="var-name"><input value="${render(json.id)}"></span>
|
||||
<span class="var-is">值为</span>
|
||||
<span class="var-init">${render(json.init)}</span>
|
||||
</span>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function render(json) {
|
||||
let type = nodes[json.type];
|
||||
if(!type){
|
||||
return "";
|
||||
}
|
||||
return type.render(json);
|
||||
}
|
||||
|
||||
function renderList(json) {
|
||||
let list = [];
|
||||
for(let i=0;i<json.length;i++){
|
||||
list[i] = render(json[i]);
|
||||
}
|
||||
return list.join("");
|
||||
}
|
||||
|
||||
function parse(json){
|
||||
let container = document.getElementsByClassName('container')[0];
|
||||
|
||||
for(let i=0;i<json.length;i++){
|
||||
container.innerHTML=render(json[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
|
||||
|
||||
|
||||
function parse(json){
|
||||
|
||||
var jsonObject = JSON.parse(json);
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue