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 java.lang.invoke.MethodHandle;
  33 import java.lang.invoke.MethodHandles;
  34 import java.lang.invoke.MethodType;
  35 import java.util.ArrayList;
  36 import java.util.List;
  37 import java.nio.Buffer;
  38 import java.nio.ByteBuffer;
  39 import java.nio.CharBuffer;
  40 import java.nio.DoubleBuffer;
  41 import java.nio.FloatBuffer;
  42 import java.nio.IntBuffer;
  43 import java.nio.LongBuffer;
  44 import java.nio.ShortBuffer;
  45 import jdk.dynalink.CallSiteDescriptor;
  46 import jdk.dynalink.CompositeOperation;
  47 import jdk.dynalink.NamedOperation;
  48 import jdk.dynalink.Operation;
  49 import jdk.dynalink.StandardOperation;
  50 import jdk.dynalink.linker.GuardingDynamicLinker;
  51 import jdk.dynalink.linker.GuardingDynamicLinkerExporter;
  52 import jdk.dynalink.linker.GuardedInvocation;
  53 import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker;
  54 import jdk.dynalink.linker.LinkRequest;
  55 import jdk.dynalink.linker.LinkerServices;
  56 import jdk.dynalink.linker.support.Guards;
  57 import jdk.dynalink.linker.support.Lookup;
  58 
  59 /**
  60  * This is a dynalink pluggable linker (see http://openjdk.java.net/jeps/276).
  61  * This linker adds array-like indexing and "length" property to nio Buffer objects.
  62  */
  63 public final class BufferIndexingLinkerExporter extends GuardingDynamicLinkerExporter {
  64     static {
  65         System.out.println("pluggable dynalink buffer indexing linker loaded");
  66     }
  67 
  68     private static final MethodHandle BUFFER_LIMIT;
  69     private static final MethodHandle BYTEBUFFER_GET;
  70     private static final MethodHandle BYTEBUFFER_PUT;
  71     private static final MethodHandle CHARBUFFER_GET;
  72     private static final MethodHandle CHARBUFFER_PUT;
  73     private static final MethodHandle SHORTBUFFER_GET;
  74     private static final MethodHandle SHORTBUFFER_PUT;
  75     private static final MethodHandle INTBUFFER_GET;
  76     private static final MethodHandle INTBUFFER_PUT;
  77     private static final MethodHandle LONGBUFFER_GET;
  78     private static final MethodHandle LONGBUFFER_PUT;
  79     private static final MethodHandle FLOATBUFFER_GET;
  80     private static final MethodHandle FLOATBUFFER_PUT;
  81     private static final MethodHandle DOUBLEBUFFER_GET;
  82     private static final MethodHandle DOUBLEBUFFER_PUT;
  83 
  84     // guards
  85     private static final MethodHandle IS_BUFFER;
  86     private static final MethodHandle IS_BYTEBUFFER;
  87     private static final MethodHandle IS_CHARBUFFER;
  88     private static final MethodHandle IS_SHORTBUFFER;
  89     private static final MethodHandle IS_INTBUFFER;
  90     private static final MethodHandle IS_LONGBUFFER;
  91     private static final MethodHandle IS_FLOATBUFFER;
  92     private static final MethodHandle IS_DOUBLEBUFFER;
  93 
  94     private static final MethodType GUARD_TYPE;
  95 
  96     static {
  97         Lookup look = Lookup.PUBLIC;
  98         BUFFER_LIMIT = look.findVirtual(Buffer.class, "limit", MethodType.methodType(int.class));
  99         BYTEBUFFER_GET = look.findVirtual(ByteBuffer.class, "get",
 100                 MethodType.methodType(byte.class, int.class));
 101         BYTEBUFFER_PUT = look.findVirtual(ByteBuffer.class, "put",
 102                 MethodType.methodType(ByteBuffer.class, int.class, byte.class));
 103         CHARBUFFER_GET = look.findVirtual(CharBuffer.class, "get",
 104                 MethodType.methodType(char.class, int.class));
 105         CHARBUFFER_PUT = look.findVirtual(CharBuffer.class, "put",
 106                 MethodType.methodType(CharBuffer.class, int.class, char.class));
 107         SHORTBUFFER_GET = look.findVirtual(ShortBuffer.class, "get",
 108                 MethodType.methodType(short.class, int.class));
 109         SHORTBUFFER_PUT = look.findVirtual(ShortBuffer.class, "put",
 110                 MethodType.methodType(ShortBuffer.class, int.class, short.class));
 111         INTBUFFER_GET = look.findVirtual(IntBuffer.class, "get",
 112                 MethodType.methodType(int.class, int.class));
 113         INTBUFFER_PUT = look.findVirtual(IntBuffer.class, "put",
 114                 MethodType.methodType(IntBuffer.class, int.class, int.class));
 115         LONGBUFFER_GET = look.findVirtual(LongBuffer.class, "get",
 116                 MethodType.methodType(long.class, int.class));
 117         LONGBUFFER_PUT = look.findVirtual(LongBuffer.class, "put",
 118                 MethodType.methodType(LongBuffer.class, int.class, long.class));
 119         FLOATBUFFER_GET = look.findVirtual(FloatBuffer.class, "get",
 120                 MethodType.methodType(float.class, int.class));
 121         FLOATBUFFER_PUT = look.findVirtual(FloatBuffer.class, "put",
 122                 MethodType.methodType(FloatBuffer.class, int.class, float.class));
 123         DOUBLEBUFFER_GET = look.findVirtual(DoubleBuffer.class, "get",
 124                 MethodType.methodType(double.class, int.class));
 125         DOUBLEBUFFER_PUT = look.findVirtual(DoubleBuffer.class, "put",
 126                 MethodType.methodType(DoubleBuffer.class, int.class, double.class));
 127 
 128         GUARD_TYPE = MethodType.methodType(boolean.class, Object.class);
 129         IS_BUFFER = Guards.isInstance(Buffer.class, GUARD_TYPE);
 130         IS_BYTEBUFFER = Guards.isInstance(ByteBuffer.class, GUARD_TYPE);
 131         IS_CHARBUFFER = Guards.isInstance(CharBuffer.class, GUARD_TYPE);
 132         IS_SHORTBUFFER = Guards.isInstance(ShortBuffer.class, GUARD_TYPE);
 133         IS_INTBUFFER = Guards.isInstance(IntBuffer.class, GUARD_TYPE);
 134         IS_LONGBUFFER = Guards.isInstance(LongBuffer.class, GUARD_TYPE);
 135         IS_FLOATBUFFER = Guards.isInstance(FloatBuffer.class, GUARD_TYPE);
 136         IS_DOUBLEBUFFER = Guards.isInstance(DoubleBuffer.class, GUARD_TYPE);
 137     }
 138 
 139     // locate the first standard operation from the call descriptor
 140     private static StandardOperation getFirstStandardOperation(final CallSiteDescriptor desc) {
 141         final Operation base = NamedOperation.getBaseOperation(desc.getOperation());
 142         if (base instanceof StandardOperation) {
 143             return (StandardOperation)base;
 144         } else if (base instanceof CompositeOperation) {
 145             final CompositeOperation cop = (CompositeOperation)base;
 146             for(int i = 0; i < cop.getOperationCount(); ++i) {
 147                 final Operation op = cop.getOperation(i);
 148                 if (op instanceof StandardOperation) {
 149                     return (StandardOperation)op;
 150                 }
 151             }
 152         }
 153         return null;
 154     }
 155 
 156     @Override
 157     public List<GuardingDynamicLinker> get() {
 158         final ArrayList<GuardingDynamicLinker> linkers = new ArrayList<>();
 159         linkers.add(new TypeBasedGuardingDynamicLinker() {
 160             @Override
 161             public boolean canLinkType(final Class<?> type) {
 162                 return Buffer.class.isAssignableFrom(type);
 163             }
 164 
 165             @Override
 166             public GuardedInvocation getGuardedInvocation(LinkRequest request,
 167                 LinkerServices linkerServices) throws Exception {
 168                 final Object self = request.getReceiver();
 169                 if (self == null || !canLinkType(self.getClass())) {
 170                     return null;
 171                 }
 172 
 173                 CallSiteDescriptor desc = request.getCallSiteDescriptor();
 174                 StandardOperation op = getFirstStandardOperation(desc);
 175                 if (op == null) {
 176                     return null;
 177                 }
 178 
 179                 switch (op) {
 180                     case GET_ELEMENT:
 181                         return linkGetElement(self);
 182                     case SET_ELEMENT:
 183                         return linkSetElement(self);
 184                     case GET_PROPERTY: {
 185                         Object name = NamedOperation.getName(desc.getOperation());
 186                         if ("length".equals(name)) {
 187                             return linkLength();
 188                         }
 189                     }
 190                 }
 191 
 192                 return null;
 193             }
 194         });
 195         return linkers;
 196     }
 197 
 198     private static GuardedInvocation linkGetElement(Object self) {
 199         MethodHandle method = null;
 200         MethodHandle guard = null;
 201         if (self instanceof ByteBuffer) {
 202             method = BYTEBUFFER_GET;
 203             guard = IS_BYTEBUFFER;
 204         } else if (self instanceof CharBuffer) {
 205             method = CHARBUFFER_GET;
 206             guard = IS_CHARBUFFER;
 207         } else if (self instanceof ShortBuffer) {
 208             method = SHORTBUFFER_GET;
 209             guard = IS_SHORTBUFFER;
 210         } else if (self instanceof IntBuffer) {
 211             method = INTBUFFER_GET;
 212             guard = IS_INTBUFFER;
 213         } else if (self instanceof LongBuffer) {
 214             method = LONGBUFFER_GET;
 215             guard = IS_LONGBUFFER;
 216         } else if (self instanceof FloatBuffer) {
 217             method = FLOATBUFFER_GET;
 218             guard = IS_FLOATBUFFER;
 219         } else if (self instanceof DoubleBuffer) {
 220             method = DOUBLEBUFFER_GET;
 221             guard = IS_DOUBLEBUFFER;
 222         }
 223 
 224         return method != null? new GuardedInvocation(method, guard) : null;
 225     }
 226 
 227     private static GuardedInvocation linkSetElement(Object self) {
 228         MethodHandle method = null;
 229         MethodHandle guard = null;
 230         if (self instanceof ByteBuffer) {
 231             method = BYTEBUFFER_PUT;
 232             guard = IS_BYTEBUFFER;
 233         } else if (self instanceof CharBuffer) {
 234             method = CHARBUFFER_PUT;
 235             guard = IS_CHARBUFFER;
 236         } else if (self instanceof ShortBuffer) {
 237             method = SHORTBUFFER_PUT;
 238             guard = IS_SHORTBUFFER;
 239         } else if (self instanceof IntBuffer) {
 240             method = INTBUFFER_PUT;
 241             guard = IS_INTBUFFER;
 242         } else if (self instanceof LongBuffer) {
 243             method = LONGBUFFER_PUT;
 244             guard = IS_LONGBUFFER;
 245         } else if (self instanceof FloatBuffer) {
 246             method = FLOATBUFFER_PUT;
 247             guard = IS_FLOATBUFFER;
 248         } else if (self instanceof DoubleBuffer) {
 249             method = DOUBLEBUFFER_PUT;
 250             guard = IS_DOUBLEBUFFER;
 251         }
 252 
 253         return method != null? new GuardedInvocation(method, guard) : null;
 254     }
 255 
 256     private static GuardedInvocation linkLength() {
 257         return new GuardedInvocation(BUFFER_LIMIT, IS_BUFFER);
 258     }
 259 }