use of org.objectweb.asm.tree.JumpInsnNode in project MinecraftForge by MinecraftForge.
the class EventSubscriptionTransformer method buildEvents.
private boolean buildEvents(ClassNode classNode) throws Exception {
// Yes, this recursively loads classes until we get this base class. THIS IS NOT A ISSUE. Coremods should handle re-entry just fine.
// If they do not this a COREMOD issue NOT a Forge/LaunchWrapper issue.
Class<?> parent = this.getClass().getClassLoader().loadClass(classNode.superName.replace('/', '.'));
if (!Event.class.isAssignableFrom(parent)) {
return false;
}
//Class<?> listenerListClazz = Class.forName("net.minecraftforge.fml.common.eventhandler.ListenerList", false, getClass().getClassLoader());
Type tList = Type.getType("Lnet/minecraftforge/fml/common/eventhandler/ListenerList;");
boolean edited = false;
boolean hasSetup = false;
boolean hasGetListenerList = false;
boolean hasDefaultCtr = false;
boolean hasCancelable = false;
boolean hasResult = false;
String voidDesc = Type.getMethodDescriptor(VOID_TYPE);
String boolDesc = Type.getMethodDescriptor(BOOLEAN_TYPE);
String listDesc = tList.getDescriptor();
String listDescM = Type.getMethodDescriptor(tList);
for (MethodNode method : classNode.methods) {
if (method.name.equals("setup") && method.desc.equals(voidDesc) && (method.access & ACC_PROTECTED) == ACC_PROTECTED)
hasSetup = true;
if ((method.access & ACC_PUBLIC) == ACC_PUBLIC) {
if (method.name.equals("getListenerList") && method.desc.equals(listDescM))
hasGetListenerList = true;
if (method.name.equals("isCancelable") && method.desc.equals(boolDesc))
hasCancelable = true;
if (method.name.equals("hasResult") && method.desc.equals(boolDesc))
hasResult = true;
}
if (method.name.equals("<init>") && method.desc.equals(voidDesc))
hasDefaultCtr = true;
}
if (classNode.visibleAnnotations != null) {
for (AnnotationNode node : classNode.visibleAnnotations) {
if (!hasResult && node.desc.equals("Lnet/minecraftforge/fml/common/eventhandler/Event$HasResult;")) {
/* Add:
* public boolean hasResult()
* {
* return true;
* }
*/
MethodNode method = new MethodNode(ACC_PUBLIC, "hasResult", boolDesc, null, null);
method.instructions.add(new InsnNode(ICONST_1));
method.instructions.add(new InsnNode(IRETURN));
classNode.methods.add(method);
edited = true;
} else if (!hasCancelable && node.desc.equals("Lnet/minecraftforge/fml/common/eventhandler/Cancelable;")) {
/* Add:
* public boolean isCancelable()
* {
* return true;
* }
*/
MethodNode method = new MethodNode(ACC_PUBLIC, "isCancelable", boolDesc, null, null);
method.instructions.add(new InsnNode(ICONST_1));
method.instructions.add(new InsnNode(IRETURN));
classNode.methods.add(method);
edited = true;
}
}
}
if (hasSetup) {
if (!hasGetListenerList)
throw new RuntimeException("Event class defines setup() but does not define getListenerList! " + classNode.name);
else
return edited;
}
Type tSuper = Type.getType(classNode.superName);
//Add private static ListenerList LISTENER_LIST
classNode.fields.add(new FieldNode(ACC_PRIVATE | ACC_STATIC, "LISTENER_LIST", listDesc, null, null));
/*Add:
* public <init>()
* {
* super();
* }
*/
if (!hasDefaultCtr) {
MethodNode method = new MethodNode(ACC_PUBLIC, "<init>", voidDesc, null, null);
method.instructions.add(new VarInsnNode(ALOAD, 0));
method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "<init>", voidDesc, false));
method.instructions.add(new InsnNode(RETURN));
classNode.methods.add(method);
}
/*Add:
* protected void setup()
* {
* super.setup();
* if (LISTENER_LIST != NULL)
* {
* return;
* }
* LISTENER_LIST = new ListenerList(super.getListenerList());
* }
*/
MethodNode method = new MethodNode(ACC_PROTECTED, "setup", voidDesc, null, null);
method.instructions.add(new VarInsnNode(ALOAD, 0));
method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "setup", voidDesc, false));
method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", listDesc));
LabelNode initListener = new LabelNode();
method.instructions.add(new JumpInsnNode(IFNULL, initListener));
method.instructions.add(new InsnNode(RETURN));
method.instructions.add(initListener);
method.instructions.add(new FrameNode(F_SAME, 0, null, 0, null));
method.instructions.add(new TypeInsnNode(NEW, tList.getInternalName()));
method.instructions.add(new InsnNode(DUP));
method.instructions.add(new VarInsnNode(ALOAD, 0));
method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tSuper.getInternalName(), "getListenerList", listDescM, false));
method.instructions.add(new MethodInsnNode(INVOKESPECIAL, tList.getInternalName(), "<init>", getMethodDescriptor(VOID_TYPE, tList), false));
method.instructions.add(new FieldInsnNode(PUTSTATIC, classNode.name, "LISTENER_LIST", listDesc));
method.instructions.add(new InsnNode(RETURN));
classNode.methods.add(method);
/*Add:
* public ListenerList getListenerList()
* {
* return this.LISTENER_LIST;
* }
*/
method = new MethodNode(ACC_PUBLIC, "getListenerList", listDescM, null, null);
method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, "LISTENER_LIST", listDesc));
method.instructions.add(new InsnNode(ARETURN));
classNode.methods.add(method);
return true;
}
use of org.objectweb.asm.tree.JumpInsnNode in project jphp by jphp-compiler.
the class IfElseCompiler method writeBody.
private void writeBody(IfStmtToken token) {
LabelNode end = new LabelNode();
LabelNode elseL = new LabelNode();
expr.writePopBoolean();
add(new JumpInsnNode(IFEQ, token.getElseBody() != null ? elseL : end));
expr.stackPop();
if (token.getBody() != null) {
expr.write(token.getBody());
}
if (token.getElseBody() != null) {
add(new JumpInsnNode(GOTO, end));
add(elseL);
expr.write(token.getElseBody());
}
add(end);
add(new LineNumberNode(token.getMeta().getEndLine(), end));
}
use of org.objectweb.asm.tree.JumpInsnNode in project jphp by jphp-compiler.
the class DoCompiler method write.
@Override
public void write(DoStmtToken token) {
expr.writeDefineVariables(token.getLocal());
LabelNode start = expr.writeLabel(node, token.getMeta().getStartLine());
LabelNode end = new LabelNode();
method.pushJump(end, start);
expr.write(token.getBody());
method.popJump();
expr.writeConditional(token.getCondition(), end);
add(new JumpInsnNode(GOTO, start));
add(end);
add(new LineNumberNode(token.getMeta().getEndLine(), end));
expr.writeUndefineVariables(token.getLocal());
}
use of org.objectweb.asm.tree.JumpInsnNode in project jphp by jphp-compiler.
the class ReturnCompiler method write.
@Override
public void write(ReturnStmtToken token) {
boolean isGeneratorReturn = false;
if (token.getValue() != null && method.getGeneratorEntity() != null) {
isGeneratorReturn = true;
expr.writeVarLoad(LocalVariable.THIS);
expr.writePushEnv();
expr.writePushTraceInfo(token);
/*env.error(
token.toTraceInfo(compiler.getContext()),
ErrorType.E_ERROR,
"Generators cannot return values using \"return\""
);*/
}
Memory result = token.isEmpty() ? Memory.UNDEFINED : Memory.NULL;
boolean isImmutable = method.getEntity().isImmutable();
if (token.getValue() != null) {
result = expr.writeExpression(token.getValue(), true, true);
if (methodStatement.getReturnHintType() == HintType.VOID) {
String suffix = result != null && result.isNull() ? " (did you mean \"return;\" instead of \"return null;\"?)" : "";
env.error(token.toTraceInfo(compiler.getContext()), ErrorType.E_ERROR, "A void function must not return a value" + suffix);
}
}
if (result != null) {
if (methodStatement.getReturnHintType() == HintType.VOID) {
if (methodStatement.isReturnOptional()) {
env.error(token.toTraceInfo(compiler.getContext()), ErrorType.E_ERROR, "Void type cannot be nullable");
}
}
if (isImmutable) {
Memory r = method.getEntity().getResult();
if (r == null || r.isUndefined()) {
method.getEntity().setResult(result);
}
}
expr.writePushMemory(result);
} else {
method.getEntity().setImmutable(false);
}
if (expr.stackEmpty(false)) {
expr.writePushNull();
} else {
expr.writePopBoxing(false);
}
if (method.getEntity().isReturnReference()) {
expr.writePushDup();
expr.writePushEnv();
expr.writePushTraceInfo(token);
expr.writeSysStaticCall(InvokeHelper.class, "checkReturnReference", void.class, Memory.class, Environment.class, TraceInfo.class);
} else {
expr.writePopImmutable();
}
if (isGeneratorReturn) {
expr.writeSysDynamicCall(Generator.class, "setReturn", Memory.class, Environment.class, TraceInfo.class, Memory.class);
}
if (!method.getTryStack().empty()) {
LocalVariable variable = method.getLocalVariable("~result~");
if (variable == null) {
variable = method.addLocalVariable("~result~", null, Memory.class);
}
expr.writeVarStore(variable, false, false);
add(new JumpInsnNode(GOTO, method.getTryStack().peek().getReturnLabel()));
} else {
add(new InsnNode(ARETURN));
// removeStackFrame();
expr.stackPop();
}
}
use of org.objectweb.asm.tree.JumpInsnNode in project jphp by jphp-compiler.
the class SwitchCompiler method write.
@Override
public void write(SwitchStmtToken token) {
expr.writeDefineVariables(token.getLocal());
LabelNode l = new LabelNode();
LabelNode end = new LabelNode();
add(l);
LocalVariable switchValue = method.addLocalVariable("~switch~" + method.nextStatementIndex(Memory.class), l, Memory.class);
switchValue.setEndLabel(end);
LabelNode[][] jumps = new LabelNode[token.getCases().size() + 1][2];
int i = 0;
for (CaseStmtToken one : token.getCases()) {
// checkLabel, bodyLabel
jumps[i] = new LabelNode[] { new LabelNode(), new LabelNode() };
if (i == jumps.length - 1)
jumps[i] = new LabelNode[] { end, end };
i++;
}
jumps[jumps.length - 1] = new LabelNode[] { end, end };
method.pushJump(end, end);
expr.writeExpression(token.getValue(), true, false);
expr.writePopBoxing();
expr.writeVarStore(switchValue, false, false);
i = 0;
for (CaseStmtToken one : token.getCases()) {
// conditional
add(jumps[i][0]);
if (one.getConditional() != null) {
expr.writeVarLoad(switchValue);
expr.writeExpression(one.getConditional(), true, false);
expr.writeSysDynamicCall(Memory.class, "equal", Boolean.TYPE, expr.stackPeek().type.toClass());
add(new JumpInsnNode(IFEQ, jumps[i + 1][0]));
expr.stackPop();
}
// if is done...
add(new JumpInsnNode(GOTO, jumps[i][1]));
i++;
}
i = 0;
for (CaseStmtToken one : token.getCases()) {
add(jumps[i][1]);
expr.writeDefineVariables(one.getLocals());
expr.write(BodyStmtToken.class, one.getBody());
i++;
expr.writeUndefineVariables(one.getLocals());
}
method.popJump();
add(end);
add(new LineNumberNode(token.getMeta().getEndLine(), end));
method.prevStatementIndex(Memory.class);
expr.writeUndefineVariables(token.getLocal());
}
Aggregations