blob: b2c34c954fcea8d56cc3ed1bd6d6da50722d262d [file] [log] [blame]
/*
* 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 java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import com.carrotsearch.hppc.ObjectIntHashMap;
import com.carrotsearch.hppc.cursors.ObjectIntCursor;
import com.google.common.collect.Lists;
class ValueHolderIden {
// private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ValueHolderIden.class);
// the index of a field is the number in which it appears within the holder
private final ObjectIntHashMap<String> fieldMap; // field name -> index
private final Type[] types; // the type of each field in the holder, by index
private final String[] names; // the name of each field in the holder, by index
private final int[] offsets; // the offset of each field in the holder, by index
private final Type type; // type of the holder itself
public ValueHolderIden(Class<?> c) {
Field[] fields = c.getFields();
// Find the non-static member variables
List<Field> fldList = Lists.newArrayList();
for (Field f : fields) {
if (!Modifier.isStatic(f.getModifiers())) {
fldList.add(f);
}
}
this.type = Type.getType(c);
this.types = new Type[fldList.size()];
this.names = new String[fldList.size()];
this.offsets = new int[fldList.size()];
fieldMap = new ObjectIntHashMap<String>(fldList.size());
int i = 0; // index of the next holder member variable
int offset = 0; // offset of the next holder member variable
for (Field f : fldList) {
types[i] = Type.getType(f.getType());
names[i] = f.getName();
offsets[i] = offset;
fieldMap.put(f.getName(), i);
// the next offset and index
offset += types[i].getSize();
i++;
}
}
public void dump(final StringBuilder sb) {
sb.append("ValueHolderIden: type=" + type + '\n');
for (ObjectIntCursor<String> oic : fieldMap) {
sb.append(" " + oic.key + ": i=" + oic.value + ", type=" + types[oic.value] +
", offset=" + offsets[oic.value] + '\n');
}
}
private static void initType(int offset, Type t, DirectSorter v) {
switch(t.getSort()) {
case Type.BOOLEAN:
case Type.BYTE:
case Type.CHAR:
case Type.SHORT:
case Type.INT:
v.visitInsn(Opcodes.ICONST_0);
v.directVarInsn(Opcodes.ISTORE, offset);
break;
case Type.LONG:
v.visitInsn(Opcodes.LCONST_0);
v.directVarInsn(Opcodes.LSTORE, offset);
break;
case Type.FLOAT:
v.visitInsn(Opcodes.FCONST_0);
v.directVarInsn(Opcodes.FSTORE, offset);
break;
case Type.DOUBLE:
v.visitInsn(Opcodes.DCONST_0);
v.directVarInsn(Opcodes.DSTORE, offset);
break;
case Type.OBJECT:
v.visitInsn(Opcodes.ACONST_NULL);
v.directVarInsn(Opcodes.ASTORE, offset);
break;
default:
throw new UnsupportedOperationException();
}
}
/**
* Add the locals necessary to replace the members of a holder of this type.
*
* @param adder the method visitor to use to add the necessary instructions
* @param defaultFirst the default index to return for the first variable
* if we don't find another one
* @return the index of the first local variable (standing in for the first holder member)
*/
private int addLocals(final DirectSorter adder, final int defaultFirst) {
int first = defaultFirst;
for (int i = 0; i < types.length; i++) {
int varIndex = adder.newLocal(types[i]);
if (i == 0) {
first = varIndex; // first offset
}
}
return first;
}
public ValueHolderSub getHolderSub(DirectSorter adder) {
final int first = addLocals(adder, -1);
return new ValueHolderSub(first);
}
public ValueHolderSub getHolderSubWithDefinedLocals(int first) {
return new ValueHolderSub(first);
}
/**
* Return the DUP or DUP2 opcode appropriate for the given type.
*
* @param type the type
* @return the DUP/DUP2 opcode to use for type
*/
private static int getDupOpcode(final Type type) {
assert type.getSize() >= 1;
return type.getSize() == 1 ? Opcodes.DUP : Opcodes.DUP2;
}
/**
* Transfer the member variables of this holder to local variables.
*
* <p>If this is used, the maximum stack size must be increased by one
* to accommodate the extra DUP instruction this will generate.
*
* @param adder a visitor that will be called to add the necessary instructions
* @param localVariable the offset of the first local variable to use
*/
public void transferToLocal(final DirectSorter adder, final int localVariable) {
for (int i = 0; i < types.length; i++) {
final Type t = types[i];
if (i + 1 < types.length) {
adder.visitInsn(Opcodes.DUP); // not size dependent: always the objectref from which we'll GETFIELD
}
adder.visitFieldInsn(Opcodes.GETFIELD, type.getInternalName(), names[i], t.getDescriptor());
adder.directVarInsn(t.getOpcode(Opcodes.ISTORE), localVariable + offsets[i]);
}
}
/**
* Create local variables and transfer the members of a holder to them.
*
* @param adder the method visitor to use to add the variables
* @return the index of the first variable added
*/
public int createLocalAndTransfer(final DirectSorter adder) {
final int first = addLocals(adder, 0);
transferToLocal(adder, first);
return first;
}
public class ValueHolderSub {
private int first; // TODO: deal with transfer() so this can be made final
@Override
public String toString() {
return "ValueHolderSub(" + first + ")";
}
public ValueHolderSub(int first) {
assert first != -1 : "Create Holder for sub that doesn't have any fields.";
this.first = first;
}
public ValueHolderIden iden() {
return ValueHolderIden.this;
}
public void init(DirectSorter mv) {
for (int i = 0; i < types.length; i++) {
initType(first + offsets[i], types[i], mv);
}
}
public int first() {
return first;
}
private int getFieldIndex(final String name, final InstructionModifier mv) {
if (!fieldMap.containsKey(name)) {
throw new IllegalArgumentException(String.format(
"Unknown name '%s' on line %d.", name, mv.getLastLineNumber()));
}
return fieldMap.get(name); // using lget() is not thread-safe
}
public void addInsn(String name, InstructionModifier mv, int opcode) {
switch (opcode) {
case Opcodes.GETFIELD:
addKnownInsn(name, mv, Opcodes.ILOAD);
return;
case Opcodes.PUTFIELD:
addKnownInsn(name, mv, Opcodes.ISTORE);
}
}
// TODO: do we really need this? Instead of moving the variables, can't we just
// use the original locations in the subsequent references?
public void transfer(InstructionModifier mv, int newStart) {
// if the new location is the same as the current position, there's nothing to do
if (first == newStart) {
return;
}
for (int i = 0; i < types.length; i++) {
mv.directVarInsn(types[i].getOpcode(Opcodes.ILOAD), first + offsets[i]);
mv.directVarInsn(types[i].getOpcode(Opcodes.ISTORE), newStart + offsets[i]);
}
this.first = newStart;
}
private void addKnownInsn(String name, InstructionModifier mv, int analogOpcode) {
int f = getFieldIndex(name, mv);
Type t = types[f];
mv.directVarInsn(t.getOpcode(analogOpcode), first + offsets[f]);
}
/**
*
* @param adder
* @param owner
* @param name
* @param desc
* @return amount of additional stack space that will be required for this instruction stream
*/
public int transferToExternal(final DirectSorter adder, final String owner,
final String name, final String desc) {
// create a new object and assign it to the desired field.
adder.visitTypeInsn(Opcodes.NEW, type.getInternalName());
adder.visitInsn(getDupOpcode(type));
adder.visitMethodInsn(Opcodes.INVOKESPECIAL, type.getInternalName(), "<init>", "()V", false);
// now we need to set all of the values of this new object.
int additionalStack = 0;
for (int i = 0; i < types.length; i++) {
final Type t = types[i];
// dup the object where we are putting the field.
adder.visitInsn(getDupOpcode(type)); // dup for every as we want to save in place at end.
adder.directVarInsn(t.getOpcode(Opcodes.ILOAD), first + offsets[i]);
adder.visitFieldInsn(Opcodes.PUTFIELD, type.getInternalName(), names[i], t.getDescriptor());
/*
* The above substitutes a reference to a scalar in a holder with a direct reference to
* the scalar.
*
* In the case of longs or doubles, this requires more stack space than was used before;
* if we were moving a reference to a holder with a long, we were previously moving the
* reference. But now we're moving the long, which is twice as big. So we may need more
* stack space than has currently been allocated.
*/
if (t.getSize() > additionalStack) {
additionalStack = t.getSize();
}
}
// lastly we save it to the desired field.
adder.visitFieldInsn(Opcodes.PUTFIELD, owner, name, desc);
return additionalStack;
}
}
}