org.apache.drill.exec.compile.bytecode.InstructionModifier.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.drill.exec.compile.bytecode.InstructionModifier.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.drill.exec.compile.bytecode;

import org.apache.drill.exec.compile.bytecode.ValueHolderIden.ValueHolderSub;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import com.carrotsearch.hppc.IntIntOpenHashMap;
import com.carrotsearch.hppc.IntObjectOpenHashMap;

public class InstructionModifier extends MethodVisitor {
    static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(InstructionModifier.class);

    private final IntObjectOpenHashMap<ValueHolderIden.ValueHolderSub> oldToNew = new IntObjectOpenHashMap<>();
    private final IntIntOpenHashMap oldLocalToFirst = new IntIntOpenHashMap();

    private DirectSorter adder;
    int lastLineNumber = 0;
    private TrackingInstructionList list;

    public InstructionModifier(int access, String name, String desc, String signature, String[] exceptions,
            TrackingInstructionList list, MethodVisitor inner) {
        super(Opcodes.ASM4, new DirectSorter(access, desc, inner));
        this.list = list;
        this.adder = (DirectSorter) mv;
    }

    public void setList(TrackingInstructionList list) {
        this.list = list;
    }

    private ReplacingBasicValue local(int var) {
        Object o = list.currentFrame.getLocal(var);
        if (o instanceof ReplacingBasicValue) {
            return (ReplacingBasicValue) o;
        }
        return null;
    }

    private ReplacingBasicValue popCurrent() {
        return popCurrent(false);
    }

    private ReplacingBasicValue popCurrent(boolean includeReturnVals) {
        // for vararg, we could try to pop an empty stack. TODO: handle this better.
        if (list.currentFrame.getStackSize() == 0) {
            return null;
        }

        Object o = list.currentFrame.pop();
        if (o instanceof ReplacingBasicValue) {
            ReplacingBasicValue v = (ReplacingBasicValue) o;
            if (!v.isFunctionReturn || includeReturnVals) {
                return v;
            }
        }
        return null;
    }

    private ReplacingBasicValue getReturn() {
        Object o = list.nextFrame.getStack(list.nextFrame.getStackSize() - 1);
        if (o instanceof ReplacingBasicValue) {
            return (ReplacingBasicValue) o;
        }
        return null;
    }

    @Override
    public void visitInsn(int opcode) {
        switch (opcode) {
        case Opcodes.DUP:
            if (popCurrent() != null) {
                return;
            }
        }
        super.visitInsn(opcode);
    }

    @Override
    public void visitTypeInsn(int opcode, String type) {
        ReplacingBasicValue r = getReturn();
        if (r != null) {
            ValueHolderSub sub = r.getIden().getHolderSub(adder);
            oldToNew.put(r.getIndex(), sub);
        } else {
            super.visitTypeInsn(opcode, type);
        }
    }

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

    @Override
    public void visitVarInsn(int opcode, int var) {
        ReplacingBasicValue v;
        if (opcode == Opcodes.ASTORE && (v = popCurrent(true)) != null) {
            if (!v.isFunctionReturn) {
                ValueHolderSub from = oldToNew.get(v.getIndex());

                ReplacingBasicValue current = local(var);
                // if local var is set, then transfer to it to the existing holders in the local position.
                if (current != null) {
                    if (oldToNew.get(current.getIndex()).iden() == from.iden()) {
                        int targetFirst = oldToNew.get(current.index).first();
                        from.transfer(this, targetFirst);
                        return;
                    }
                }

                // if local var is not set, then check map to see if existing holders are mapped to local var.
                if (oldLocalToFirst.containsKey(var)) {
                    ValueHolderSub sub = oldToNew.get(oldLocalToFirst.lget());
                    if (sub.iden() == from.iden()) {
                        // if they are, then transfer to that.
                        from.transfer(this, oldToNew.get(oldLocalToFirst.lget()).first());
                        return;
                    }
                }

                // map from variables to global space for future use.
                oldLocalToFirst.put(var, v.getIndex());

            } else {
                // this is storage of a function return, we need to map the fields to the holder spots.
                int first;
                if (oldLocalToFirst.containsKey(var)) {
                    first = oldToNew.get(oldLocalToFirst.lget()).first();
                    v.iden.transferToLocal(adder, first);
                } else {
                    first = v.iden.createLocalAndTrasfer(adder);
                }
                ValueHolderSub from = v.iden.getHolderSubWithDefinedLocals(first);
                oldToNew.put(v.getIndex(), from);
                v.disableFunctionReturn();
            }

        } else if (opcode == Opcodes.ALOAD && (v = getReturn()) != null) {
            // noop.
        } else {
            super.visitVarInsn(opcode, var);
        }

    }

    void directVarInsn(int opcode, int var) {
        adder.directVarInsn(opcode, var);
    }

    @Override
    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
        ReplacingBasicValue v;

        switch (opcode) {
        case Opcodes.PUTFIELD:
            // pop twice for put.
            v = popCurrent(true);
            if (v != null) {
                if (v.isFunctionReturn) {
                    super.visitFieldInsn(opcode, owner, name, desc);
                    return;
                } else {
                    // we are trying to store a replaced variable in an external context, we need to generate an instance and
                    // transfer it out.
                    ValueHolderSub sub = oldToNew.get(v.getIndex());
                    sub.transferToExternal(adder, owner, name, desc);
                    return;
                }
            }

        case Opcodes.GETFIELD:
            // pop again.
            v = popCurrent();
            if (v != null) {
                // super.visitFieldInsn(opcode, owner, name, desc);
                ValueHolderSub sub = oldToNew.get(v.getIndex());
                sub.addInsn(name, this, opcode);
                return;
            }
        }

        super.visitFieldInsn(opcode, owner, name, desc);
    }

    @Override
    public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
        // if (!ReplacingInterpreter.HOLDER_DESCRIPTORS.contains(desc)) {
        // super.visitLocalVariable(name, desc, signature, start, end, index);
        // }
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
        final int len = Type.getArgumentTypes(desc).length;
        final boolean isStatic = opcode == Opcodes.INVOKESTATIC;

        ReplacingBasicValue obj = popCurrent();

        if (obj != null && !isStatic) {
            if ("<init>".equals(name)) {
                oldToNew.get(obj.getIndex()).init(adder);
            } else {
                throw new IllegalStateException("you can't call a method on a value holder.");
            }
            return;
        }

        obj = getReturn();

        if (obj != null) {
            // the return of this method is an actual instance of the object we're escaping. Update so that it get's mapped
            // correctly.
            super.visitMethodInsn(opcode, owner, name, desc);
            obj.markFunctionReturn();
            return;
        }

        int i = isStatic ? 1 : 0;
        for (; i < len; i++) {
            checkArg(name, popCurrent());
        }

        super.visitMethodInsn(opcode, owner, name, desc);
    }

    private void checkArg(String name, ReplacingBasicValue obj) {
        if (obj == null) {
            return;
        }
        throw new IllegalStateException(String.format(
                "Holder types are not allowed to be passed between methods.  Ran across problem attempting to invoke method '%s' on line number %d",
                name, lastLineNumber));
    }

}