org.lanternpowered.server.transformer.data.FastValueContainerMethodVisitor.java Source code

Java tutorial

Introduction

Here is the source code for org.lanternpowered.server.transformer.data.FastValueContainerMethodVisitor.java

Source

/*
 * This file is part of LanternServer, licensed under the MIT License (MIT).
 *
 * Copyright (c) LanternPowered <https://www.lanternpowered.org>
 * Copyright (c) SpongePowered <https://www.spongepowered.org>
 * Copyright (c) contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the Software), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.lanternpowered.server.transformer.data;

import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.POP;

import org.lanternpowered.server.data.CompositeValueStoreHelper;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.TypePath;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

final class FastValueContainerMethodVisitor extends MethodVisitor {

    private static final Map<String, String[]> entries = new HashMap<>();

    static {
        for (Method method : CompositeValueStoreHelper.class.getDeclaredMethods()) {
            final int modifiers = method.getModifiers();
            if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
                continue;
            }
            final String desc1 = Type.getMethodDescriptor(method);

            // Replace the first argument, this was originally not present because
            // it was a non static method directly targeting the loaded target object
            String oldDesc = desc1.replaceFirst("Lorg/spongepowered/api/data/value/mutable/CompositeValueStore;",
                    "");
            final String desc2 = oldDesc;
            // Replace the boolean return type with the DataTransactionResult
            oldDesc = oldDesc.substring(0, oldDesc.length() - 1)
                    + "Lorg/spongepowered/api/data/DataTransactionResult;";

            entries.put(method.getName() + ';' + oldDesc, new String[] { desc1, desc2 });
        }
    }

    private int store;
    private int opcode;
    private String[] entry;
    private String owner;
    private String name;
    private String desc;
    private boolean itf;

    FastValueContainerMethodVisitor(MethodVisitor mv) {
        super(Opcodes.ASM5, mv);
    }

    private void reset() {
        if (this.entry != null) {
            super.visitMethodInsn(this.opcode, this.owner, this.name, this.desc, this.itf);
            this.entry = null;
        }
    }

    @Override
    public void visitParameter(String name, int access) {
        reset();
        super.visitParameter(name, access);
    }

    @Override
    public AnnotationVisitor visitAnnotationDefault() {
        reset();
        return super.visitAnnotationDefault();
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        reset();
        return super.visitAnnotation(desc, visible);
    }

    @Override
    public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
        reset();
        return super.visitTypeAnnotation(typeRef, typePath, desc, visible);
    }

    @Override
    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
        reset();
        return super.visitParameterAnnotation(parameter, desc, visible);
    }

    @Override
    public void visitAttribute(Attribute attr) {
        reset();
        super.visitAttribute(attr);
    }

    @Override
    public void visitCode() {
        reset();
        super.visitCode();
    }

    @Override
    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
        reset();
        super.visitFrame(type, nLocal, local, nStack, stack);
    }

    private void writeMethod() {
        if (this.store == 1) {
            // The store extends CompositeValueStore, we don't know about about ICompositeValueStore,
            // so we forward this to a helper method
            super.visitMethodInsn(INVOKESTATIC, "org/lanternpowered/server/data/CompositeValueStoreHelper",
                    this.name, this.entry[0], false);
        } else if (this.store == 2) {
            // The store extends ICompositeValueStore, we can use a direct method,
            // Fast methods names should always end with Fast
            super.visitMethodInsn(INVOKEVIRTUAL, this.owner, this.name + "Fast", this.entry[1], this.itf);
        } else {
            throw new IllegalStateException(this.store + "");
        }
        this.entry = null;
    }

    @Override
    public void visitInsn(int opcode) {
        // We have a winner, if the next operation is a POP, which discards the DataTransactionResult,
        // then we can replace this with a fast method.
        // Example code:
        /*
        final LanternEntity entity = new LanternItem(UUID.randomUUID());
        entity.offer(Keys.VELOCITY, Vector3d.ZERO);
         */
        // to
        /*
        final LanternEntity entity = new LanternItem(UUID.randomUUID());
        CompositeValueStoreHelper.offer(entity, Keys.VELOCITY, Vector3d.ZERO);
         */
        // or
        /*
        final LanternEntity entity = new LanternItem(UUID.randomUUID());
        entity.offerFast(Keys.VELOCITY, Vector3d.ZERO);
         */
        if (opcode == POP && this.entry != null) {
            writeMethod();
        } else {
            reset();
        }
        super.visitInsn(opcode);
    }

    @Override
    public void visitIntInsn(int opcode, int operand) {
        reset();
        super.visitIntInsn(opcode, operand);
    }

    @Override
    public void visitVarInsn(int opcode, int var) {
        reset();
        super.visitVarInsn(opcode, var);
    }

    @Override
    public void visitTypeInsn(int opcode, String type) {
        reset();
        super.visitTypeInsn(opcode, type);
    }

    @Override
    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
        reset();
        super.visitFieldInsn(opcode, owner, name, desc);
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
        reset();
        super.visitMethodInsn(opcode, owner, name, desc);
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
        // If a isSuccessful method is called directly after retrieving the DataTransactionResult,
        // then we can also replace this with one method call
        // For example:
        /*
        final LanternEntity entity = new LanternItem(UUID.randomUUID());
        boolean test = entity.offer(Keys.VELOCITY, Vector3d.ZERO).isSuccessful();
         */
        // to
        /*
        final LanternEntity entity = new LanternItem(UUID.randomUUID());
        boolean test = CompositeValueStoreHelper.offer(entity, Keys.VELOCITY, Vector3d.ZERO);
         */
        // or
        /*
        final LanternEntity entity = new LanternItem(UUID.randomUUID());
        boolean test = entity.offerFast(Keys.VELOCITY, Vector3d.ZERO);
         */
        if (this.entry != null && opcode == INVOKEVIRTUAL
                && owner.equals("org/spongepowered/api/data/DataTransactionResult") && name.equals("isSuccessful")
                && desc.equals("()Z")) {
            writeMethod();
            return;
        }

        reset();

        // Catch every method call that can possibly be replaced by a fast method,
        // the next opcodes will define if the method will actually be replaced.
        // If this is not the case then will the original method be written.
        String[] entry;
        int store;

        final String key = name + ';' + desc;
        if (opcode == INVOKEVIRTUAL && (entry = entries.get(key)) != null
                && (store = FastValueContainerCheckerClassVisitor.isCompositeValueStore(owner)) > 0) {
            this.store = store;
            this.entry = entry;
            this.opcode = opcode;
            this.owner = owner;
            this.name = name;
            this.desc = desc;
            this.itf = itf;
        } else {
            super.visitMethodInsn(opcode, owner, name, desc, itf);
        }
    }

    @Override
    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
        reset();
        super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
    }

    @Override
    public void visitJumpInsn(int opcode, Label label) {
        reset();
        super.visitJumpInsn(opcode, label);
    }

    @Override
    public void visitLabel(Label label) {
        reset();
        super.visitLabel(label);
    }

    @Override
    public void visitLdcInsn(Object cst) {
        reset();
        super.visitLdcInsn(cst);
    }

    @Override
    public void visitIincInsn(int var, int increment) {
        reset();
        super.visitIincInsn(var, increment);
    }

    @Override
    public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
        reset();
        super.visitTableSwitchInsn(min, max, dflt, labels);
    }

    @Override
    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
        reset();
        super.visitLookupSwitchInsn(dflt, keys, labels);
    }

    @Override
    public void visitMultiANewArrayInsn(String desc, int dims) {
        reset();
        super.visitMultiANewArrayInsn(desc, dims);
    }

    @Override
    public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
        reset();
        return super.visitInsnAnnotation(typeRef, typePath, desc, visible);
    }

    @Override
    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
        reset();
        super.visitTryCatchBlock(start, end, handler, type);
    }

    @Override
    public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
        reset();
        return super.visitTryCatchAnnotation(typeRef, typePath, desc, visible);
    }

    @Override
    public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
        reset();
        super.visitLocalVariable(name, desc, signature, start, end, index);
    }

    @Override
    public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start,
            Label[] end, int[] index, String desc, boolean visible) {
        reset();
        return super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, desc, visible);
    }

    @Override
    public void visitLineNumber(int line, Label start) {
        reset();
        super.visitLineNumber(line, start);
    }

    @Override
    public void visitMaxs(int maxStack, int maxLocals) {
        reset();
        super.visitMaxs(maxStack, maxLocals);
    }

    @Override
    public void visitEnd() {
        reset();
        super.visitEnd();
    }
}