DefaultInternalObjectFilter.java revision 1551:f3b883bec2d0
11573Srgrimes/*
214287Spst * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
31573Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
41573Srgrimes *
51573Srgrimes * This code is free software; you can redistribute it and/or modify it
61573Srgrimes * under the terms of the GNU General Public License version 2 only, as
71573Srgrimes * published by the Free Software Foundation.  Oracle designates this
81573Srgrimes * particular file as subject to the "Classpath" exception as provided
91573Srgrimes * by Oracle in the LICENSE file that accompanied this code.
101573Srgrimes *
111573Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT
121573Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
131573Srgrimes * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
141573Srgrimes * version 2 for more details (a copy is included in the LICENSE file that
151573Srgrimes * accompanied this code).
161573Srgrimes *
171573Srgrimes * You should have received a copy of the GNU General Public License version
181573Srgrimes * 2 along with this work; if not, write to the Free Software Foundation,
191573Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
201573Srgrimes *
211573Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
221573Srgrimes * or visit www.oracle.com if you need additional information or have any
231573Srgrimes * questions.
241573Srgrimes */
251573Srgrimes
261573Srgrimes/*
271573Srgrimes * This file is available under and governed by the GNU General Public
281573Srgrimes * License version 2 only, as published by the Free Software Foundation.
291573Srgrimes * However, the following notice accompanied the original version of this
301573Srgrimes * file, and Oracle licenses the original version of this file under the BSD
3114287Spst * license:
321573Srgrimes */
3392986Sobrien/*
3492986Sobrien   Copyright 2009-2015 Attila Szegedi
351573Srgrimes
361573Srgrimes   Licensed under both the Apache License, Version 2.0 (the "Apache License")
371573Srgrimes   and the BSD License (the "BSD License"), with licensee being free to
381573Srgrimes   choose either of the two at their discretion.
391573Srgrimes
401573Srgrimes   You may not use this file except in compliance with either the Apache
411573Srgrimes   License or the BSD License.
421573Srgrimes
431573Srgrimes   If you choose to use this file in compliance with the Apache License, the
441573Srgrimes   following notice applies to you:
451573Srgrimes
461573Srgrimes       You may obtain a copy of the Apache License at
471573Srgrimes
481573Srgrimes           http://www.apache.org/licenses/LICENSE-2.0
491573Srgrimes
501573Srgrimes       Unless required by applicable law or agreed to in writing, software
511573Srgrimes       distributed under the License is distributed on an "AS IS" BASIS,
521573Srgrimes       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
531573Srgrimes       implied. See the License for the specific language governing
541573Srgrimes       permissions and limitations under the License.
551573Srgrimes
561573Srgrimes   If you choose to use this file in compliance with the BSD License, the
571573Srgrimes   following notice applies to you:
581573Srgrimes
591573Srgrimes       Redistribution and use in source and binary forms, with or without
60189291Sdelphij       modification, are permitted provided that the following conditions are
611573Srgrimes       met:
621573Srgrimes       * Redistributions of source code must retain the above copyright
6314287Spst         notice, this list of conditions and the following disclaimer.
641573Srgrimes       * Redistributions in binary form must reproduce the above copyright
651573Srgrimes         notice, this list of conditions and the following disclaimer in the
661573Srgrimes         documentation and/or other materials provided with the distribution.
671573Srgrimes       * Neither the name of the copyright holder nor the names of
681573Srgrimes         contributors may be used to endorse or promote products derived from
691573Srgrimes         this software without specific prior written permission.
701573Srgrimes
711573Srgrimes       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
721573Srgrimes       IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
731573Srgrimes       TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
741573Srgrimes       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
7514287Spst       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
7614287Spst       CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
7714287Spst       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
7814287Spst       BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
7914287Spst       WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
8014287Spst       OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
8114287Spst       ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8214287Spst*/
8314287Spst
8414287Spstpackage jdk.dynalink.linker.support;
8539327Simp
8639327Simpimport java.lang.invoke.MethodHandle;
8714287Spstimport java.lang.invoke.MethodHandles;
8814287Spstimport java.lang.invoke.MethodType;
8914287Spstimport jdk.dynalink.DynamicLinkerFactory;
9014287Spstimport jdk.dynalink.linker.MethodHandleTransformer;
9114287Spst
9214287Spst/**
9314287Spst * Default implementation for a
9414287Spst * {@link DynamicLinkerFactory#setInternalObjectsFilter(MethodHandleTransformer)}
9514287Spst * that delegates to a pair of filtering method handles. It takes a method
9614287Spst * handle of {@code Object(Object)} type for filtering parameter values and
9714287Spst * another one of the same type for filtering return values. It applies them as
9814287Spst * parameter and return value filters on method handles passed to its
9914287Spst * {@link #transform(MethodHandle)} method, on those parameters and return values
10014287Spst * that are declared to have type {@link Object}. Also handles
1011573Srgrimes * {@link MethodHandle#isVarargsCollector() method handles that support variable
1021573Srgrimes * arity calls} with a last {@code Object[]} parameter. You can broadly think of
10314287Spst * the parameter filter as being a wrapping method for exposing internal runtime
1041573Srgrimes * objects wrapped into an adapter with some public interface, and the return
10514287Spst * value filter as being its inverse unwrapping method.
1061573Srgrimes */
1071573Srgrimespublic class DefaultInternalObjectFilter implements MethodHandleTransformer {
1081573Srgrimes    private static final MethodHandle FILTER_VARARGS = new Lookup(MethodHandles.lookup()).findStatic(
1091573Srgrimes            DefaultInternalObjectFilter.class, "filterVarArgs", MethodType.methodType(Object[].class, MethodHandle.class, Object[].class));
1101573Srgrimes
1111573Srgrimes    private final MethodHandle parameterFilter;
1121573Srgrimes    private final MethodHandle returnFilter;
1131573Srgrimes    private final MethodHandle varArgFilter;
1141573Srgrimes
1151573Srgrimes    /**
1161573Srgrimes     * Creates a new filter.
1171573Srgrimes     * @param parameterFilter the filter for method parameters. Must be of type
1181573Srgrimes     * {@code Object(Object)}, or {@code null}.
1191573Srgrimes     * @param returnFilter the filter for return values. Must be of type
1201573Srgrimes     * {@code Object(Object)}, or {@code null}.
1211573Srgrimes     * @throws IllegalArgumentException if one or both filters are not of the
1221573Srgrimes     * expected type.
1231573Srgrimes     */
1241573Srgrimes    public DefaultInternalObjectFilter(final MethodHandle parameterFilter, final MethodHandle returnFilter) {
1251573Srgrimes        this.parameterFilter = checkHandle(parameterFilter, "parameterFilter");
1261573Srgrimes        this.returnFilter = checkHandle(returnFilter, "returnFilter");
1271573Srgrimes        this.varArgFilter = parameterFilter == null ? null : FILTER_VARARGS.bindTo(parameterFilter);
1281573Srgrimes    }
1291573Srgrimes
1301573Srgrimes    @Override
1311573Srgrimes    public MethodHandle transform(final MethodHandle target) {
1321573Srgrimes        assert target != null;
1331573Srgrimes        MethodHandle[] filters = null;
1341573Srgrimes        final MethodType type = target.type();
1351573Srgrimes        final boolean isVarArg = target.isVarargsCollector();
1361573Srgrimes        final int paramCount = type.parameterCount();
1371573Srgrimes        final MethodHandle paramsFiltered;
13814287Spst        // Filter parameters
1391573Srgrimes        if (parameterFilter != null) {
1401573Srgrimes            int firstFilter = -1;
1411573Srgrimes            // Ignore receiver, start from argument 1
14214287Spst            for(int i = 1; i < paramCount; ++i) {
1431573Srgrimes                final Class<?> paramType = type.parameterType(i);
1441573Srgrimes                final boolean filterVarArg = isVarArg && i == paramCount - 1 && paramType == Object[].class;
1451573Srgrimes                if (filterVarArg || paramType == Object.class) {
1461573Srgrimes                    if (filters == null) {
1471573Srgrimes                        firstFilter = i;
1481573Srgrimes                        filters = new MethodHandle[paramCount - firstFilter];
1491573Srgrimes                    }
1501573Srgrimes                    filters[i - firstFilter] = filterVarArg ? varArgFilter : parameterFilter;
1511573Srgrimes                }
1521573Srgrimes            }
1531573Srgrimes            paramsFiltered = filters != null ? MethodHandles.filterArguments(target, firstFilter, filters) : target;
1541573Srgrimes        } else {
1551573Srgrimes            paramsFiltered = target;
15614287Spst        }
1571573Srgrimes        // Filter return value if needed
1581573Srgrimes        final MethodHandle returnFiltered = returnFilter != null && type.returnType() == Object.class ? MethodHandles.filterReturnValue(paramsFiltered, returnFilter) : paramsFiltered;
1591573Srgrimes        // Preserve varargs collector state
1601573Srgrimes        return isVarArg && !returnFiltered.isVarargsCollector() ? returnFiltered.asVarargsCollector(type.parameterType(paramCount - 1)) : returnFiltered;
16114287Spst
1621573Srgrimes    }
1631573Srgrimes
16464381Sgreen    private static MethodHandle checkHandle(final MethodHandle handle, final String handleKind) {
16564381Sgreen        if (handle != null) {
16664381Sgreen            final MethodType objectObjectType = MethodType.methodType(Object.class, Object.class);
16764381Sgreen            if (!handle.type().equals(objectObjectType)) {
16864381Sgreen                throw new IllegalArgumentException("Method type for " + handleKind + " must be " + objectObjectType);
16914287Spst            }
17064381Sgreen        }
17164381Sgreen        return handle;
172189327Sdelphij    }
17314287Spst
1741573Srgrimes    @SuppressWarnings("unused")
1751573Srgrimes    private static Object[] filterVarArgs(final MethodHandle parameterFilter, final Object[] args) throws Throwable {
1761573Srgrimes        Object[] newArgs = null;
1771573Srgrimes        for(int i = 0; i < args.length; ++i) {
1781573Srgrimes            final Object arg = args[i];
1791573Srgrimes            final Object newArg = parameterFilter.invokeExact(arg);
1801573Srgrimes            if (arg != newArg) {
1811573Srgrimes                if (newArgs == null) {
1821573Srgrimes                    newArgs = args.clone();
1831573Srgrimes                }
1841573Srgrimes                newArgs[i] = newArg;
1851573Srgrimes            }
1861573Srgrimes        }
1871573Srgrimes        return newArgs == null ? args : newArgs;
1881573Srgrimes    }
189189291Sdelphij}
1901573Srgrimes