001/*
002 *  Copyright 2013 Christopher Pheby
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.jadira.reflection.access.invokedynamic;
017
018import java.lang.invoke.CallSite;
019import java.lang.invoke.MethodHandle;
020import java.lang.invoke.MethodType;
021import java.lang.reflect.Field;
022
023import org.dynalang.dynalink.DefaultBootstrapper;
024import org.jadira.reflection.access.api.FieldAccess;
025
026/**
027 * FieldAccess implementation using an InvokeDynamic based strategy (using ASM and Dynalang)
028 * @param <C> The Class containing the Field to be accessed
029 */
030public class InvokeDynamicFieldAccess<C> implements FieldAccess<C> {
031
032    private String fieldName;
033        
034        private Class<C> declaringClass;
035        private Class<?> fieldClass;
036        private Field field;    
037
038        CallSite getCallSite;
039        CallSite setCallSite;
040        
041        MethodHandle setMh;
042        MethodHandle getMh;
043        
044        @SuppressWarnings("unchecked")
045        private InvokeDynamicFieldAccess(InvokeDynamicClassAccess<C> classAccess, Field f) {
046                
047                this.declaringClass = (Class<C>) f.getDeclaringClass();
048                
049                this.fieldClass = f.getType();
050                
051                this.fieldName = f.getName();
052                
053                this.field = f;
054                
055                setCallSite = DefaultBootstrapper.publicBootstrap(null, "dyn:setProp:" + fieldName, MethodType.methodType(void.class, Object.class, fieldClass));
056                getCallSite = DefaultBootstrapper.publicBootstrap(null, "dyn:getProp:" + fieldName, MethodType.methodType(fieldClass, Object.class));
057                
058                setMh = setCallSite.dynamicInvoker();
059            getMh = getCallSite.dynamicInvoker();
060        }
061        
062        @Override
063        public Class<C> declaringClass() {
064                return declaringClass;
065        }
066
067        @Override
068        public Class<?> fieldClass() {
069                return fieldClass;
070        }
071
072        @Override
073        public Field field() {
074                return field;
075        }
076        
077        /**
078         * Get a new instance that can access the given Field
079         * @param classAccess The InvokeDynamicClassAccess instance to be delegated to
080         * @param f Field to be accessed
081         * @param <C> The type of class being accessed
082         * @return New InvokeDynamicFieldAccess instance
083         */
084        public static final <C> InvokeDynamicFieldAccess<C> get(InvokeDynamicClassAccess<C> classAccess, Field f) {
085                return new InvokeDynamicFieldAccess<C>(classAccess, f);
086        }
087
088        @Override
089        public Object getValue(C parent) {
090        try {
091            return getMh.invokeExact(parent);
092        } catch (Throwable e) {
093            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
094                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
095        }
096        }
097
098        @Override
099        public void putValue(C parent, Object newFieldValue) {
100            try {
101            setMh.invokeExact(parent, newFieldValue);
102        } catch (Throwable e) {
103            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
104                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
105        }
106        }
107
108        @Override
109        public boolean getBooleanValue(C parent) {
110        try {
111            return (boolean) getMh.invokeExact(parent);
112        } catch (Throwable e) {
113            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
114                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
115        }
116        }
117
118        @Override
119        public byte getByteValue(C parent) {
120        try {
121            return (byte) getMh.invokeExact(parent);
122        } catch (Throwable e) {
123            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
124                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
125        }
126        }
127
128        @Override
129        public char getCharValue(C parent) {
130        try {
131            return (char) getMh.invokeExact(parent);
132        } catch (Throwable e) {
133            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
134                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
135        }
136        }
137
138        @Override
139        public short getShortValue(C parent) {
140        try {
141            return (short) getMh.invokeExact(parent);
142        } catch (Throwable e) {
143            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
144                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
145        }
146        }
147
148        @Override
149        public int getIntValue(C parent) {
150        try {
151            return (int) getMh.invokeExact(parent);
152        } catch (Throwable e) {
153            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
154                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
155        }
156        }
157
158        @Override
159        public long getLongValue(C parent) {
160        try {
161            return (long) getMh.invokeExact(parent);
162        } catch (Throwable e) {
163            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
164                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
165        }
166        }
167
168        @Override
169        public float getFloatValue(C parent) {
170        try {
171            return (float) getMh.invokeExact(parent);
172        } catch (Throwable e) {
173            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
174                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
175        }
176        }
177
178        @Override
179        public double getDoubleValue(C parent) {
180        try {
181            return (double) getMh.invokeExact(parent);
182        } catch (Throwable e) {
183            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
184                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
185        }
186        }
187
188        @Override
189        public void putBooleanValue(C parent, boolean newFieldValue) {
190            try {
191            setMh.invokeExact(parent, newFieldValue);
192        } catch (Throwable e) {
193            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
194                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
195        }
196        }
197
198        @Override
199        public void putByteValue(C parent, byte newFieldValue) {
200            try {
201            setMh.invokeExact(parent, newFieldValue);
202        } catch (Throwable e) {
203            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
204                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
205        }
206        }
207
208        @Override
209        public void putCharValue(C parent, char newFieldValue) {
210            try {
211            setMh.invokeExact(parent, newFieldValue);
212        } catch (Throwable e) {
213            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
214                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
215        }
216        }
217
218        @Override
219        public void putShortValue(C parent, short newFieldValue) {
220            try {
221            setMh.invokeExact(parent, newFieldValue);
222        } catch (Throwable e) {
223            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
224                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
225        }
226        }
227
228        @Override
229        public void putIntValue(C parent, int newFieldValue) {
230            try {
231            setMh.invokeExact(parent, newFieldValue);
232        } catch (Throwable e) {
233            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
234                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
235        }
236        }
237
238        @Override
239        public void putLongValue(C parent, long newFieldValue) {
240            try {
241            setMh.invokeExact(parent, newFieldValue);
242        } catch (Throwable e) {
243            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
244                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
245        }
246        }
247
248        @Override
249        public void putFloatValue(C parent, float newFieldValue) {
250            try {
251            setMh.invokeExact(parent, newFieldValue);
252        } catch (Throwable e) {
253            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
254                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
255        }
256        }
257
258        @Override
259        public void putDoubleValue(C parent, double newFieldValue) {
260            try {
261            setMh.invokeExact(parent, newFieldValue);
262        } catch (Throwable e) {
263            throw new IllegalStateException("Problem accessing {" + field.getName() + "} of object {"
264                    + System.identityHashCode(parent) + "}: " + e.getMessage(), e);
265        }
266        }
267}