/*
* Copyright 2013 Christopher Pheby
*
* Licensed 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.jadira.reflection.access.invokedynamic;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import org.dynalang.dynalink.DefaultBootstrapper;
import org.jadira.reflection.access.api.FieldAccess;
/**
* FieldAccess implementation using an InvokeDynamic based strategy (using ASM and Dynalang)
* @param <C> The Class containing the Field to be accessed
*/
public class InvokeDynamicFieldAccess<C> implements FieldAccess<C> {
private String fieldName;
private Class<C> declaringClass;
private Class<?> fieldClass;
private Field field;
CallSite getCallSite;
CallSite setCallSite;
MethodHandle setMh;
MethodHandle getMh;
@SuppressWarnings("unchecked")
private InvokeDynamicFieldAccess(InvokeDynamicClassAccess<C> classAccess, Field f) {
this.declaringClass = (Class<C>) f.getDeclaringClass();
this.fieldClass = f.getType();
this.fieldName = f.getName();
this.field = f;
setCallSite = DefaultBootstrapper.publicBootstrap(null, "dyn:setProp:" + fieldName, MethodType.methodType(void.class, Object.class, fieldClass));
getCallSite = DefaultBootstrapper.publicBootstrap(null, "dyn:getProp:" + fieldName, MethodType.methodType(fieldClass, Object.class));
setMh = setCallSite.dynamicInvoker();
getMh = getCallSite.dynamicInvoker();
}
@Override
public Class<C> declaringClass() {
return declaringClass;
}
@Override
public Class<?> fieldClass() {
return fieldClass;
}
@Override
public Field field() {
return field;
}
/**
* Get a new instance that can access the given Field
* @param classAccess The InvokeDynamicClassAccess instance to be delegated to
* @param f Field to be accessed
* @param <C> The type of class being accessed
* @return New InvokeDynamicFieldAccess instance
*/
public static final <C> InvokeDynamicFieldAccess<C> get(InvokeDynamicClassAccess<C> classAccess, Field f) {
return new InvokeDynamicFieldAccess<C>(classAccess, f);
}
@Override
public Object getValue(C parent) {
try {
return getMh.invokeExact(parent);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public void putValue(C parent, Object newFieldValue) {
try {
setMh.invokeExact(parent, newFieldValue);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public boolean getBooleanValue(C parent) {
try {
return (boolean) getMh.invokeExact(parent);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public byte getByteValue(C parent) {
try {
return (byte) getMh.invokeExact(parent);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public char getCharValue(C parent) {
try {
return (char) getMh.invokeExact(parent);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public short getShortValue(C parent) {
try {
return (short) getMh.invokeExact(parent);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public int getIntValue(C parent) {
try {
return (int) getMh.invokeExact(parent);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public long getLongValue(C parent) {
try {
return (long) getMh.invokeExact(parent);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public float getFloatValue(C parent) {
try {
return (float) getMh.invokeExact(parent);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public double getDoubleValue(C parent) {
try {
return (double) getMh.invokeExact(parent);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public void putBooleanValue(C parent, boolean newFieldValue) {
try {
setMh.invokeExact(parent, newFieldValue);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public void putByteValue(C parent, byte newFieldValue) {
try {
setMh.invokeExact(parent, newFieldValue);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public void putCharValue(C parent, char newFieldValue) {
try {
setMh.invokeExact(parent, newFieldValue);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public void putShortValue(C parent, short newFieldValue) {
try {
setMh.invokeExact(parent, newFieldValue);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public void putIntValue(C parent, int newFieldValue) {
try {
setMh.invokeExact(parent, newFieldValue);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public void putLongValue(C parent, long newFieldValue) {
try {
setMh.invokeExact(parent, newFieldValue);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public void putFloatValue(C parent, float newFieldValue) {
try {
setMh.invokeExact(parent, newFieldValue);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
@Override
public void putDoubleValue(C parent, double newFieldValue) {
try {
setMh.invokeExact(parent, newFieldValue);
} catch (Throwable e) {
throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
+ System.identityHashCode(parent) + "}: " + e.getMessage(), e);
}
}
}