1 /*
  2  * Copyright (c) 2024, 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 package jdk.incubator.code;
 27 
 28 import java.util.Objects;
 29 import java.util.function.BiFunction;
 30 
 31 /**
 32  * An operation transformer.
 33  */
 34 @FunctionalInterface
 35 public interface OpTransformer extends BiFunction<Block.Builder, Op, Block.Builder> {
 36     /**
 37      * A copying transformer that applies the operation to the block builder, and returning the block builder.
 38      */
 39     OpTransformer COPYING_TRANSFORMER = (block, op) -> {
 40         block.op(op);
 41         return block;
 42     };
 43 
 44     /**
 45      * A transformer that performs no action on the block builder.
 46      */
 47     OpTransformer NOOP_TRANSFORMER = (block, op) -> block;
 48 
 49     /**
 50      * A transformer that drops location information from operations.
 51      */
 52     OpTransformer DROP_LOCATION_TRANSFORMER = (block, op) -> {
 53         Op.Result r = block.op(op);
 54         r.op().setLocation(Location.NO_LOCATION);
 55         return block;
 56     };
 57 
 58     /**
 59      * A transformer that lowers operations that are {@link Op.Lowerable lowerable},
 60      * and copies other operations.
 61      */
 62     OpTransformer LOWERING_TRANSFORMER = (block, op) -> {
 63         if (op instanceof Op.Lowerable lop) {
 64             return lop.lower(block);
 65         } else {
 66             block.op(op);
 67             return block;
 68         }
 69     };
 70 
 71     /**
 72      * Transforms a given operation to zero or more other operations appended to the
 73      * given block builder. Returns a block builder to be used for appending further operations, such
 74      * as subsequent operations from the same block as the given operation.
 75      *
 76      * @param block the block builder.
 77      * @param op    the operation to transform.
 78      * @return      the block builder to append to for subsequent operations to transform that have same parent block.
 79      */
 80     Block.Builder apply(Block.Builder block, Op op);
 81 
 82     /**
 83      * Transforms a given block to zero or more operations appended to the given block builder.
 84      *
 85      * @implSpec
 86      * The default implementation iterates through each operation of the block to transform
 87      * and {@link #apply(Block.Builder, Op) applies} a block builder and the operation to this
 88      * transformer.
 89      * On first iteration the block builder that is applied is block builder passed as an argument
 90      * to this method.
 91      * On second and subsequent iterations the block builder that is applied is the resulting
 92      * block builder of the prior iteration.
 93      *
 94      * @param block the block builder
 95      * @param b     the block to transform
 96      * @throws NullPointerException if a resulting block builder is null
 97      */
 98     default void apply(Block.Builder block, Block b) {
 99         for (Op op : b.ops()) {
100             block = apply(block, op);
101             // @@@ See andThen composition
102             Objects.requireNonNull(block);
103         }
104     }
105 
106     default OpTransformer compose(OpTransformer before) {
107         return before.andThen(this);
108     }
109 
110     default OpTransformer andThen(OpTransformer after) {
111         if (after == NOOP_TRANSFORMER) {
112             return this;
113         } else if (this == NOOP_TRANSFORMER) {
114             return after;
115         } else {
116             return (bb, o) -> {
117                 Block.Builder nbb = apply(bb, o);
118                 if (nbb != null) {
119                     return after.apply(nbb, o);
120                 } else {
121                     // @@@ This does not currently occur
122                     return null;
123                 }
124             };
125         }
126     }
127 }