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.util.stream.DoubleStream;
  38 import java.util.stream.IntStream;
  39 import java.util.stream.LongStream;
  40 import java.util.stream.Stream;
  41 import jdk.dynalink.CallSiteDescriptor;
  42 import jdk.dynalink.CompositeOperation;
  43 import jdk.dynalink.NamedOperation;
  44 import jdk.dynalink.Operation;
  45 import jdk.dynalink.StandardOperation;
  46 import jdk.dynalink.linker.GuardingDynamicLinker;
  47 import jdk.dynalink.linker.GuardingDynamicLinkerExporter;
  48 import jdk.dynalink.linker.GuardedInvocation;
  49 import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker;
  50 import jdk.dynalink.linker.LinkRequest;
  51 import jdk.dynalink.linker.LinkerServices;
  52 import jdk.dynalink.linker.support.Guards;
  53 import jdk.dynalink.linker.support.Lookup;
  54 
  55 /**
  56  * This is a dynalink pluggable linker (see http://openjdk.java.net/jeps/276).
  57  * This linker adds "stream" property to Java arrays. The appropriate Stream
  58  * type object is returned for "stream" property on Java arrays. Note that
  59  * the dynalink beans linker just adds "length" property and Java array objects
  60  * don't have any other property. "stream" property does not conflict with anything
  61  * else!
  62  */
  63 public final class ArrayStreamLinkerExporter extends GuardingDynamicLinkerExporter {
  64     static {
  65         System.out.println("pluggable dynalink array stream linker loaded");
  66     }
  67 
  68     public static Object arrayToStream(Object array) {
  69         if (array instanceof int[]) {
  70             return IntStream.of((int[])array);
  71         } else if (array instanceof long[]) {
  72             return LongStream.of((long[])array);
  73         } else if (array instanceof double[]) {
  74             return DoubleStream.of((double[])array);
  75         } else if (array instanceof Object[]) {
  76             return Stream.of((Object[])array);
  77         } else {
  78             throw new IllegalArgumentException();
  79         }
  80     }
  81 
  82     private static final MethodType GUARD_TYPE = MethodType.methodType(Boolean.TYPE, Object.class);
  83     private static final MethodHandle ARRAY_TO_STREAM = Lookup.PUBLIC.findStatic(
  84             ArrayStreamLinkerExporter.class, "arrayToStream",
  85             MethodType.methodType(Object.class, Object.class));
  86 
  87     @Override
  88     public List<GuardingDynamicLinker> get() {
  89         final ArrayList<GuardingDynamicLinker> linkers = new ArrayList<>();
  90         linkers.add(new TypeBasedGuardingDynamicLinker() {
  91             @Override
  92             public boolean canLinkType(final Class<?> type) {
  93                 return type == Object[].class || type == int[].class ||
  94                        type == long[].class || type == double[].class;
  95             }
  96 
  97             @Override
  98             public GuardedInvocation getGuardedInvocation(LinkRequest request,
  99                 LinkerServices linkerServices) throws Exception {
 100                 final Object self = request.getReceiver();
 101                 if (self == null || !canLinkType(self.getClass())) {
 102                     return null;
 103                 }
 104 
 105                 CallSiteDescriptor desc = request.getCallSiteDescriptor();
 106                 Operation op = desc.getOperation();
 107                 Object name = NamedOperation.getName(op);
 108                 boolean getProp = CompositeOperation.contains(
 109                         NamedOperation.getBaseOperation(op),
 110                         StandardOperation.GET_PROPERTY);
 111                 if (getProp && name instanceof String) {
 112                     String nameStr = (String)name;
 113                     if (nameStr.equals("stream")) {
 114                         return new GuardedInvocation(ARRAY_TO_STREAM,
 115                             Guards.isOfClass(self.getClass(), GUARD_TYPE));
 116                     }
 117                 }
 118 
 119                 return null;
 120             }
 121         });
 122         return linkers;
 123     }
 124 }