package org.develnext.jphp.core.compiler.jvm.statement.expr;
import org.develnext.jphp.core.compiler.jvm.misc.LocalVariable;
import org.develnext.jphp.core.compiler.jvm.statement.ExpressionStmtCompiler;
import org.develnext.jphp.core.compiler.jvm.statement.MethodStmtCompiler;
import org.develnext.jphp.core.tokenizer.token.stmt.BodyStmtToken;
import org.develnext.jphp.core.tokenizer.token.stmt.CatchStmtToken;
import org.develnext.jphp.core.tokenizer.token.stmt.TryStmtToken;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import php.runtime.Memory;
import php.runtime.env.Environment;
import php.runtime.lang.BaseException;
import php.runtime.lang.exception.BaseBaseException;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Opcodes.GOTO;
public class TryCatchCompiler extends BaseStatementCompiler<TryStmtToken> {
public TryCatchCompiler(ExpressionStmtCompiler exprCompiler) {
super(exprCompiler);
}
@Override
public void write(TryStmtToken token) {
if (token.getBody() == null || token.getBody().getInstructions().isEmpty()){
if (token.getFinally() != null)
expr.write(BodyStmtToken.class, token.getFinally());
return;
}
expr.writeDefineVariables(token.getLocal());
LabelNode tryStart = expr.writeLabel(node, token.getMeta().getStartLine());
LabelNode tryEnd = new LabelNode();
LabelNode catchStart = new LabelNode();
LabelNode catchEnd = new LabelNode();
LabelNode returnLabel = new LabelNode();
method.node.tryCatchBlocks.add(0,
new TryCatchBlockNode(tryStart, tryEnd, catchStart, Type.getInternalName(BaseBaseException.class))
);
if (token.getFinally() != null) {
method.getTryStack().push(new MethodStmtCompiler.TryCatchItem(token, returnLabel));
}
expr.write(BodyStmtToken.class, token.getBody());
if (token.getFinally() != null) {
method.getTryStack().pop();
}
add(tryEnd);
add(new JumpInsnNode(GOTO, catchEnd));
add(catchStart);
LocalVariable exception = method.addLocalVariable(
"~catch~" + method.nextStatementIndex(BaseException.class), catchStart, BaseBaseException.class
);
exception.setEndLabel(catchEnd);
expr.makeVarStore(exception);
LabelNode nextCatch = null;
int i = 0, size = token.getCatches().size();
LocalVariable local = null;
LabelNode catchFail = new LabelNode();
for(CatchStmtToken _catch : token.getCatches()) {
if (nextCatch != null) {
add(nextCatch);
}
if (i == size - 1) {
nextCatch = catchFail;
} else {
nextCatch = new LabelNode();
}
local = method.getLocalVariable(_catch.getVariable().getName());
expr.writePushEnv();
expr.writeVarLoad(exception);
expr.writePushConstString(_catch.getException().toName());
expr.writePushConstString(_catch.getException().toName().toLowerCase());
expr.writeSysDynamicCall(
Environment.class, "__throwCatch", Memory.class, BaseBaseException.class, String.class, String.class
);
expr.writeVarAssign(local, _catch.getVariable(), true, false);
expr.writePopBoolean();
add(new JumpInsnNode(IFEQ, nextCatch));
expr.stackPop();
expr.write(BodyStmtToken.class, _catch.getBody());
add(new JumpInsnNode(GOTO, catchEnd));
i++;
}
add(catchFail);
if (token.getFinally() != null){
expr.write(BodyStmtToken.class, token.getFinally());
}
/*if (method.getTryStack().empty()) {
expr.writePushEnv();
expr.writeVarLoad(exception);
expr.writeSysDynamicCall(Environment.class, "__throwFailedCatch", void.class, BaseException.class);
} else {*/
expr.makeVarLoad(exception);
add(new InsnNode(ATHROW));
//}
add(catchEnd);
if (token.getFinally() != null){
LabelNode skip = new LabelNode();
add(new JumpInsnNode(GOTO, skip));
// finally for return
add(returnLabel);
expr.write(BodyStmtToken.class, token.getFinally());
if (method.getTryStack().empty()){
// all finally blocks are done
LocalVariable retVar = method.getOrAddLocalVariable("~result~", null, Memory.class);
expr.writeVarLoad(retVar);
add(new InsnNode(ARETURN));
expr.stackPop();
} else {
// goto next finally block
add(new JumpInsnNode(GOTO, method.getTryStack().peek().getReturnLabel()));
}
add(skip);
// other finally
expr.write(BodyStmtToken.class, token.getFinally());
}
expr.writeUndefineVariables(token.getLocal());
method.prevStatementIndex(BaseBaseException.class);
}
}