/* * 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.codehaus.groovy.vmplugin.v7; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.HashMap; import org.codehaus.groovy.GroovyBugError; /** * Class for handling array access through invokedynamic using static callsite information * @author Jochen Theodorou * @since 2.5.0 */ public class IndyArrayAccess { private static final MethodHandle notNegative,normalizeIndex; static { try { notNegative = MethodHandles.lookup().findStatic(IndyArrayAccess.class, "notNegative", MethodType.methodType(boolean.class, int.class)); normalizeIndex = MethodHandles.lookup().findStatic(IndyArrayAccess.class, "normalizeIndex", MethodType.methodType(int.class, Object.class, int.class)); } catch (ReflectiveOperationException e) { throw new GroovyBugError(e); } } private static final HashMap<Class,MethodHandle> getterMap, setterMap; static { getterMap = new HashMap<Class,MethodHandle>(); Class[] classes = new Class[]{ int[].class, byte[].class, short[].class, long[].class, double[].class, float[].class, boolean[].class, char[].class, Object[].class}; for (Class arrayClass : classes) { MethodHandle handle = buildGetter(arrayClass); getterMap.put(arrayClass, handle); } setterMap = new HashMap<Class,MethodHandle>(); for (Class arrayClass : classes) { MethodHandle handle = buildSetter(arrayClass); setterMap.put(arrayClass, handle); } } private static MethodHandle buildGetter(Class arrayClass) { MethodHandle get = MethodHandles.arrayElementGetter(arrayClass); MethodHandle fallback = MethodHandles.explicitCastArguments(get, get.type().changeParameterType(0, Object.class)); fallback = MethodHandles.dropArguments(fallback, 2, int.class); MethodType reorderType = fallback.type(). insertParameterTypes(0, int.class). dropParameterTypes(2,3); fallback = MethodHandles.permuteArguments(fallback, reorderType, 1, 0, 0); fallback = MethodHandles.foldArguments(fallback, normalizeIndex); fallback = MethodHandles.explicitCastArguments(fallback, get.type()); MethodHandle guard = MethodHandles.dropArguments(notNegative, 0, arrayClass); MethodHandle handle = MethodHandles.guardWithTest(guard, get, fallback); return handle; } private static MethodHandle buildSetter(Class arrayClass){ MethodHandle set = MethodHandles.arrayElementSetter(arrayClass); MethodHandle fallback = MethodHandles.explicitCastArguments(set, set.type().changeParameterType(0, Object.class)); fallback = MethodHandles.dropArguments(fallback, 3, int.class); MethodType reorderType = fallback.type(). insertParameterTypes(0, int.class). dropParameterTypes(4,5); fallback = MethodHandles.permuteArguments(fallback, reorderType, 1, 0, 3, 0); fallback = MethodHandles.foldArguments(fallback, normalizeIndex); fallback = MethodHandles.explicitCastArguments(fallback, set.type()); MethodHandle guard = MethodHandles.dropArguments(notNegative, 0, arrayClass); MethodHandle handle = MethodHandles.guardWithTest(guard, set, fallback); return handle; } private static int getLength(Object array) { if (array instanceof Object[]) return ((Object[])array).length; if (array instanceof boolean[]) return ((boolean[])array).length; if (array instanceof byte[]) return ((byte[])array).length; if (array instanceof char[]) return ((char[])array).length; if (array instanceof short[]) return ((short[])array).length; if (array instanceof int[]) return ((int[])array).length; if (array instanceof long[]) return ((long[])array).length; if (array instanceof float[]) return ((float[])array).length; if (array instanceof double[]) return ((double[])array).length; return 0; } private static int normalizeIndex(Object array, int i) { int temp = i; int size = getLength(array); i += size; if (i < 0) { throw new ArrayIndexOutOfBoundsException("Negative array index [" + temp + "] too large for array size " + size); } return i; } public static boolean notNegative(int index) { return index>=0; } public static MethodHandle arrayGet(MethodType type) { Class key = type.parameterType(0); MethodHandle res = getterMap.get(key); if (res!=null) return res; res = buildGetter(key); res = MethodHandles.explicitCastArguments(res, type); return res; } public static MethodHandle arraySet(MethodType type) { Class key = type.parameterType(0); MethodHandle res = setterMap.get(key); if (res!=null) return res; res = buildSetter(key); res = MethodHandles.explicitCastArguments(res, type); return res; } }