1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  *
   4  * Redistribution and use in source and binary forms, with or without
   5  * modification, are permitted provided that the following conditions
   6  * are met:
   7  *
   8  *   - Redistributions of source code must retain the above copyright
   9  *     notice, this list of conditions and the following disclaimer.
  10  *
  11  *   - Redistributions in binary form must reproduce the above copyright
  12  *     notice, this list of conditions and the following disclaimer in the
  13  *     documentation and/or other materials provided with the distribution.
  14  *
  15  *   - Neither the name of Oracle nor the names of its
  16  *     contributors may be used to endorse or promote products derived
  17  *     from this software without specific prior written permission.
  18  *
  19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30  */
  31 
  32 import static jdk.dynalink.StandardNamespace.ELEMENT;
  33 import static jdk.dynalink.StandardNamespace.PROPERTY;
  34 import static jdk.dynalink.StandardOperation.GET;
  35 import static jdk.dynalink.StandardOperation.SET;
  36 
  37 import java.lang.invoke.MethodHandle;
  38 import java.lang.invoke.MethodType;
  39 import java.nio.Buffer;
  40 import java.nio.ByteBuffer;
  41 import java.nio.CharBuffer;
  42 import java.nio.DoubleBuffer;
  43 import java.nio.FloatBuffer;
  44 import java.nio.IntBuffer;
  45 import java.nio.LongBuffer;
  46 import java.nio.ShortBuffer;
  47 import java.util.ArrayList;
  48 import java.util.List;
  49 import jdk.dynalink.CallSiteDescriptor;
  50 import jdk.dynalink.NamedOperation;
  51 import jdk.dynalink.NamespaceOperation;
  52 import jdk.dynalink.Operation;
  53 import jdk.dynalink.StandardNamespace;
  54 import jdk.dynalink.linker.GuardedInvocation;
  55 import jdk.dynalink.linker.GuardingDynamicLinker;
  56 import jdk.dynalink.linker.GuardingDynamicLinkerExporter;
  57 import jdk.dynalink.linker.LinkRequest;
  58 import jdk.dynalink.linker.LinkerServices;
  59 import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker;
  60 import jdk.dynalink.linker.support.Guards;
  61 import jdk.dynalink.linker.support.Lookup;
  62 
  63 /**
  64  * This is a dynalink pluggable linker (see http://openjdk.java.net/jeps/276).
  65  * This linker adds array-like indexing and "length" property to nio Buffer objects.
  66  */
  67 public final class BufferIndexingLinkerExporter extends GuardingDynamicLinkerExporter {
  68     static {
  69         System.out.println("pluggable dynalink buffer indexing linker loaded");
  70     }
  71 
  72     private static final MethodHandle BUFFER_LIMIT;
  73     private static final MethodHandle BYTEBUFFER_GET;
  74     private static final MethodHandle BYTEBUFFER_PUT;
  75     private static final MethodHandle CHARBUFFER_GET;
  76     private static final MethodHandle CHARBUFFER_PUT;
  77     private static final MethodHandle SHORTBUFFER_GET;
  78     private static final MethodHandle SHORTBUFFER_PUT;
  79     private static final MethodHandle INTBUFFER_GET;
  80     private static final MethodHandle INTBUFFER_PUT;
  81     private static final MethodHandle LONGBUFFER_GET;
  82     private static final MethodHandle LONGBUFFER_PUT;
  83     private static final MethodHandle FLOATBUFFER_GET;
  84     private static final MethodHandle FLOATBUFFER_PUT;
  85     private static final MethodHandle DOUBLEBUFFER_GET;
  86     private static final MethodHandle DOUBLEBUFFER_PUT;
  87 
  88     // guards
  89     private static final MethodHandle IS_BUFFER;
  90     private static final MethodHandle IS_BYTEBUFFER;
  91     private static final MethodHandle IS_CHARBUFFER;
  92     private static final MethodHandle IS_SHORTBUFFER;
  93     private static final MethodHandle IS_INTBUFFER;
  94     private static final MethodHandle IS_LONGBUFFER;
  95     private static final MethodHandle IS_FLOATBUFFER;
  96     private static final MethodHandle IS_DOUBLEBUFFER;
  97 
  98     private static final MethodType GUARD_TYPE;
  99 
 100     static {
 101         final Lookup look = Lookup.PUBLIC;
 102         BUFFER_LIMIT = look.findVirtual(Buffer.class, "limit", MethodType.methodType(int.class));
 103         BYTEBUFFER_GET = look.findVirtual(ByteBuffer.class, "get",
 104                 MethodType.methodType(byte.class, int.class));
 105         BYTEBUFFER_PUT = look.findVirtual(ByteBuffer.class, "put",
 106                 MethodType.methodType(ByteBuffer.class, int.class, byte.class));
 107         CHARBUFFER_GET = look.findVirtual(CharBuffer.class, "get",
 108                 MethodType.methodType(char.class, int.class));
 109         CHARBUFFER_PUT = look.findVirtual(CharBuffer.class, "put",
 110                 MethodType.methodType(CharBuffer.class, int.class, char.class));
 111         SHORTBUFFER_GET = look.findVirtual(ShortBuffer.class, "get",
 112                 MethodType.methodType(short.class, int.class));
 113         SHORTBUFFER_PUT = look.findVirtual(ShortBuffer.class, "put",
 114                 MethodType.methodType(ShortBuffer.class, int.class, short.class));
 115         INTBUFFER_GET = look.findVirtual(IntBuffer.class, "get",
 116                 MethodType.methodType(int.class, int.class));
 117         INTBUFFER_PUT = look.findVirtual(IntBuffer.class, "put",
 118                 MethodType.methodType(IntBuffer.class, int.class, int.class));
 119         LONGBUFFER_GET = look.findVirtual(LongBuffer.class, "get",
 120                 MethodType.methodType(long.class, int.class));
 121         LONGBUFFER_PUT = look.findVirtual(LongBuffer.class, "put",
 122                 MethodType.methodType(LongBuffer.class, int.class, long.class));
 123         FLOATBUFFER_GET = look.findVirtual(FloatBuffer.class, "get",
 124                 MethodType.methodType(float.class, int.class));
 125         FLOATBUFFER_PUT = look.findVirtual(FloatBuffer.class, "put",
 126                 MethodType.methodType(FloatBuffer.class, int.class, float.class));
 127         DOUBLEBUFFER_GET = look.findVirtual(DoubleBuffer.class, "get",
 128                 MethodType.methodType(double.class, int.class));
 129         DOUBLEBUFFER_PUT = look.findVirtual(DoubleBuffer.class, "put",
 130                 MethodType.methodType(DoubleBuffer.class, int.class, double.class));
 131 
 132         GUARD_TYPE = MethodType.methodType(boolean.class, Object.class);
 133         IS_BUFFER = Guards.isInstance(Buffer.class, GUARD_TYPE);
 134         IS_BYTEBUFFER = Guards.isInstance(ByteBuffer.class, GUARD_TYPE);
 135         IS_CHARBUFFER = Guards.isInstance(CharBuffer.class, GUARD_TYPE);
 136         IS_SHORTBUFFER = Guards.isInstance(ShortBuffer.class, GUARD_TYPE);
 137         IS_INTBUFFER = Guards.isInstance(IntBuffer.class, GUARD_TYPE);
 138         IS_LONGBUFFER = Guards.isInstance(LongBuffer.class, GUARD_TYPE);
 139         IS_FLOATBUFFER = Guards.isInstance(FloatBuffer.class, GUARD_TYPE);
 140         IS_DOUBLEBUFFER = Guards.isInstance(DoubleBuffer.class, GUARD_TYPE);
 141     }
 142 
 143     @Override
 144     public List<GuardingDynamicLinker> get() {
 145         final ArrayList<GuardingDynamicLinker> linkers = new ArrayList<>();
 146         linkers.add(new TypeBasedGuardingDynamicLinker() {
 147             @Override
 148             public boolean canLinkType(final Class<?> type) {
 149                 return Buffer.class.isAssignableFrom(type);
 150             }
 151 
 152             @Override
 153             public GuardedInvocation getGuardedInvocation(final LinkRequest request,
 154                 final LinkerServices linkerServices) throws Exception {
 155                 final Object self = request.getReceiver();
 156                 if (self == null || !canLinkType(self.getClass())) {
 157                     return null;
 158                 }
 159 
 160                 final CallSiteDescriptor desc = request.getCallSiteDescriptor();
 161                 final Operation namedOp = desc.getOperation();
 162                 final Operation namespaceOp = NamedOperation.getBaseOperation(namedOp);
 163                 final Operation op = NamespaceOperation.getBaseOperation(namespaceOp);
 164                 final StandardNamespace ns = StandardNamespace.findFirst(namespaceOp);
 165                 if (ns == null) {
 166                     return null;
 167                 }
 168 
 169                 if (op == GET) {
 170                     if (ns == ELEMENT) {
 171                         return linkGetElement(self);
 172                     } else if (ns == PROPERTY) {
 173                         final Object name = NamedOperation.getName(desc.getOperation());
 174                         if ("length".equals(name)) {
 175                             return linkLength();
 176                         }
 177                     }
 178                 } else if (op == SET && ns == ELEMENT) {
 179                     return linkSetElement(self);
 180                 }
 181 
 182                 return null;
 183             }
 184         });
 185         return linkers;
 186     }
 187 
 188     private static GuardedInvocation linkGetElement(final Object self) {
 189         MethodHandle method = null;
 190         MethodHandle guard = null;
 191         if (self instanceof ByteBuffer) {
 192             method = BYTEBUFFER_GET;
 193             guard = IS_BYTEBUFFER;
 194         } else if (self instanceof CharBuffer) {
 195             method = CHARBUFFER_GET;
 196             guard = IS_CHARBUFFER;
 197         } else if (self instanceof ShortBuffer) {
 198             method = SHORTBUFFER_GET;
 199             guard = IS_SHORTBUFFER;
 200         } else if (self instanceof IntBuffer) {
 201             method = INTBUFFER_GET;
 202             guard = IS_INTBUFFER;
 203         } else if (self instanceof LongBuffer) {
 204             method = LONGBUFFER_GET;
 205             guard = IS_LONGBUFFER;
 206         } else if (self instanceof FloatBuffer) {
 207             method = FLOATBUFFER_GET;
 208             guard = IS_FLOATBUFFER;
 209         } else if (self instanceof DoubleBuffer) {
 210             method = DOUBLEBUFFER_GET;
 211             guard = IS_DOUBLEBUFFER;
 212         }
 213 
 214         return method != null? new GuardedInvocation(method, guard) : null;
 215     }
 216 
 217     private static GuardedInvocation linkSetElement(final Object self) {
 218         MethodHandle method = null;
 219         MethodHandle guard = null;
 220         if (self instanceof ByteBuffer) {
 221             method = BYTEBUFFER_PUT;
 222             guard = IS_BYTEBUFFER;
 223         } else if (self instanceof CharBuffer) {
 224             method = CHARBUFFER_PUT;
 225             guard = IS_CHARBUFFER;
 226         } else if (self instanceof ShortBuffer) {
 227             method = SHORTBUFFER_PUT;
 228             guard = IS_SHORTBUFFER;
 229         } else if (self instanceof IntBuffer) {
 230             method = INTBUFFER_PUT;
 231             guard = IS_INTBUFFER;
 232         } else if (self instanceof LongBuffer) {
 233             method = LONGBUFFER_PUT;
 234             guard = IS_LONGBUFFER;
 235         } else if (self instanceof FloatBuffer) {
 236             method = FLOATBUFFER_PUT;
 237             guard = IS_FLOATBUFFER;
 238         } else if (self instanceof DoubleBuffer) {
 239             method = DOUBLEBUFFER_PUT;
 240             guard = IS_DOUBLEBUFFER;
 241         }
 242 
 243         return method != null? new GuardedInvocation(method, guard) : null;
 244     }
 245 
 246     private static GuardedInvocation linkLength() {
 247         return new GuardedInvocation(BUFFER_LIMIT, IS_BUFFER);
 248     }
 249 }