package org.develnext.jphp.core.compiler.jvm.statement.expr.value;
import org.develnext.jphp.core.compiler.jvm.Constants;
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.expr.BaseExprCompiler;
import org.develnext.jphp.core.tokenizer.token.expr.value.ClosureStmtToken;
import org.develnext.jphp.core.tokenizer.token.expr.value.macro.ClassMacroToken;
import org.develnext.jphp.core.tokenizer.token.stmt.ArgumentStmtToken;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
import php.runtime.Memory;
import php.runtime.env.Environment;
import php.runtime.lang.IObject;
import php.runtime.memory.ObjectMemory;
import php.runtime.memory.StringMemory;
import php.runtime.reflection.ClassEntity;
import php.runtime.reflection.helper.ClosureEntity;
import java.util.Collection;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Opcodes.AASTORE;
public class ClosureValueCompiler extends BaseExprCompiler<ClosureStmtToken> {
public ClosureValueCompiler(ExpressionStmtCompiler exprCompiler) {
super(exprCompiler);
}
protected void writePushUses(Collection<ArgumentStmtToken> parameters){
if (parameters.isEmpty()){
add(new InsnNode(ACONST_NULL));
expr.stackPush(Memory.Type.REFERENCE);
return;
}
expr.writePushSmallInt(parameters.size());
add(new TypeInsnNode(ANEWARRAY, Type.getInternalName(Memory.class)));
expr.stackPop();
expr.stackPush(Memory.Type.REFERENCE);
int i = 0;
for(ArgumentStmtToken param : parameters){
expr.writePushDup();
expr.writePushSmallInt(i);
LocalVariable local = method.getLocalVariable(param.getName().getName());
if (local == null)
expr.writePushNull();
else
expr.writeVarLoad(local);
if (!param.isReference())
expr.writePopBoxing(true);
add(new InsnNode(AASTORE));
expr.stackPop();
expr.stackPop();
expr.stackPop();
i++;
}
}
protected void writeContext() {
if (method.clazz.getClassContext() != null && method.clazz.getClassContext().isTrait()) {
expr.writePushEnv();
expr.writeSysDynamicCall(Environment.class, "__getMacroClass", Memory.class);
expr.writePopString();
} else {
if (method.clazz.getClassContext() != null) {
expr.writePushConstString(method.clazz.getClassContext().getFulledName());
} else {
if (method.clazz.isSystem()) {
expr.writePushConstNull();
} else {
expr.writePushConstString(method.clazz.getEntity().getName());
}
}
}
}
@Override
public void write(ClosureStmtToken closure, boolean returnValue) {
if (returnValue){
ClosureEntity entity = compiler.getModule().findClosure( closure.getId() );
boolean thisExists = closure.getFunction().isThisExists();
boolean staticExists = closure.getFunction().isStaticExists();
if (closure.getFunction().getUses().isEmpty() && !thisExists && !staticExists
&& closure.getFunction().getStaticLocal().isEmpty()){
expr.writePushEnv();
expr.writePushConstString(compiler.getModule().getInternalName());
expr.writePushConstInt((int) entity.getId());
expr.writeSysDynamicCall(
Environment.class, "__getSingletonClosure", Memory.class, String.class, Integer.TYPE
);
} else {
add(new TypeInsnNode(NEW, entity.getInternalName()));
expr.stackPush(Memory.Type.REFERENCE);
expr.writePushDup();
expr.writePushEnv();
expr.writePushEnv();
expr.writePushConstString(compiler.getModule().getInternalName());
expr.writePushConstInt((int) entity.getId());
expr.writeSysDynamicCall(Environment.class, "__getClosure", ClassEntity.class, String.class, Integer.TYPE);
if (thisExists)
expr.writePushThis();
else
expr.writePushNull();
writeContext();
writePushUses(closure.getFunction().getUses());
add(new MethodInsnNode(
INVOKESPECIAL, entity.getInternalName(), Constants.INIT_METHOD,
Type.getMethodDescriptor(
Type.getType(void.class),
Type.getType(Environment.class),
Type.getType(ClassEntity.class),
Type.getType(Memory.class),
Type.getType(String.class),
Type.getType(Memory[].class)
),
false
));
expr.stackPop();
expr.stackPop();
expr.stackPop();
expr.stackPop();
expr.stackPop();
expr.stackPop();
expr.writeSysStaticCall(ObjectMemory.class, "valueOf", Memory.class, IObject.class);
}
expr.setStackPeekAsImmutable();
}
}
}