從零開始編寫指令碼引擎

辣子雞 發表於 2022-05-15

使用Java實現JS指令碼引擎

專案地址

https://github.com/GitHub-Laz...

  • 支援解析js指令碼生成語法樹
  • 支援執行完整js指令碼
  • 支援安全模式執行單行表示式
  • 支援設定超時時間

打包後命令列執行

git clone https://github.com/GitHub-Laziji/js-engine.git
cd js-engine
mvn package
cd target

java -jar ./js-engine-1.0-SNAPSHOT-jar-with-dependencies.jar ./test.js

或者安裝後直接引入依賴

git clone https://github.com/GitHub-Laziji/js-engine.git
cd js-engine
mvn install
<dependency>
    <groupId>org.laziji.commons</groupId>
    <artifactId>js-engine</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

1. 生成語法樹 並輸出格式化程式碼

示例程式碼

class Test{
    public static void main(String[] args){
        Top.init();
        DocNode doc = Top.compile("let      a=1+2,b=3,c=\"string\",d=a*(b+c/2),func=function(){};");
        System.out.println(doc);
    }
}

輸出

let a = 1 + 2, b = 3, c = "string", d = a * (b + c / 2), func = function () {

}

2. 執行完整指令碼

以下示例為執行快排演算法(執行環境執行緒隔離)

示例程式碼

class Test{
    /**
     * function sort(arr, i, j) {
     *   if (i >= j) {
     *     return;
     *   }
     *   let p = i, q = j;
     *   let temp = arr[p];
     *   while (p < q) {
     *     while (p < q && arr[q] >= temp) {
     *       q-=1;
     *     }
     *     arr[p] = arr[q];
     *     while (p < q && arr[p] <= temp) {
     *       p+=1;
     *     }
     *     arr[q] = arr[p];
     *   }
     *   arr[q] = temp;
     *   sort(arr, i, q - 1);
     *   sort(arr, q + 1, j);
     * }
     *
     * let arr = [234, 57, 12, 123, 346, 1234, 2];
     *
     * sort(arr, 0, arr.length - 1);
     */
    public static void main(String[] args){
        Top.init();
        Top.eval("function sort(arr, i, j) {\n" +
                "    if (i >= j) {\n" +
                "        return;\n" +
                "    }\n" +
                "    let p = i, q = j;\n" +
                "    let temp = arr[p];\n" +
                "    while (p < q) {\n" +
                "        while (p < q && arr[q] >= temp) {\n" +
                "            q-=1;\n" +
                "        }\n" +
                "        arr[p] = arr[q];\n" +
                "        while (p < q && arr[p] <= temp) {\n" +
                "            p+=1;\n" +
                "        }\n" +
                "        arr[q] = arr[p];\n" +
                "    }\n" +
                "    arr[q] = temp;\n" +
                "    sort(arr, i, q - 1);\n" +
                "    sort(arr, q + 1, j);\n" +
                "}\n" +
                "\n" +
                "let arr = [234, 57, 12, 123, 346, 1234, 2];\n" +
                "\n" +
                "sort(arr, 0, arr.length - 1);");
        Top.loop();
        System.out.println(Top.getThreadLocalTop().getMainContexts().getContexts().peek().toSimpleString());
    }
}

輸出

arr: [2, 12, 57, 123, 234, 346, 1234]
sort: [object Object]

3. 執行單行表示式

該模式下只支援單行表示式 並且無法使用for、while、function、lambda、import關鍵字

示例程式碼

class Test{
    public static void main(String[] args){
        Top.init();
        System.out.println(Top.exprEval("'hello '+(1*2*3*4)"));
    }
}

輸出

hello 24

4. 設定超時時間

通過Top.getThreadLocalTop().setOvertime(100L);設定超時時間,單位毫秒

示例程式碼

class Test{
    public static void main(String[] args){
        Top.init();
        Top.getThreadLocalTop().setOvertime(100L);
        Top.addInternalModules("sys", new SystemModuleValue());
        Top.eval("import { print } from \"sys\";\n" +
                "\n" +
                "let i=1;\n" +
                "while(true){\n" +
                "    print(i++);\n" +
                "}");
        Top.loop();
    }
}

輸出

1
2
3
...

org.laziji.commons.js.exception.RunException: Run timeout.
...