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 package hat.optools;
 26 
 27 import hat.buffer.Buffer;
 28 
 29 import hat.ifacemapper.MappableIface;
 30 import jdk.incubator.code.Block;
 31 import jdk.incubator.code.Body;
 32 import jdk.incubator.code.Op;
 33 import jdk.incubator.code.TypeElement;
 34 import jdk.incubator.code.Value;
 35 import jdk.incubator.code.op.CoreOp;
 36 import jdk.incubator.code.op.ExtendedOp;
 37 import jdk.incubator.code.type.ClassType;
 38 import jdk.incubator.code.type.JavaType;
 39 
 40 import java.lang.invoke.MethodHandles;
 41 import java.lang.reflect.Type;
 42 import java.util.ArrayList;
 43 import java.util.List;
 44 import java.util.function.Consumer;
 45 import java.util.stream.Stream;
 46 
 47 public class OpWrapper<T extends Op> {
 48     @SuppressWarnings("unchecked")
 49     public static <O extends Op, OW extends OpWrapper<O>> OW wrap(O op) {
 50         // We have one special case
 51         // This is possibly a premature optimization. But it allows us to treat vardeclarations differently from params.
 52         if (op instanceof CoreOp.VarOp varOp && !varOp.isUninitialized()) {
 53             // this gets called a lot and we can't wrap yet or we recurse so we
 54             // use the raw model. Basically we want a different wrapper for VarDeclations
 55             // which  relate to func parameters.
 56             // This saves us asking each time if a var is indeed a func param.
 57 
 58             if (varOp.operands().getFirst() instanceof Block.Parameter parameter &&
 59                     parameter.invokableOperation() instanceof CoreOp.FuncOp funcOp) {
 60                 return (OW) new VarFuncDeclarationOpWrapper(varOp, funcOp, parameter);
 61             }
 62         }
 63         return switch (op) {
 64             case CoreOp.ModuleOp $ -> (OW) new ModuleOpWrapper($);
 65             case ExtendedOp.JavaForOp $ -> (OW) new ForOpWrapper($);
 66             case ExtendedOp.JavaWhileOp $ -> (OW) new WhileOpWrapper($);
 67             case ExtendedOp.JavaIfOp $ -> (OW) new IfOpWrapper($);
 68             case CoreOp.NotOp $ -> (OW) new UnaryArithmeticOrLogicOpWrapper($);
 69             case CoreOp.NegOp $ -> (OW) new UnaryArithmeticOrLogicOpWrapper($);
 70             case CoreOp.BinaryOp $ -> (OW) new BinaryArithmeticOrLogicOperation($);
 71             case CoreOp.BinaryTestOp $ -> (OW) new BinaryTestOpWrapper($);
 72             case CoreOp.FuncOp $ -> (OW) new FuncOpWrapper($);
 73             case CoreOp.VarOp $ -> (OW) new VarDeclarationOpWrapper($);
 74             case CoreOp.YieldOp $ -> (OW) new YieldOpWrapper($);
 75             case CoreOp.FuncCallOp $ -> (OW) new FuncCallOpWrapper($);
 76             case CoreOp.ConvOp $ -> (OW) new ConvOpWrapper($);
 77             case CoreOp.ConstantOp $ -> (OW) new ConstantOpWrapper($);
 78             case CoreOp.ReturnOp $ -> (OW) new ReturnOpWrapper($);
 79             case CoreOp.VarAccessOp.VarStoreOp $ -> (OW) new VarStoreOpWrapper($);
 80             case CoreOp.VarAccessOp.VarLoadOp $ -> (OW) new VarLoadOpWrapper($);
 81             case CoreOp.FieldAccessOp.FieldStoreOp $ -> (OW) new FieldStoreOpWrapper($);
 82             case CoreOp.FieldAccessOp.FieldLoadOp $ -> (OW) new FieldLoadOpWrapper($);
 83             case CoreOp.InvokeOp $ -> (OW) new InvokeOpWrapper($);
 84             case CoreOp.TupleOp $ -> (OW) new TupleOpWrapper($);
 85             case CoreOp.LambdaOp $ -> (OW) new LambdaOpWrapper($);
 86             case ExtendedOp.JavaConditionalOp $ -> (OW) new LogicalOpWrapper($);
 87             case ExtendedOp.JavaConditionalExpressionOp $ -> (OW) new TernaryOpWrapper($);
 88             case ExtendedOp.JavaLabeledOp $ -> (OW) new JavaLabeledOpWrapper($);
 89             case ExtendedOp.JavaBreakOp $ -> (OW) new JavaBreakOpWrapper($);
 90             case ExtendedOp.JavaContinueOp $ -> (OW) new JavaContinueOpWrapper($);
 91             default -> (OW) new OpWrapper<>(op);
 92         };
 93     }
 94 
 95     private final T op;
 96 
 97     OpWrapper(T op) {
 98         this.op = op;
 99     }
100 
101     public T op() {
102         return (T) op;
103     }
104 
105     public Body firstBody() {
106         if (op.bodies().isEmpty()) {
107             throw new IllegalStateException("no body!");
108         }
109         return op.bodies().getFirst();
110     }
111 
112     public Body onlyBody() {
113         if (op.bodies().size() != 1) {
114             throw new IllegalStateException("not the only body!");
115         }
116         return firstBody();
117     }
118 
119     public void onlyBody(Consumer<BodyWrapper> bodyWrapperConsumer) {
120         bodyWrapperConsumer.accept(new BodyWrapper(onlyBody()));
121     }
122 
123     public final Stream<Body> bodies() {
124         return op.bodies().stream();
125     }
126 
127     public void selectOnlyBlockOfOnlyBody(Consumer<BlockWrapper> blockWrapperConsumer) {
128         onlyBody(w -> {
129             w.onlyBlock(blockWrapperConsumer);
130         });
131     }
132 
133     public void selectCalls(Consumer<InvokeOpWrapper> consumer) {
134         this.op.traverse(null, (map, op) -> {
135             if (op instanceof CoreOp.InvokeOp invokeOp) {
136                 consumer.accept(wrap(invokeOp));
137             }
138             return map;
139         });
140     }
141     public void selectAssignments(Consumer<VarOpWrapper> consumer) {
142         this.op.traverse(null, (map, op) -> {
143             if (op instanceof CoreOp.VarOp varOp) {
144                 consumer.accept(wrap(varOp));
145             }
146             return map;
147         });
148     }
149 
150     public BlockWrapper parentBlock() {
151         return new BlockWrapper(op.parentBlock());
152     }
153 
154     public BodyWrapper parentBodyOfParentBlock() {
155         return parentBlock().parentBody();
156     }
157 
158 
159     public Op.Result operandNAsResult(int i) {
160         if (operandNAsValue(i) instanceof Op.Result result) {
161             return result;
162         } else {
163             return null;
164         }
165     }
166 
167     public Value operandNAsValue(int i) {
168         return hasOperandN(i) ? op().operands().get(i) : null;
169     }
170 
171     public boolean hasOperandN(int i) {
172         return operandCount() > i;
173     }
174 
175     public int operandCount() {
176         return op().operands().size();
177     }
178 
179     public boolean hasOperands() {
180         return !hasNoOperands();
181     }
182 
183     public boolean hasNoOperands() {
184         return operands().isEmpty();
185     }
186 
187     public List<Value> operands() {
188         return op.operands();
189     }
190 
191     public Body bodyN(int i) {
192         return op().bodies().get(i);
193     }
194 
195     public boolean hasBodyN(int i) {
196         return op().bodies().size() > i;
197     }
198 
199     public Block firstBlockOfBodyN(int i) {
200         return bodyN(i).entryBlock();
201     }
202 
203     public Block firstBlockOfFirstBody() {
204         return op().bodies().getFirst().entryBlock();
205     }
206 
207     public String toText() {
208         return op().toText();
209     }
210 
211     public Stream<OpWrapper<?>> wrappedOpStream(Block block) {
212         return block.ops().stream().map(OpWrapper::wrap);
213     }
214 
215     public Stream<OpWrapper<?>> wrappedYieldOpStream(Block block) {
216         return wrappedOpStream(block).filter(wrapped -> wrapped instanceof YieldOpWrapper);
217     }
218 
219     private Stream<OpWrapper<?>> roots(Block block) {
220         var rootSet = RootSet.getRootSet(block.ops().stream());
221         return block.ops().stream().filter(rootSet::contains).map(OpWrapper::wrap);
222     }
223 
224     private Stream<OpWrapper<?>> rootsWithoutVarFuncDeclarations(Block block) {
225         return roots(block).filter(w -> !(w instanceof VarFuncDeclarationOpWrapper));
226     }
227 
228     private Stream<OpWrapper<?>> rootsWithoutVarFuncDeclarationsOrYields(Block block) {
229         return rootsWithoutVarFuncDeclarations(block).filter(w -> !(w instanceof YieldOpWrapper));
230     }
231 
232     public Stream<OpWrapper<?>> wrappedRootOpStream(Block block) {
233         return rootsWithoutVarFuncDeclarationsOrYields(block);
234     }
235 
236     public Stream<OpWrapper<?>> wrappedRootOpStreamSansFinalContinue(Block block) {
237         var list = new ArrayList<>(rootsWithoutVarFuncDeclarationsOrYields(block).toList());
238         if (list.getLast() instanceof JavaContinueOpWrapper javaContinueOpWrapper) {
239             list.removeLast();
240         }
241         return list.stream();
242     }
243 
244     public Op.Result result() {
245         return op.result();
246     }
247 
248     public TypeElement resultType() {
249         return op.resultType();
250     }
251 
252     public static boolean isIface(JavaType javaType){
253         return  (isAssignable(javaType, MappableIface.class));
254     }
255     public static Type classTypeToType(ClassType classType){
256         Type javaTypeClass = null;
257         try {
258             javaTypeClass = classType.resolve(MethodHandles.lookup());
259         } catch (ReflectiveOperationException e) {
260             throw new RuntimeException(e);
261         }
262         return javaTypeClass;
263 
264     }
265     public static boolean isAssignable(JavaType javaType, Class<?> ... classes) {
266         if (javaType instanceof ClassType classType) {
267             Type type = classTypeToType(classType);
268             for (Class<?> clazz : classes) {
269                 if (clazz.isAssignableFrom((Class<?>) type)) {
270                         return true;
271                 }
272             }
273         }
274         return false;
275 
276     }
277 }