Search in sources :

Example 1 with Label

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);
}
Also used : Pattern(dyvilx.tools.compiler.ast.pattern.Pattern) Label(dyvilx.tools.asm.Label) IType(dyvilx.tools.compiler.ast.type.IType)

Example 2 with Label

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();
}
Also used : MethodWriterImpl(dyvilx.tools.compiler.backend.MethodWriterImpl) MethodWriter(dyvilx.tools.compiler.backend.MethodWriter) Label(dyvilx.tools.asm.Label) FieldVisitor(dyvilx.tools.asm.FieldVisitor)

Example 3 with Label

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);
}
Also used : Label(dyvilx.tools.asm.Label)

Example 4 with 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);
}
Also used : Label(dyvilx.tools.asm.Label)

Example 5 with 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);
    }
}
Also used : MethodWriterImpl(dyvilx.tools.compiler.backend.MethodWriterImpl) Label(dyvilx.tools.asm.Label)

Aggregations

Label (dyvilx.tools.asm.Label)16 MethodWriterImpl (dyvilx.tools.compiler.backend.MethodWriterImpl)4 IType (dyvilx.tools.compiler.ast.type.IType)3 MethodWriter (dyvilx.tools.compiler.backend.MethodWriter)3 Nullable (dyvil.annotation.internal.Nullable)1 HashSet (dyvil.collection.mutable.HashSet)1 FieldVisitor (dyvilx.tools.asm.FieldVisitor)1 ParameterList (dyvilx.tools.compiler.ast.parameter.ParameterList)1 Pattern (dyvilx.tools.compiler.ast.pattern.Pattern)1 ArrayType (dyvilx.tools.compiler.ast.type.compound.ArrayType)1