| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.drill.exec.compile; |
| |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| |
| import org.objectweb.asm.ClassReader; |
| import org.objectweb.asm.ClassWriter; |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.tree.ClassNode; |
| import org.objectweb.asm.util.TraceClassVisitor; |
| import org.slf4j.Logger; |
| |
| /** |
| * Utilities commonly used with ASM. |
| * |
| * <p>There are several class verification utilities which use |
| * CheckClassAdapter (DrillCheckClassAdapter) to ensure classes are well-formed; |
| * these are packaged as boolean functions so that they can be used in assertions. |
| */ |
| public class AsmUtil { |
| // This class only contains static utilities. |
| private AsmUtil() { |
| } |
| |
| /** |
| * Check to see if a class is well-formed. |
| * |
| * @param logger the logger to write to if a problem is found |
| * @param logTag a tag to print to the log if a problem is found |
| * @param classNode the class to check |
| * @return true if the class is ok, false otherwise |
| */ |
| public static boolean isClassOk(final Logger logger, final String logTag, final ClassNode classNode) { |
| final StringWriter sw = new StringWriter(); |
| final ClassWriter verifyWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); |
| classNode.accept(verifyWriter); |
| final ClassReader ver = new ClassReader(verifyWriter.toByteArray()); |
| try { |
| DrillCheckClassAdapter.verify(ver, false, new PrintWriter(sw)); |
| } catch(final Exception e) { |
| logger.info("Caught exception verifying class:"); |
| logClass(logger, logTag, classNode); |
| throw e; |
| } |
| final String output = sw.toString(); |
| if (!output.isEmpty()) { |
| logger.info("Invalid class:\n" + output); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Check to see if a class is well-formed. |
| * |
| * @param logger the logger to write to if a problem is found |
| * @param logTag a tag to print to the log if a problem is found |
| * @param classBytes the bytecode of the class to check |
| * @return true if the class is ok, false otherwise |
| */ |
| public static boolean isClassBytesOk(final Logger logger, final String logTag, final byte[] classBytes) { |
| final ClassNode classNode = classFromBytes(classBytes, 0); |
| return isClassOk(logger, logTag, classNode); |
| } |
| |
| /** |
| * Create a ClassNode from bytecode. |
| * |
| * @param classBytes the bytecode |
| * @param asmReaderFlags flags for ASM; see {@link org.objectweb.asm.ClassReader#accept(org.objectweb.asm.ClassVisitor, int)} |
| * @return the ClassNode |
| */ |
| public static ClassNode classFromBytes(final byte[] classBytes, final int asmReaderFlags) { |
| final ClassNode classNode = new ClassNode(CompilationConfig.ASM_API_VERSION); |
| final ClassReader classReader = new ClassReader(classBytes); |
| classReader.accept(classNode, asmReaderFlags); |
| return classNode; |
| } |
| |
| /** |
| * Write a class to the log. |
| * |
| * <p> |
| * Writes at level TRACE. |
| * |
| * @param logger |
| * the logger to write to |
| * @param logTag |
| * a tag to print to the log |
| * @param classNode |
| * the class |
| */ |
| public static void logClass(final Logger logger, final String logTag, final ClassNode classNode) { |
| if (logger.isTraceEnabled()) { |
| logger.trace(logTag); |
| final StringWriter stringWriter = new StringWriter(); |
| final PrintWriter printWriter = new PrintWriter(stringWriter); |
| final TraceClassVisitor traceClassVisitor = new TraceClassVisitor(printWriter); |
| classNode.accept(traceClassVisitor); |
| logger.trace(stringWriter.toString()); |
| } |
| } |
| |
| /** |
| * Write a class to the log. |
| * |
| * <p>Writes at level DEBUG. |
| * |
| * @param logTag a tag to print to the log |
| * @param classBytes the class' bytecode |
| * @param logger the logger to write to |
| */ |
| public static void logClassFromBytes(final Logger logger, final String logTag, final byte[] classBytes) { |
| final ClassNode classNode = classFromBytes(classBytes, 0); |
| logClass(logger, logTag, classNode); |
| } |
| |
| /** |
| * Determine if the given opcode is a load of a constant (xCONST_y). |
| * |
| * @param opcode the opcode |
| * @return true if the opcode is one of the constant loading ones, false otherwise |
| */ |
| public static boolean isXconst(final int opcode) { |
| switch(opcode) { |
| case Opcodes.ICONST_0: |
| case Opcodes.ICONST_1: |
| case Opcodes.ICONST_2: |
| case Opcodes.ICONST_3: |
| case Opcodes.ICONST_4: |
| case Opcodes.ICONST_5: |
| case Opcodes.ICONST_M1: |
| case Opcodes.DCONST_0: |
| case Opcodes.DCONST_1: |
| case Opcodes.FCONST_0: |
| case Opcodes.FCONST_1: |
| case Opcodes.LCONST_0: |
| case Opcodes.LCONST_1: |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Determine if the given opcode is an ADD of some kind (xADD). |
| * |
| * @param opcode the opcode |
| * @return true if the opcode is one of the ADDs, false otherwise |
| */ |
| public static boolean isXadd(final int opcode) { |
| switch(opcode) { |
| case Opcodes.IADD: |
| case Opcodes.DADD: |
| case Opcodes.FADD: |
| case Opcodes.LADD: |
| return true; |
| } |
| |
| return false; |
| } |
| } |