/* * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import static jdk.dynalink.StandardNamespace.ELEMENT; import static jdk.dynalink.StandardNamespace.PROPERTY; import static jdk.dynalink.StandardOperation.GET; import static jdk.dynalink.StandardOperation.SET; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.DoubleBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.List; import jdk.dynalink.CallSiteDescriptor; import jdk.dynalink.NamedOperation; import jdk.dynalink.NamespaceOperation; import jdk.dynalink.Operation; import jdk.dynalink.StandardNamespace; import jdk.dynalink.linker.GuardedInvocation; import jdk.dynalink.linker.GuardingDynamicLinker; import jdk.dynalink.linker.GuardingDynamicLinkerExporter; import jdk.dynalink.linker.LinkRequest; import jdk.dynalink.linker.LinkerServices; import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker; import jdk.dynalink.linker.support.Guards; import jdk.dynalink.linker.support.Lookup; /** * This is a dynalink pluggable linker (see http://openjdk.java.net/jeps/276). * This linker adds array-like indexing and "length" property to nio Buffer objects. */ public final class BufferIndexingLinkerExporter extends GuardingDynamicLinkerExporter { static { System.out.println("pluggable dynalink buffer indexing linker loaded"); } private static final MethodHandle BUFFER_LIMIT; private static final MethodHandle BYTEBUFFER_GET; private static final MethodHandle BYTEBUFFER_PUT; private static final MethodHandle CHARBUFFER_GET; private static final MethodHandle CHARBUFFER_PUT; private static final MethodHandle SHORTBUFFER_GET; private static final MethodHandle SHORTBUFFER_PUT; private static final MethodHandle INTBUFFER_GET; private static final MethodHandle INTBUFFER_PUT; private static final MethodHandle LONGBUFFER_GET; private static final MethodHandle LONGBUFFER_PUT; private static final MethodHandle FLOATBUFFER_GET; private static final MethodHandle FLOATBUFFER_PUT; private static final MethodHandle DOUBLEBUFFER_GET; private static final MethodHandle DOUBLEBUFFER_PUT; // guards private static final MethodHandle IS_BUFFER; private static final MethodHandle IS_BYTEBUFFER; private static final MethodHandle IS_CHARBUFFER; private static final MethodHandle IS_SHORTBUFFER; private static final MethodHandle IS_INTBUFFER; private static final MethodHandle IS_LONGBUFFER; private static final MethodHandle IS_FLOATBUFFER; private static final MethodHandle IS_DOUBLEBUFFER; private static final MethodType GUARD_TYPE; static { final Lookup look = Lookup.PUBLIC; BUFFER_LIMIT = look.findVirtual(Buffer.class, "limit", MethodType.methodType(int.class)); BYTEBUFFER_GET = look.findVirtual(ByteBuffer.class, "get", MethodType.methodType(byte.class, int.class)); BYTEBUFFER_PUT = look.findVirtual(ByteBuffer.class, "put", MethodType.methodType(ByteBuffer.class, int.class, byte.class)); CHARBUFFER_GET = look.findVirtual(CharBuffer.class, "get", MethodType.methodType(char.class, int.class)); CHARBUFFER_PUT = look.findVirtual(CharBuffer.class, "put", MethodType.methodType(CharBuffer.class, int.class, char.class)); SHORTBUFFER_GET = look.findVirtual(ShortBuffer.class, "get", MethodType.methodType(short.class, int.class)); SHORTBUFFER_PUT = look.findVirtual(ShortBuffer.class, "put", MethodType.methodType(ShortBuffer.class, int.class, short.class)); INTBUFFER_GET = look.findVirtual(IntBuffer.class, "get", MethodType.methodType(int.class, int.class)); INTBUFFER_PUT = look.findVirtual(IntBuffer.class, "put", MethodType.methodType(IntBuffer.class, int.class, int.class)); LONGBUFFER_GET = look.findVirtual(LongBuffer.class, "get", MethodType.methodType(long.class, int.class)); LONGBUFFER_PUT = look.findVirtual(LongBuffer.class, "put", MethodType.methodType(LongBuffer.class, int.class, long.class)); FLOATBUFFER_GET = look.findVirtual(FloatBuffer.class, "get", MethodType.methodType(float.class, int.class)); FLOATBUFFER_PUT = look.findVirtual(FloatBuffer.class, "put", MethodType.methodType(FloatBuffer.class, int.class, float.class)); DOUBLEBUFFER_GET = look.findVirtual(DoubleBuffer.class, "get", MethodType.methodType(double.class, int.class)); DOUBLEBUFFER_PUT = look.findVirtual(DoubleBuffer.class, "put", MethodType.methodType(DoubleBuffer.class, int.class, double.class)); GUARD_TYPE = MethodType.methodType(boolean.class, Object.class); IS_BUFFER = Guards.isInstance(Buffer.class, GUARD_TYPE); IS_BYTEBUFFER = Guards.isInstance(ByteBuffer.class, GUARD_TYPE); IS_CHARBUFFER = Guards.isInstance(CharBuffer.class, GUARD_TYPE); IS_SHORTBUFFER = Guards.isInstance(ShortBuffer.class, GUARD_TYPE); IS_INTBUFFER = Guards.isInstance(IntBuffer.class, GUARD_TYPE); IS_LONGBUFFER = Guards.isInstance(LongBuffer.class, GUARD_TYPE); IS_FLOATBUFFER = Guards.isInstance(FloatBuffer.class, GUARD_TYPE); IS_DOUBLEBUFFER = Guards.isInstance(DoubleBuffer.class, GUARD_TYPE); } @Override public List<GuardingDynamicLinker> get() { final ArrayList<GuardingDynamicLinker> linkers = new ArrayList<>(); linkers.add(new TypeBasedGuardingDynamicLinker() { @Override public boolean canLinkType(final Class<?> type) { return Buffer.class.isAssignableFrom(type); } @Override public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices) throws Exception { final Object self = request.getReceiver(); if (self == null || !canLinkType(self.getClass())) { return null; } final CallSiteDescriptor desc = request.getCallSiteDescriptor(); final Operation namedOp = desc.getOperation(); final Operation namespaceOp = NamedOperation.getBaseOperation(namedOp); final Operation op = NamespaceOperation.getBaseOperation(namespaceOp); final StandardNamespace ns = StandardNamespace.findFirst(namespaceOp); if (ns == null) { return null; } if (op == GET) { if (ns == ELEMENT) { return linkGetElement(self); } else if (ns == PROPERTY) { final Object name = NamedOperation.getName(desc.getOperation()); if ("length".equals(name)) { return linkLength(); } } } else if (op == SET && ns == ELEMENT) { return linkSetElement(self); } return null; } }); return linkers; } private static GuardedInvocation linkGetElement(final Object self) { MethodHandle method = null; MethodHandle guard = null; if (self instanceof ByteBuffer) { method = BYTEBUFFER_GET; guard = IS_BYTEBUFFER; } else if (self instanceof CharBuffer) { method = CHARBUFFER_GET; guard = IS_CHARBUFFER; } else if (self instanceof ShortBuffer) { method = SHORTBUFFER_GET; guard = IS_SHORTBUFFER; } else if (self instanceof IntBuffer) { method = INTBUFFER_GET; guard = IS_INTBUFFER; } else if (self instanceof LongBuffer) { method = LONGBUFFER_GET; guard = IS_LONGBUFFER; } else if (self instanceof FloatBuffer) { method = FLOATBUFFER_GET; guard = IS_FLOATBUFFER; } else if (self instanceof DoubleBuffer) { method = DOUBLEBUFFER_GET; guard = IS_DOUBLEBUFFER; } return method != null? new GuardedInvocation(method, guard) : null; } private static GuardedInvocation linkSetElement(final Object self) { MethodHandle method = null; MethodHandle guard = null; if (self instanceof ByteBuffer) { method = BYTEBUFFER_PUT; guard = IS_BYTEBUFFER; } else if (self instanceof CharBuffer) { method = CHARBUFFER_PUT; guard = IS_CHARBUFFER; } else if (self instanceof ShortBuffer) { method = SHORTBUFFER_PUT; guard = IS_SHORTBUFFER; } else if (self instanceof IntBuffer) { method = INTBUFFER_PUT; guard = IS_INTBUFFER; } else if (self instanceof LongBuffer) { method = LONGBUFFER_PUT; guard = IS_LONGBUFFER; } else if (self instanceof FloatBuffer) { method = FLOATBUFFER_PUT; guard = IS_FLOATBUFFER; } else if (self instanceof DoubleBuffer) { method = DOUBLEBUFFER_PUT; guard = IS_DOUBLEBUFFER; } return method != null? new GuardedInvocation(method, guard) : null; } private static GuardedInvocation linkLength() { return new GuardedInvocation(BUFFER_LIMIT, IS_BUFFER); } }