From cb881ca903c15a80f40ea951d18355df1b97689c Mon Sep 17 00:00:00 2001 From: aLIEz <121407312@qq.com> Date: Fri, 26 Aug 2022 13:00:33 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20try-catch-finally=E4=B8=ADreturn?= =?UTF-8?q?=E5=92=8Cjava=E8=A1=A8=E7=8E=B0=E4=B8=8D=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../script/compile/MagicScriptCompiler.java | 57 ++++---- .../script/parsing/ast/statement/Return.java | 57 +++++--- .../script/parsing/ast/statement/Throw.java | 1 + .../parsing/ast/statement/TryStatement.java | 125 ++++++++++++------ 4 files changed, 162 insertions(+), 78 deletions(-) diff --git a/src/main/java/org/ssssssss/script/compile/MagicScriptCompiler.java b/src/main/java/org/ssssssss/script/compile/MagicScriptCompiler.java index 7a565fe..38f5441 100644 --- a/src/main/java/org/ssssssss/script/compile/MagicScriptCompiler.java +++ b/src/main/java/org/ssssssss/script/compile/MagicScriptCompiler.java @@ -1,7 +1,12 @@ package org.ssssssss.script.compile; import org.ssssssss.script.MagicScriptContext; -import org.ssssssss.script.asm.*; +import org.ssssssss.script.asm.ClassWriter; +import org.ssssssss.script.asm.Handle; +import org.ssssssss.script.asm.Label; +import org.ssssssss.script.asm.MethodVisitor; +import org.ssssssss.script.asm.Opcodes; +import org.ssssssss.script.asm.Type; import org.ssssssss.script.parsing.Span; import org.ssssssss.script.parsing.VarIndex; import org.ssssssss.script.parsing.ast.Expression; @@ -9,8 +14,8 @@ import org.ssssssss.script.parsing.ast.Node; import org.ssssssss.script.parsing.ast.VariableSetter; import org.ssssssss.script.parsing.ast.binary.AssigmentOperation; import org.ssssssss.script.parsing.ast.statement.VariableAccess; -import org.ssssssss.script.runtime.RuntimeContext; import org.ssssssss.script.runtime.MagicScriptRuntime; +import org.ssssssss.script.runtime.RuntimeContext; import org.ssssssss.script.runtime.Variables; import org.ssssssss.script.runtime.function.MagicScriptLambdaFunction; import org.ssssssss.script.runtime.handle.ArithmeticHandle; @@ -18,8 +23,18 @@ import org.ssssssss.script.runtime.handle.BitHandle; import org.ssssssss.script.runtime.handle.FunctionCallHandle; import org.ssssssss.script.runtime.handle.OperatorHandle; -import java.lang.invoke.*; -import java.util.*; +import java.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Stack; import java.util.concurrent.atomic.AtomicLong; import static org.ssssssss.script.asm.ClassWriter.COMPUTE_FRAMES; @@ -63,7 +78,10 @@ public class MagicScriptCompiler implements Opcodes { private final Stack labelStack = new Stack<>(); - private final Stack> finallyStack = new Stack<>(); + /* + * 替换为双端队列实现栈,因为Stack在for遍历时先从栈尾开始 + */ + private final Deque> finallyStack = new LinkedList<>(); private final Set varIndices; @@ -117,7 +135,6 @@ public class MagicScriptCompiler implements Opcodes { visitor.visitCode(); methodVisitors.push(visitor); vars.push(new ArrayList<>()); - finallyStack.push(null); labelStack.push(new Label[2]); return this; } @@ -252,6 +269,11 @@ public class MagicScriptCompiler implements Opcodes { return pop && node instanceof Expression ? insn(POP) : this; } + /** + * 设定tryCatch跳转 + * 如果在Label start到Label end代码范围内捕获到type异常,则跳转到Label handler + * type为null则表示finally,只要抛异常就跳转到handler + */ public MagicScriptCompiler tryCatch(Label start, Label end, Label handle, Class target) { self().visitTryCatchBlock(start, end, handle, getJvmType(target)); return this; @@ -649,30 +671,19 @@ public class MagicScriptCompiler implements Opcodes { return visitMethod(methodName, childNodes, parameters, () -> this.compile(childNodes)); } - public List finallyBlock() { - if (finallyStack.isEmpty()) { - return null; - } - return finallyStack.peek(); - } - - public void putHeadFinallyBlock(List finallyBlock) { - finallyStack.insertElementAt(finallyBlock, 0); + public Deque> finallyBlockStack() { + return finallyStack; } - public void putFinallyBlock(List finallyBlock) { - finallyStack.push(finallyBlock); + public void pushFinallyBlock(List finallyBlock) { + finallyStack.addFirst(finallyBlock); } - public List getFinallyBlock() { - if (finallyStack.isEmpty()) { - return null; - } - return finallyStack.pop(); + public List popFinallyBlock() { + return finallyStack.pollFirst(); } public MagicScriptCompiler pop() { - getFinallyBlock(); MethodVisitor visitor = methodVisitors.pop(); visitor.visitInsn(ACONST_NULL); visitor.visitInsn(ARETURN); diff --git a/src/main/java/org/ssssssss/script/parsing/ast/statement/Return.java b/src/main/java/org/ssssssss/script/parsing/ast/statement/Return.java index 96cd499..39a444f 100644 --- a/src/main/java/org/ssssssss/script/parsing/ast/statement/Return.java +++ b/src/main/java/org/ssssssss/script/parsing/ast/statement/Return.java @@ -21,30 +21,55 @@ public class Return extends Node { @Override public void visitMethod(MagicScriptCompiler compiler) { - if(returnValue != null){ + if (returnValue != null) { returnValue.visitMethod(compiler); } } @Override public void compile(MagicScriptCompiler compiler) { - List block = compiler.getFinallyBlock(); - if (returnValue == null) { // return - if (block != null) { // 如果有finally块 - compiler.compile(block); // 执行finally块 + /* + * 单独弹出栈顶的finallyBlock + * 防止在return_1所属的finallyBlock_1中存在return_2语句时, + * 编译过程: return_1 -> finallyBlock_1 -> return_2 -> finallyBlock_1 (loop),最终造成堆栈溢出 + */ + // 单独弹出栈顶的finallyBlock_1 + List finallyBlock = compiler.popFinallyBlock(); + if (returnValue == null) { + // return; or return null; + // 在返回前执行finallyBlockStack + if (finallyBlock != null) { + // 编译return_1的finallyBlock_1,此时若finallyBlock_1中存在return_2,不会再在return_2中编译finallyBlock_1 + compiler.compile(finallyBlock); + // 编译栈中剩余的finallyBlock + for (List fb : compiler.finallyBlockStack()) { + compiler.compile(fb); + } + // 压入finallyBlock_1,供try-catch-finally其他地方的return再次编译finallyBlock_1 + compiler.pushFinallyBlock(finallyBlock); } - compiler.insn(ACONST_NULL); // 压入 NULL - } else { // return expr; + // 压入 NULL + compiler.insn(ACONST_NULL); + } else { + // return expr; compiler.visit(returnValue); - if (block != null) { // 如果有finally块 - compiler.store(3) // 保存返回结果 - .compile(block) // 执行 finally - .load3(); // 加载返回结果 + // 在返回前执行finallyBlockStack + if (finallyBlock != null) { + // 保存返回结果 + compiler.store(3); + // 编译return_1的finallyBlock_1,此时若finallyBlock_1中存在return_2,不会再在return_2中编译finallyBlock_1 + compiler.compile(finallyBlock); + // 编译栈中剩余的finallyBlock + for (List fb : compiler.finallyBlockStack()) { + compiler.compile(fb); + } + // 加载返回结果 + compiler.load3(); + // 压入finallyBlock_1,供try-catch-finally其他地方的return再次编译finallyBlock_1 + compiler.pushFinallyBlock(finallyBlock); } } - if(block != null){ - compiler.putFinallyBlock(block); - } - compiler.insn(ARETURN); // 返回 + // 返回 + compiler.insn(ARETURN); } -} \ No newline at end of file +} diff --git a/src/main/java/org/ssssssss/script/parsing/ast/statement/Throw.java b/src/main/java/org/ssssssss/script/parsing/ast/statement/Throw.java index 3788b08..5bee2fe 100644 --- a/src/main/java/org/ssssssss/script/parsing/ast/statement/Throw.java +++ b/src/main/java/org/ssssssss/script/parsing/ast/statement/Throw.java @@ -22,6 +22,7 @@ public class Throw extends Node { @Override public void compile(MagicScriptCompiler compiler) { + // throw MagicScriptRuntimeException.create(expr); compiler.visit(expression) .invoke(INVOKESTATIC, MagicScriptRuntimeException.class, "create", MagicScriptRuntimeException.class, Object.class) .insn(ATHROW); diff --git a/src/main/java/org/ssssssss/script/parsing/ast/statement/TryStatement.java b/src/main/java/org/ssssssss/script/parsing/ast/statement/TryStatement.java index e48ef94..a890002 100644 --- a/src/main/java/org/ssssssss/script/parsing/ast/statement/TryStatement.java +++ b/src/main/java/org/ssssssss/script/parsing/ast/statement/TryStatement.java @@ -30,6 +30,7 @@ public class TryStatement extends Node { public void visitMethod(MagicScriptCompiler compiler) { tryResources.forEach(it -> it.visitMethod(compiler)); } + @Override public void compile(MagicScriptCompiler compiler) { tryResources.forEach(it -> compiler.load(it.getVarIndex()).invoke(INVOKESTATIC, TryStatement.class, "autoClose", void.class, Object.class)); @@ -55,57 +56,103 @@ public class TryStatement extends Node { @Override public void compile(MagicScriptCompiler compiler) { - Label l0 = new Label(); - Label l1 = new Label(); - Label l2 = new Label(); - Label l3 = new Label(); - Label l4 = new Label(); - Label l5 = new Label(); - Label end = new Label(); boolean hasCatch = exceptionVarNode != null; boolean hasFinally = !finallyBlock.isEmpty(); - if (hasFinally) { // try + catch + finally - compiler.putFinallyBlock(finallyBlock); - } - compiler.label(l0) - .compile(tryResources) - .compile(tryBlock) // try - .label(l1); if (hasFinally) { - compiler.compile(finallyBlock) - .jump(GOTO, end); // 跳转至结束 + compileWithFinally(compiler, hasCatch); + } else { + compileWithoutFinally(compiler, hasCatch); } - compiler.label(l2) // catch MagicExitException - .store(3) -// .compile(this.autoClose) - .load3() - .insn(ATHROW); // throw e - if(hasCatch){ - compiler.label(l3) + } + + private void compileWithFinally(MagicScriptCompiler compiler, boolean hasCatch) { + Label tryStart = new Label(); + Label tryEnd = new Label(); + Label catchMagicExitException = new Label(); + Label catchThrowable = new Label(); + Label catchEnd = new Label(); + Label finallyStart = new Label(); + Label finallyThrowStart = new Label(); + Label end = new Label(); + // try中如果有return语句,需要取这里压入的finallyBlock在return之前执行 + compiler.pushFinallyBlock(finallyBlock); + compiler.label(tryStart) + // tryResources + .compile(tryResources) + // try + .compile(tryBlock) + .label(tryEnd); + // 弹出自身压入的finallyBlock + compiler.popFinallyBlock(); + compiler + .label(finallyStart) + .compile(finallyBlock) + // 跳转至结束 + .jump(GOTO, end); + compiler.label(catchMagicExitException) + // catch (MagicExitException e) { throw e; } + .insn(ATHROW); + if (hasCatch) { + // catch中如果有return语句,需要取这里压入的finallyBlock在return之前执行 + compiler.pushFinallyBlock(finallyBlock); + compiler.label(catchThrowable) + // catch (Throwable e) { } + // 将异常变量存入variables .store(3) - .pre_store(exceptionVarNode) //保存异常变量前准备 + .pre_store(exceptionVarNode) .load3() .store(exceptionVarNode) - .compile(catchBlock); // 编译catch代码块 + .compile(catchBlock) + .label(catchEnd); + // 弹出自身压入的finallyBlock + compiler.popFinallyBlock(); + compiler + // 跳转至finally + .jump(GOTO, finallyStart); + } + compiler.label(finallyThrowStart) + .store(3) + .compile(finallyBlock) + .load3() + .insn(ATHROW); + compiler.label(end); + compiler.tryCatch(tryStart, tryEnd, finallyThrowStart, MagicExitException.class); + if (hasCatch) { + compiler.tryCatch(tryStart, tryEnd, catchThrowable, Throwable.class); + compiler.tryCatch(catchThrowable, catchEnd, finallyThrowStart, null); } - if (hasFinally) { - compiler.label(l5).compile(finallyBlock).jump(GOTO, end); // 跳转至结束 - compiler.label(l4) + compiler.tryCatch(tryStart, tryEnd, finallyThrowStart, null); + } + + private void compileWithoutFinally(MagicScriptCompiler compiler, boolean hasCatch) { + Label tryStart = new Label(); + Label tryEnd = new Label(); + Label catchMagicExitException = new Label(); + Label catchThrowable = new Label(); + compiler.label(tryStart) + // tryResources + .compile(tryResources) + // try + .compile(tryBlock) + .label(tryEnd); + compiler.label(catchMagicExitException) + // catch (MagicExitException e) { throw e; } + .insn(ATHROW); + if (hasCatch) { + compiler.label(catchThrowable) + // catch (Throwable e) { } + // 将异常变量存入variables .store(3) - .compile(finallyBlock) + .pre_store(exceptionVarNode) .load3() - .insn(ATHROW); - compiler.getFinallyBlock(); - } - compiler.label(end); - compiler.tryCatch(l0, l1, l2, MagicExitException.class); - if(hasCatch){ - compiler.tryCatch(l0, l1, l3, Throwable.class); + .store(exceptionVarNode) + .compile(catchBlock); } - if (hasFinally) { // try + catch + finally - compiler.tryCatch(l0, l1, l4, null); - compiler.tryCatch(l2, l5, l4, null); + + compiler.tryCatch(tryStart, tryEnd, catchMagicExitException, MagicExitException.class); + if (hasCatch) { + compiler.tryCatch(tryStart, tryEnd, catchThrowable, Throwable.class); } } } -- Gitee From 82c33dc3a281a741ac0531a3ef0c082df54e1ead Mon Sep 17 00:00:00 2001 From: aLIEz <121407312@qq.com> Date: Fri, 26 Aug 2022 13:01:45 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../script/TryCatchFinallyReturnTest.java | 59 +++++++++++++++++++ .../grammar/try_catch_finally_return_3.ms | 12 ++++ .../grammar/try_catch_finally_return_5.ms | 13 ++++ 3 files changed, 84 insertions(+) create mode 100644 src/test/java/org/ssssssss/script/TryCatchFinallyReturnTest.java create mode 100644 src/test/resources/grammar/try_catch_finally_return_3.ms create mode 100644 src/test/resources/grammar/try_catch_finally_return_5.ms diff --git a/src/test/java/org/ssssssss/script/TryCatchFinallyReturnTest.java b/src/test/java/org/ssssssss/script/TryCatchFinallyReturnTest.java new file mode 100644 index 0000000..27fbc65 --- /dev/null +++ b/src/test/java/org/ssssssss/script/TryCatchFinallyReturnTest.java @@ -0,0 +1,59 @@ +package org.ssssssss.script; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +public class TryCatchFinallyReturnTest extends BaseTest { + + @Test + public void finallyReturn() { + Assert.assertEquals(finallyReturnJAVA().toString(), execute("grammar/try_catch_finally_return_3.ms").toString()); + } + + private static List finallyReturnJAVA() { + int a = 1; + StringBuilder sb = new StringBuilder(); + try { + int b = a++ / 0; + } catch (Exception e) { + a++; + sb.append("a: " + a + " " + e + ", "); + int c = a++ / 0; + } finally { + sb.append("b: " + a++ + ", "); + return returnWarp(a, sb); + } + } + + @Test + public void tryNestedReturn() { + Assert.assertEquals(tryNestedReturnJAVA().toString(), execute("grammar/try_catch_finally_return_5.ms").toString()); + } + + private static List tryNestedReturnJAVA() { + int a = 1; + StringBuilder sb = new StringBuilder(); + try { + a++; + try { + a++; + return returnWarp(a, sb); + } finally { + sb.append("a: " + a++ + ", "); + } + } finally { + sb.append("b: " + a++ + ", "); + } + } + + private static List returnWarp(int a, StringBuilder sb) { + List ret = new ArrayList<>(2); + ret.add(a); + ret.add(sb); + return ret; + } + +} diff --git a/src/test/resources/grammar/try_catch_finally_return_3.ms b/src/test/resources/grammar/try_catch_finally_return_3.ms new file mode 100644 index 0000000..2f00c3a --- /dev/null +++ b/src/test/resources/grammar/try_catch_finally_return_3.ms @@ -0,0 +1,12 @@ +a = 1 +s = '' +try { + b= a++ / 0 +} catch(e) { + a++; + s = s + 'a: ' + a + ' ' + e + ', '; + c = a++ / 0 +} finally { + s = s + 'b: ' + a++ + ', '; + return [a, s] +} \ No newline at end of file diff --git a/src/test/resources/grammar/try_catch_finally_return_5.ms b/src/test/resources/grammar/try_catch_finally_return_5.ms new file mode 100644 index 0000000..5829c8a --- /dev/null +++ b/src/test/resources/grammar/try_catch_finally_return_5.ms @@ -0,0 +1,13 @@ +a = 1 +StringBuilder sb = new StringBuilder(); +try { + a++; + try { + a++; + return [a, sb]; + } finally { + sb.append('a: ' + a++ + ', '); + } +} finally { + sb.append('b: ' + a++ + ', '); +} -- Gitee From a7ec2f2cff583b2e9e7c0eccb5f7c0750cc739f9 Mon Sep 17 00:00:00 2001 From: aLIEz <121407312@qq.com> Date: Fri, 26 Aug 2022 14:26:59 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B:=20try=5Fcatch=5Ffinally=5Freturn=5F6.ms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../script/TryCatchFinallyReturnTest.java | 36 +++++++++++++++++++ .../grammar/try_catch_finally_return_6.ms | 28 +++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 src/test/resources/grammar/try_catch_finally_return_6.ms diff --git a/src/test/java/org/ssssssss/script/TryCatchFinallyReturnTest.java b/src/test/java/org/ssssssss/script/TryCatchFinallyReturnTest.java index 27fbc65..7c51daa 100644 --- a/src/test/java/org/ssssssss/script/TryCatchFinallyReturnTest.java +++ b/src/test/java/org/ssssssss/script/TryCatchFinallyReturnTest.java @@ -49,6 +49,42 @@ public class TryCatchFinallyReturnTest extends BaseTest { } } + @Test + public void tryFinallyNestedReturn() { + Assert.assertEquals(tryFinallyNestedReturnJAVA().toString(), execute("grammar/try_catch_finally_return_6.ms").toString()); + } + + private static List tryFinallyNestedReturnJAVA() { + int a = 1; + StringBuilder sb = new StringBuilder(); + try { + a++; + try { + a++; + try { + a++; + try { + a++; + sb.append("a: " + a++ + ", "); + return returnWarp(a, sb); + } finally { + sb.append("b: " + a++ + ", "); + return returnWarp(a, sb); + } + } finally { + sb.append("c: " + a++ + ", "); + return returnWarp(a, sb); + } + } finally { + sb.append("d: " + a++ + ", "); + return returnWarp(a, sb); + } + } finally { + sb.append("e: " + a++ + ", "); + return returnWarp(a, sb); + } + } + private static List returnWarp(int a, StringBuilder sb) { List ret = new ArrayList<>(2); ret.add(a); diff --git a/src/test/resources/grammar/try_catch_finally_return_6.ms b/src/test/resources/grammar/try_catch_finally_return_6.ms new file mode 100644 index 0000000..ec019f8 --- /dev/null +++ b/src/test/resources/grammar/try_catch_finally_return_6.ms @@ -0,0 +1,28 @@ +a = 1 +StringBuilder sb = new StringBuilder(); +try { + a++; + try { + a++; + try { + a++; + try { + a++; + sb.append('a: ' + a++ + ', '); + return [a, sb]; + } finally { + sb.append('b: ' + a++ + ', '); + return [a, sb]; + } + } finally { + sb.append('c: ' + a++ + ', '); + return [a, sb]; + } + } finally { + sb.append('d: ' + a++ + ', '); + return [a, sb]; + } +} finally { + sb.append('e: ' + a++ + ', '); + return [a, sb]; +} -- Gitee From 28e507842d32d6548288fb721c45e236af504129 Mon Sep 17 00:00:00 2001 From: aLIEz <121407312@qq.com> Date: Fri, 26 Aug 2022 14:28:19 +0800 Subject: [PATCH 4/4] fix: try_catch_finally_return_6.ms --- .../script/parsing/ast/statement/Return.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/ssssssss/script/parsing/ast/statement/Return.java b/src/main/java/org/ssssssss/script/parsing/ast/statement/Return.java index 39a444f..837058d 100644 --- a/src/main/java/org/ssssssss/script/parsing/ast/statement/Return.java +++ b/src/main/java/org/ssssssss/script/parsing/ast/statement/Return.java @@ -4,6 +4,8 @@ import org.ssssssss.script.compile.MagicScriptCompiler; import org.ssssssss.script.parsing.Span; import org.ssssssss.script.parsing.ast.Node; +import java.util.Deque; +import java.util.LinkedList; import java.util.List; public class Return extends Node { @@ -41,8 +43,9 @@ public class Return extends Node { if (finallyBlock != null) { // 编译return_1的finallyBlock_1,此时若finallyBlock_1中存在return_2,不会再在return_2中编译finallyBlock_1 compiler.compile(finallyBlock); - // 编译栈中剩余的finallyBlock - for (List fb : compiler.finallyBlockStack()) { + // 编译栈中剩余的finallyBlock。这里需要拷贝到新的Deque,防止编译finallyBlock的return时抛出ConcurrentModificationException + Deque> finallyBlockStack = new LinkedList<>(compiler.finallyBlockStack()); + for (List fb : finallyBlockStack) { compiler.compile(fb); } // 压入finallyBlock_1,供try-catch-finally其他地方的return再次编译finallyBlock_1 @@ -59,8 +62,9 @@ public class Return extends Node { compiler.store(3); // 编译return_1的finallyBlock_1,此时若finallyBlock_1中存在return_2,不会再在return_2中编译finallyBlock_1 compiler.compile(finallyBlock); - // 编译栈中剩余的finallyBlock - for (List fb : compiler.finallyBlockStack()) { + // 编译栈中剩余的finallyBlock。这里需要拷贝到新的Deque,防止编译finallyBlock的return时抛出ConcurrentModificationException + Deque> finallyBlockStack = new LinkedList<>(compiler.finallyBlockStack()); + for (List fb : finallyBlockStack) { compiler.compile(fb); } // 加载返回结果 -- Gitee