use of dyvilx.tools.asm.Label in project Dyvil by Dyvil.
the class MatchExpr method generateSwitch.
private void generateSwitch(MethodWriter writer, Object frameType) throws BytecodeException {
MatchCase defaultCase = null;
Label defaultLabel = null;
int cases = 0;
// the minimum int
int low = Integer.MAX_VALUE;
// the maximum int
int high = Integer.MIN_VALUE;
// Do we need to store the value in a variable (for string equality checks later)
boolean switchVar = false;
// generated.
for (int i = 0; i < this.caseCount; i++) {
MatchCase matchCase = this.cases[i];
Pattern pattern = matchCase.pattern;
if (switchVar || pattern.switchCheck()) {
switchVar = true;
}
if (pattern.isExhaustive()) {
defaultCase = matchCase;
defaultLabel = new Label();
continue;
}
int min = pattern.minValue();
if (min < low) {
low = min;
}
int max = pattern.maxValue();
if (max > high) {
high = max;
}
cases += pattern.subPatterns();
}
// Check if a match error should be generated - Non-exhaustive pattern
// and no default label
final Label endLabel = new Label();
Label matchErrorLabel = null;
if (!this.exhaustive) {
// Need a variable for MatchError
switchVar = true;
matchErrorLabel = new Label();
if (defaultLabel == null) {
defaultLabel = matchErrorLabel;
}
} else if (defaultLabel == null) {
// Exhaustive pattern - default label is end label
defaultLabel = endLabel;
}
final boolean expr = frameType != null;
// Write the value
final IType matchedType = this.matchedValue.getType();
final int localCount = writer.localCount();
final int varIndex;
if (switchVar) {
varIndex = this.matchedValue.writeStoreLoad(writer, null);
} else {
varIndex = -1;
this.matchedValue.writeExpression(writer, null);
}
if (Types.isSuperClass(Types.ENUM, matchedType)) {
// x.name().hashCode()
writer.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Enum", "name", "()Ljava/lang/String;", false);
writer.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode", "()I", false);
} else if (Types.isSuperClass(Types.STRING, matchedType)) {
// x.hashCode()
writer.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode", "()I", false);
} else if (matchedType.getAnnotation(Types.SWITCHOPTIMIZED_CLASS) != null) {
// x.getClass().getName().hashCode()
writer.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
writer.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
writer.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode", "()I", false);
}
final int localCountInner = writer.localCount();
final KeyCache keyCache = new KeyCache(cases);
// Third run: fill the Key Cache
for (int i = 0; i < this.caseCount; i++) {
final MatchCase matchCase = this.cases[i];
final Pattern pattern = matchCase.pattern;
final int subPatterns = pattern.subPatterns();
for (int j = 0; j < subPatterns; j++) {
final Pattern subPattern = pattern.subPattern(j);
if (subPattern.isExhaustive()) {
continue;
}
final int switchValue = subPattern.switchValue();
keyCache.add(switchValue, matchCase, subPattern);
}
}
final Collection<KeyCache.Entry> entries = keyCache.uniqueEntries();
// Generate switch target labels
for (KeyCache.Entry topEntry : entries) {
for (KeyCache.Entry entry = topEntry; entry != null; entry = entry.next) {
entry.switchLabel = new Label();
}
}
// Choose and generate the appropriate instruction
if (useTableSwitch(low, high, cases)) {
this.writeTableSwitch(writer, entries, defaultLabel, low, high);
} else {
this.writeLookupSwitch(writer, entries, defaultLabel, cases);
}
// Fourth run: generate the target labels
for (KeyCache.Entry topEntry : entries) {
KeyCache.Entry entry = topEntry;
final int key = entry.key;
do {
final KeyCache.Entry next = entry.next;
final MatchCase matchCase = entry.matchCase;
final Pattern pattern = entry.pattern;
Label elseLabel;
if (next != null && next.key == key) {
elseLabel = next.switchLabel;
if (elseLabel == null) {
elseLabel = next.switchLabel = new Label();
}
} else {
elseLabel = defaultLabel;
}
writer.visitTargetLabel(entry.switchLabel);
if (pattern.switchCheck()) {
pattern.writeJumpOnMismatch(writer, varIndex, elseLabel);
}
if (matchCase.condition != null) {
matchCase.condition.writeInvJump(writer, elseLabel);
}
this.writeAction(writer, expr, frameType, matchCase.action);
writer.resetLocals(localCountInner);
if (!writer.hasReturn()) {
writer.visitJumpInsn(Opcodes.GOTO, endLabel);
}
entry = next;
} while (entry != null && entry.key == key);
}
// Default Case
if (defaultCase != null) {
writer.visitTargetLabel(defaultLabel);
if (defaultCase.pattern.switchCheck()) {
defaultCase.pattern.writeJumpOnMismatch(writer, varIndex, matchErrorLabel);
}
if (defaultCase.condition != null) {
defaultCase.condition.writeInvJump(writer, matchErrorLabel);
}
this.writeAction(writer, expr, frameType, defaultCase.action);
writer.resetLocals(localCountInner);
if (!writer.hasReturn()) {
writer.visitJumpInsn(Opcodes.GOTO, endLabel);
}
}
// Generate Match Error
if (matchErrorLabel != null) {
writer.visitLabel(matchErrorLabel);
this.writeMatchError(writer, varIndex, matchedType);
}
writer.visitLabel(endLabel);
writer.resetLocals(localCount);
}
use of dyvilx.tools.asm.Label in project Dyvil by Dyvil.
the class Field method write.
@Override
public void write(ClassWriter writer) throws BytecodeException {
final long flags = ModifierUtil.getFlags(this);
final int modifiers = ModifierUtil.getJavaModifiers(flags);
final String name = this.getInternalName();
final String descriptor = this.getDescriptor();
final String signature = this.getType().needsSignature() ? this.getSignature() : null;
final Object value = this.getObjectValue();
final FieldVisitor fieldVisitor = writer.visitField(modifiers, name, descriptor, signature, value);
this.writeAnnotations(fieldVisitor, flags);
fieldVisitor.visitEnd();
if (this.property != null) {
this.property.write(writer);
}
if (!this.hasModifier(Modifiers.LAZY)) {
return;
}
final String lazyName = name + "$lazy";
final String ownerClass = this.enclosingClass.getInternalName();
final boolean isStatic = (flags & Modifiers.STATIC) != 0;
writer.visitField(isStatic ? Modifiers.PRIVATE | Modifiers.STATIC : Modifiers.PRIVATE, lazyName, "Z", null, null);
final MethodWriter access = new MethodWriterImpl(writer, writer.visitMethod(modifiers, lazyName, "()" + descriptor, null, null));
access.visitCode();
// Get the $lazy flag
int getOpcode;
if (!isStatic) {
access.setLocalType(0, ownerClass);
access.visitVarInsn(Opcodes.ALOAD, 0);
access.visitFieldInsn(getOpcode = Opcodes.GETFIELD, ownerClass, lazyName, "Z");
} else {
access.visitFieldInsn(getOpcode = Opcodes.GETSTATIC, ownerClass, lazyName, "Z");
}
Label label = new Label();
final int returnOpcode = this.type.getReturnOpcode();
// if (this.fieldName$lazy) {
access.visitJumpInsn(Opcodes.IFEQ, label);
if (!isStatic) {
access.visitVarInsn(Opcodes.ALOAD, 0);
}
// this.fieldName ->
access.visitFieldInsn(getOpcode, ownerClass, name, descriptor);
// return
access.visitInsn(returnOpcode);
// }
access.visitTargetLabel(label);
if (!isStatic) {
access.visitVarInsn(Opcodes.ALOAD, 0);
access.visitInsn(Opcodes.DUP);
access.visitLdcInsn(1);
access.visitFieldInsn(Opcodes.PUTFIELD, ownerClass, lazyName, "Z");
this.value.writeExpression(access, this.type);
access.visitInsn(Opcodes.AUTO_DUP_X1);
access.visitFieldInsn(Opcodes.PUTFIELD, ownerClass, name, descriptor);
} else {
access.visitLdcInsn(1);
access.visitFieldInsn(Opcodes.PUTSTATIC, ownerClass, lazyName, "Z");
this.value.writeExpression(access, this.type);
access.visitInsn(Opcodes.AUTO_DUP);
access.visitFieldInsn(Opcodes.PUTSTATIC, ownerClass, name, descriptor);
}
access.visitInsn(returnOpcode);
access.visitEnd();
}
use of dyvilx.tools.asm.Label in project Dyvil by Dyvil.
the class AndOperator method writeJump.
@Override
public void writeJump(MethodWriter writer, Label dest) throws BytecodeException {
// jump if both conditions are met
final Label label = new Label();
this.left.writeInvJump(writer, label);
this.right.writeJump(writer, dest);
writer.visitLabel(label);
}
use of dyvilx.tools.asm.Label in project Dyvil by Dyvil.
the class OrOperator method writeInvJump.
@Override
public void writeInvJump(MethodWriter writer, Label dest) throws BytecodeException {
// !(x || y) <=> !x && !y
// jump if both conditions are not met
final Label label = new Label();
this.left.writeJump(writer, label);
this.right.writeInvJump(writer, dest);
writer.visitLabel(label);
}
use of dyvilx.tools.asm.Label in project Dyvil by Dyvil.
the class ObjectClassMetadata method write.
@Override
public void write(ClassWriter writer) throws BytecodeException {
super.write(writer);
String internalName = this.theClass.getInternalName();
if ((this.members & TOSTRING) == 0) {
// Generate a toString() method that simply returns the name of this
// object type.
MethodWriterImpl mw = new MethodWriterImpl(writer, writer.visitMethod(Modifiers.PUBLIC, "toString", "()Ljava/lang/String;", null, null));
mw.visitCode();
mw.setThisType(internalName);
mw.visitLdcInsn(this.theClass.getName().unqualified);
mw.visitInsn(Opcodes.ARETURN);
mw.visitEnd(Types.STRING);
}
if ((this.members & EQUALS) == 0) {
// Generate an equals(Object) method that compares the objects for
// identity
MethodWriterImpl mw = new MethodWriterImpl(writer, writer.visitMethod(Modifiers.PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null));
mw.visitCode();
mw.setThisType(internalName);
mw.visitParameter(1, "obj", Types.ANY, 0);
mw.visitVarInsn(Opcodes.ALOAD, 0);
mw.visitVarInsn(Opcodes.ALOAD, 1);
Label label = new Label();
mw.visitJumpInsn(Opcodes.IF_ACMPNE, label);
mw.visitLdcInsn(1);
mw.visitInsn(Opcodes.IRETURN);
mw.visitLabel(label);
mw.visitLdcInsn(0);
mw.visitInsn(Opcodes.IRETURN);
mw.visitEnd();
}
if ((this.members & HASHCODE) == 0) {
MethodWriterImpl mw = new MethodWriterImpl(writer, writer.visitMethod(Modifiers.PUBLIC, "hashCode", "()I", null, null));
mw.visitCode();
mw.setThisType(internalName);
mw.visitLdcInsn(internalName.hashCode());
mw.visitInsn(Opcodes.IRETURN);
mw.visitEnd();
}
if ((this.members & READ_RESOLVE) == 0) {
MethodWriterImpl mw = new MethodWriterImpl(writer, writer.visitMethod(Modifiers.PRIVATE | Modifiers.SYNTHETIC, "readResolve", "()Ljava/lang/Object;", null, null));
writeResolveBody(mw, internalName);
}
if ((this.members & WRITE_REPLACE) == 0) {
MethodWriterImpl mw = new MethodWriterImpl(writer, writer.visitMethod(Modifiers.PRIVATE | Modifiers.SYNTHETIC, "writeReplace", "()Ljava/lang/Object;", null, null));
writeResolveBody(mw, internalName);
}
}
Aggregations