1 /*
   2  *  Copyright (c) 2020, 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.  Oracle designates this
   8  *  particular file as subject to the "Classpath" exception as provided
   9  *  by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  *  This code is distributed in the hope that it will be useful, but WITHOUT
  12  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  *  version 2 for more details (a copy is included in the LICENSE file that
  15  *  accompanied this code).
  16  *
  17  *  You should have received a copy of the GNU General Public License version
  18  *  2 along with this work; if not, write to the Free Software Foundation,
  19  *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  *   Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  *  or visit www.oracle.com if you need additional information or have any
  23  *  questions.
  24  *
  25  */
  26 
  27 package jdk.incubator.jextract.tool;
  28 
  29 import jdk.incubator.foreign.MemoryLayout;
  30 import jdk.incubator.jextract.Declaration;
  31 import jdk.incubator.jextract.Position;
  32 
  33 import java.util.ArrayDeque;
  34 import java.util.Deque;
  35 import java.util.List;
  36 import java.util.stream.Collectors;
  37 
  38 final class GroupNameHandler implements Declaration.Visitor<Declaration, Void> {
  39     private final Deque<Declaration> parents = new ArrayDeque<>();
  40     private int id;
  41 
  42     Declaration.Scoped fillNames(Declaration.Scoped toplevel) {
  43         return (Declaration.Scoped)toplevel.accept(this, null);
  44     }
  45 
  46     @Override
  47     public Declaration visitScoped(Declaration.Scoped d, Void aVoid) {
  48         parents.addFirst(d);
  49         Declaration[] newMembers = d.members().stream()
  50                 .map(decl -> decl.accept(this, null)).toArray(Declaration[]::new);
  51         parents.removeFirst();
  52         return switch (d.kind()) {
  53             case ENUM -> makeGroup(d, newMembers, Declaration::enum_, Declaration::enum_);
  54             case UNION -> makeGroup(d, newMembers, Declaration::union, Declaration::union);
  55             case STRUCT -> makeGroup(d, newMembers, Declaration::struct, Declaration::struct);
  56             case CLASS -> makeGroup(d, newMembers, Declaration::class_, Declaration::class_);
  57             case BITFIELDS -> Declaration.bitfields(d.pos(), d.name(), d.layout().get(),
  58                     List.of(newMembers).toArray(new Declaration.Variable[0]));
  59             case NAMESPACE -> Declaration.namespace(d.pos(), d.name(), newMembers);
  60             case TYPEDEF -> Declaration.typedef(d.pos(), d.name(), newMembers[0]);
  61             case TOPLEVEL -> Declaration.toplevel(d.pos(), newMembers);
  62         };
  63     }
  64 
  65     private interface GroupFactoryNoLayout {
  66         Declaration make(Position pos, String name, Declaration... members);
  67     }
  68 
  69     private interface GroupFactoryLayout {
  70         Declaration make(Position pos, String name, MemoryLayout layout, Declaration... members);
  71     }
  72 
  73     private Declaration makeGroup(Declaration.Scoped d, Declaration[] newMembers, GroupFactoryNoLayout factoryNoLayout, GroupFactoryLayout factoryLayout) {
  74         String name = groupName(d.name());
  75         if (d.layout().isEmpty()) {
  76             return factoryNoLayout.make(d.pos(), name, newMembers);
  77         } else {
  78             return factoryLayout.make(d.pos(), name, d.layout().get().withName(name), newMembers);
  79         }
  80     }
  81 
  82     private String groupName(String name) {
  83         if (!name.isEmpty()) {
  84             return name;
  85         } else {
  86             String prefix = parents.stream()
  87                     .filter(d -> ((Declaration.Scoped)d).kind() != Declaration.Scoped.Kind.TOPLEVEL && !d.name().isEmpty())
  88                     .map(Declaration::name)
  89                     .collect(Collectors.joining("$"));
  90             if (((Declaration.Scoped)parents.peek()).kind() == Declaration.Scoped.Kind.TYPEDEF) {
  91                 return prefix;
  92             } else {
  93                 return prefix + "$" + id++;
  94             }
  95         }
  96     }
  97 
  98     @Override
  99     public Declaration visitDeclaration(Declaration d, Void aVoid) {
 100         return d;
 101     }
 102 }