1 /*
   2  *  Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  *  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  *  This code is free software; you can redistribute it and/or modify it
   6  *  under the terms of the GNU General Public License version 2 only, as
   7  *  published by the Free Software Foundation.
   8  *
   9  *  This code is distributed in the hope that it will be useful, but WITHOUT
  10  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  *  version 2 for more details (a copy is included in the LICENSE file that
  13  *  accompanied this code).
  14  *
  15  *  You should have received a copy of the GNU General Public License version
  16  *  2 along with this work; if not, write to the Free Software Foundation,
  17  *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  *  Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  *  or visit www.oracle.com if you need additional information or have any
  21  *  questions.
  22  */
  23 
  24 package jdk.internal.foreign;
  25 
  26 import jdk.internal.access.JavaLangInvokeAccess;
  27 import jdk.internal.access.SharedSecrets;
  28 
  29 import java.foreign.layout.Group;
  30 import java.foreign.layout.Layout;
  31 import java.foreign.layout.LayoutPath;
  32 import java.foreign.layout.Sequence;
  33 import java.foreign.layout.Value;
  34 import java.lang.invoke.VarHandle;
  35 import java.util.ArrayList;
  36 import java.util.Collections;
  37 import java.util.List;
  38 import java.util.function.LongSupplier;
  39 import java.util.function.Predicate;
  40 import java.util.stream.Stream;
  41 
  42 public class LayoutPathsImpl {
  43 
  44     private static JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
  45 
  46     public final static class LayoutPathImpl implements LayoutPath {
  47 
  48         final Layout layout;
  49         final LayoutPathImpl enclosing;
  50         final LongSupplier offsetFunc;
  51 
  52         LayoutPathImpl(Layout layout, LayoutPathImpl enclosing, LongSupplier offsetFunc) {
  53             this.layout = layout;
  54             this.enclosing = enclosing;
  55             this.offsetFunc = offsetFunc;
  56         }
  57 
  58         @Override
  59         public Layout layout() {
  60             return layout;
  61         }
  62 
  63         @Override
  64         public LayoutPathImpl enclosing() {
  65             return enclosing;
  66         }
  67 
  68         @Override
  69         public long offset() {
  70             return offsetFunc.getAsLong();
  71         }
  72 
  73         LayoutPath dup(LayoutPath prev) {
  74             return LayoutPathsImpl.of(layout(), prev, () -> prev.offset() + offset());
  75         }
  76 
  77         @Override
  78         public VarHandle dereferenceHandle(Class<?> carrier) {
  79             return JLI.memoryAddressViewVarHandle(carrier, this);
  80         }
  81 
  82         public final List<Sequence> enclosingSequences() {
  83             List<Sequence> enclSeqs = new ArrayList<>();
  84             enclosingSequences(enclSeqs);
  85             Collections.reverse(enclSeqs);
  86             return enclSeqs;
  87         }
  88 
  89         void enclosingSequences(List<Sequence> enclSeqs) {
  90             if (layout instanceof Sequence) {
  91                 enclSeqs.add((Sequence)layout);
  92             }
  93             if (enclosing != ROOT) {
  94                 enclosing.enclosingSequences(enclSeqs);
  95             }
  96         }
  97 
  98         @Override
  99         public Stream<LayoutPath> lookup(Predicate<? super Layout> condition) {
 100             return LayoutPathsImpl.lookup(layout(), condition);
 101         }
 102     }
 103 
 104     static LayoutPath of(Layout layout, LayoutPath prev, LongSupplier offsetFunc) {
 105         return new LayoutPathImpl(layout, (LayoutPathImpl)prev, offsetFunc);
 106     }
 107 
 108     public static LayoutPath of(Layout layout) {
 109         return of(layout, ROOT, () -> 0L);
 110     }
 111 
 112     /**
 113      * Lookup a layout path with given matching predicate.
 114      * @param layout the starting layout.
 115      * @param condition the predicate describing matching layout subelements.
 116      * @return a stream of matching layout subelements.
 117      */
 118     public static Stream<LayoutPath> lookup(Layout layout, Predicate<? super Layout> condition) {
 119         if (layout instanceof Group) {
 120             return lookupGroup((Group)layout, condition);
 121         } else if (layout instanceof Value) {
 122             return lookupContents((Value) layout, condition);
 123         } else {
 124             return Stream.empty();
 125         }
 126     }
 127 
 128     private static Stream<LayoutPath> lookupGroup(Group group, Predicate<? super Layout> condition) {
 129         LayoutPath prev = of(group);
 130         LongSupplier offset = prev::offset;
 131         Stream<LayoutPath> result = Stream.empty();
 132         for (Layout l : group.elements()) {
 133             LayoutPath path = of(l, prev, offset);
 134             if (condition.test(l)) {
 135                 result = Stream.concat(result, Stream.of(path));
 136             }
 137             result = Stream.concat(result,
 138                     lookup(l, condition).map(p -> ((LayoutPathImpl)p).dup(path)));
 139             if (group.kind() != Group.Kind.UNION) {
 140                 LongSupplier offsetPrev = offset;
 141                 offset = () -> offsetPrev.getAsLong() + l.bitsSize();
 142             }
 143         }
 144         return result;
 145     }
 146 
 147     private static Stream<LayoutPath> lookupContents(Value value, Predicate<? super Layout> condition) {
 148         LayoutPath thisPath = of(value);
 149         Stream<LayoutPath> paths = condition.test(value) ?
 150             Stream.of(thisPath) : Stream.empty();
 151         return Stream.concat(paths, value.contents().isPresent() ?
 152                 lookup(value.contents().get(), condition).map(p -> ((LayoutPathImpl)p).dup(thisPath)) :
 153                 Stream.empty());
 154     }
 155 
 156     public static LayoutPath ROOT = of(null, null, () -> 0L);
 157 }