Java tutorial
/** * 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)); } }