org.glassfish.pfl.dynamic.codegen.impl.ASMUtil Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://oss.oracle.com/licenses/CDDL+GPL-1.1
* or LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.pfl.dynamic.codegen.impl;
import java.util.Properties ;
import java.util.HashSet ;
import java.util.HashMap ;
import java.io.IOException ;
import java.io.PrintStream ;
import java.io.FileOutputStream ;
import java.io.File ;
import org.glassfish.pfl.objectweb.asm.ClassWriter ;
import org.glassfish.pfl.objectweb.asm.ClassVisitor ;
import org.glassfish.pfl.objectweb.asm.MethodVisitor ;
import org.glassfish.pfl.objectweb.asm.ClassAdapter ;
import org.glassfish.pfl.objectweb.asm.MethodAdapter ;
// Imports for verify method
import org.glassfish.pfl.objectweb.asm.ClassReader ;
import org.glassfish.pfl.objectweb.asm.util.CheckClassAdapter ;
// end of verify method imports
import org.glassfish.pfl.dynamic.codegen.spi.ImportList ;
import org.glassfish.pfl.dynamic.codegen.spi.Type ;
import org.glassfish.pfl.dynamic.codegen.spi.Variable ;
import org.glassfish.pfl.dynamic.codegen.spi.Wrapper ;
import java.io.PrintWriter;
import org.glassfish.pfl.basic.contain.Pair;
import org.glassfish.pfl.basic.func.NullaryFunction;
/** Simple class containing a few ASM-related utilities
* and dynamic attributes needs for the byte code generator.
*/
public class ASMUtil {
public enum RequiredEmitterType { GETTER, SETTER, NONE } ;
public static String bcName( Type type ) {
return type.name().replace( '.', '/' ) ;
}
private static void displayNode( PrintStream ps, String msg, Node node ) {
ps.println( ) ;
ps.println( "=======================================================" ) ;
ps.println( msg ) ;
Util.display( node, ps ) ;
ps.println() ;
Util.checkTree( node, ps ) ;
ps.println() ;
ps.println( "=======================================================" ) ;
}
public static void generateSourceCode( PrintStream ps, ClassGeneratorImpl cg,
ImportList imports, Properties options ) throws IOException {
TreeWalkerContext context = new TreeWalkerContext() ;
Visitor visitor = new SourceStatementVisitor( context,
imports, new CodegenPrinter( ps ) ) ;
cg.accept( visitor ) ;
}
public static File getFile( String genDir, String className,
String suffix ) {
Pair names = Wrapper.splitClassName( className ) ;
String pkgName = names.first().replace( '.', File.separatorChar ) ;
File sdir = new File( genDir, pkgName ) ;
sdir.mkdirs() ; // make sure the directory exists; may return false if already exists.
// Of course, it's not an error if the directory already exists.
String sfname = names.second() + suffix ;
File sfile = new File( sdir, sfname ) ;
return sfile ;
}
public static void generateSourceCode( String sourceGenDir, ClassGeneratorImpl cg,
ImportList imports, Properties options ) throws IOException {
PrintStream ps = null ;
try {
// Create a PrintStream for the source file.
File sfile = getFile( sourceGenDir, cg.name(), ".java" ) ;
ps = new PrintStream( sfile ) ;
// Write out the source code to the source file
generateSourceCode( ps, cg, imports, options ) ;
} finally {
if (ps != null)
ps.close() ;
}
}
/* Requires Apache constantpool package, which is not included in the ORB.
private static final int CLASS_MAGIC = 0xCAFEBABE ;
private static void readConstantPool( PrintStream ps, byte[] cldata ) {
try {
ps.println( "*** Reading constant pool ***" ) ;
ConstantPool cp = new ConstantPool() ;
ByteArrayInputStream bos = new ByteArrayInputStream( cldata ) ;
DataInputStream dis = new DataInputStream( bos ) ;
int magic = dis.readInt() ;
ps.println( "Class magic = " + magic ) ;
if (magic != CLASS_MAGIC) {
ps.println( "Bad magic" ) ;
return ;
}
int minor = dis.readUnsignedShort() ;
int major = dis.readUnsignedShort() ;
ps.println( "Version: " + major + "." + minor ) ;
cp.read( dis, true ) ;
cp.resolve() ;
} catch (Exception exc) {
ps.println( "Error in dumping constant pool: " + exc ) ;
exc.printStackTrace() ;
}
}
*/
private static class FixStackSizeClassVisitor extends ClassAdapter {
public FixStackSizeClassVisitor( final ClassVisitor cv ) {
super( cv ) ;
}
public MethodVisitor visitMethod( final int access, final String name,
final String desc, final String signature, final String[] exceptions ) {
MethodVisitor mv = cv.visitMethod( access, name, desc, signature, exceptions ) ;
return mv == null ? null : new FixStackSizeMethodVisitor( mv ) ;
}
}
private static class FixStackSizeMethodVisitor extends MethodAdapter {
public FixStackSizeMethodVisitor( final MethodVisitor mv ) {
super( mv ) ;
}
public void visitMaxs( int maxStack, int maxLocals ) {
// Make ASM calculate the stack size
mv.visitMaxs( 0, 0 ) ;
}
}
private static byte[] fixStackSize( byte[] code ) {
// Debugging code: try to read/write it again to see what happens with
// max stack size
ClassReader cr = new ClassReader( code );
ClassWriter cw = new ClassWriter( ClassWriter.COMPUTE_MAXS ) ;
ClassVisitor visitor = new FixStackSizeClassVisitor( cw ) ;
cr.accept( visitor, 0 ) ;
return cw.toByteArray() ;
}
/** Given a completed ClassGeneratorImpl, use ASM to construct
* the byte array representing the compiled class.
*/
public static byte[] generate( ClassLoader cl, ClassGeneratorImpl cg,
ImportList imports, Properties options, PrintStream debugOutput ) {
// Make sure that ClassLoader cl is used where required (mainly in the
// implementation of Type.classInfo, which is used in several places).
CurrentClassLoader.set( cl ) ;
// get options
boolean dumpConstantPool = false ;
boolean dumpAfterSetupVisitor = false ;
boolean traceByteCodeGeneration = false ;
boolean useAsmVerifier = false ;
String classGenDir = null ;
String sourceGenDir = null ;
if (options != null) {
dumpConstantPool = Boolean.parseBoolean(
options.getProperty( Wrapper.DUMP_CONSTANT_POOL )) ;
dumpAfterSetupVisitor = Boolean.parseBoolean(
options.getProperty( Wrapper.DUMP_AFTER_SETUP_VISITOR )) ;
traceByteCodeGeneration = Boolean.parseBoolean(
options.getProperty( Wrapper.TRACE_BYTE_CODE_GENERATION )) ;
useAsmVerifier = Boolean.parseBoolean(
options.getProperty( Wrapper.USE_ASM_VERIFIER )) ;
classGenDir = options.getProperty(
Wrapper.CLASS_GENERATION_DIRECTORY ) ;
sourceGenDir = options.getProperty(
Wrapper.SOURCE_GENERATION_DIRECTORY ) ;
}
if (sourceGenDir != null) {
try {
generateSourceCode( sourceGenDir, cg, imports, options ) ;
} catch (IOException exc) {
throw new IllegalArgumentException(
"Could not generate source code for class "
+ cg.name(), exc ) ;
}
}
// have ASM compute max stack size
ClassWriter cw = new ClassWriter( ClassWriter.COMPUTE_MAXS ) ;
// Prepare the tree for byte code generation. We use a fresh
// TreeWalker context for each pass with a visitor.
TreeWalkerContext twc = new TreeWalkerContext() ;
Visitor v1 = new ASMSetupVisitor( twc ) ;
cg.accept( v1 ) ;
if (dumpAfterSetupVisitor)
displayNode( debugOutput, "Contents of AST after SetupVisitor", cg ) ;
// generate byte code
twc = new TreeWalkerContext() ;
Visitor v2 = new ASMByteCodeVisitor( twc, cw, traceByteCodeGeneration,
debugOutput ) ;
cg.accept( v2 ) ;
byte[] result = fixStackSize( cw.toByteArray() ) ;
if (dumpConstantPool) {
// readConstantPool( debugOutput, result ) ;
}
if (classGenDir != null) {
// Dump the generate bytecode to a directory for debugging.
File cfile = getFile( classGenDir, cg.name(), ".class" ) ;
FileOutputStream fos = null ;
try {
fos = new FileOutputStream( cfile ) ;
fos.write( result, 0, result.length ) ;
} catch (IOException exc) {
throw new IllegalArgumentException(
"Could not dump generated bytecode to file "
+ cfile ) ;
} finally {
if (fos != null)
try {
fos.close() ;
} catch (IOException exc) {
// ignore this
}
}
}
if (useAsmVerifier) {
debugOutput.println( "*** Using ASM verifier ***" ) ;
verify( debugOutput, result ) ;
}
return result ;
}
private static void verify( final PrintStream ps, byte[] classData ) {
ClassReader cr = new ClassReader( classData ) ;
PrintWriter pw = new PrintWriter( ps ) ;
CheckClassAdapter.verify( cr, true, pw ) ;
// pw.close();
}
// Function used to initialize Attribute instances.
private static NullaryFunction makeLabel =
new NullaryFunction() {
public MyLabel evaluate() {
return new MyLabel() ;
}
} ;
// All attributes are package private so that they can be
// used in other parts of the codegen implementation.
// Attribute on MethodGenerator that defines the label on the
// return instruction at the end of the method.
static Attribute returnLabel = new Attribute(
MyLabel.class, "returnLabel", makeLabel ) ;
// Attribute on Statement nodes that labels the start of
// the statement.
static Attribute statementStartLabel = new Attribute(
MyLabel.class, "statementStartLabel", makeLabel ) ;
static Attribute statementEndLabel = new Attribute(
MyLabel.class, "statementEndLabel", makeLabel ) ;
// Attribute on BlockStatements in TryStatements used to label
// the end of the Block. Needed for generating exception table.
static Attribute throwEndLabel = new Attribute(
MyLabel.class, "throwEndLabel", makeLabel ) ;
// Attribute on all Statement nodes that gives the start of the
// sequentially next statement immediately after the current
// statement if any. This is only set if the parent node has
// a local next statement (e.g. BlockStatement).
static Attribute next = new Attribute(
Node.class, "next", (Node)null ) ;
static Attribute returnVariable = new Attribute(
Variable.class, "returnVariable", (Variable)null ) ;
// Variable attributes
// All local Variable definitions have this attribute which defines where
// they are allocated in the stack frame.
static Attribute stackFrameSlot = new Attribute(
Integer.class, "stackFrameSlot", 0 ) ;
// All Variable definitions have a getEmitter attribute which defines
// how to get the value of the Variable.
static Attribute getEmitter = new Attribute(
EmitterFactory.Emitter.class, "getEmitter", (EmitterFactory.Emitter)null ) ;
// All Variable definitions have a getEmitter attribute which defines
// how to set the value of the Variable.
static Attribute setEmitter = new Attribute(
EmitterFactory.Emitter.class, "setEmitter", (EmitterFactory.Emitter)null ) ;
// All assignable expression nodes have an emitter attribute which defines
// what operation (load or store) is needed when that reference is visited.
static Attribute emitter = new Attribute(
EmitterFactory.Emitter.class, "emitter", (EmitterFactory.Emitter)null ) ;
// Indicates whether a variable needs to emit a setter, a getter, or no
// code at all when visited for code generation.
static Attribute requiredEmitterType = new Attribute(
RequiredEmitterType.class, "requiredEmitterType", RequiredEmitterType.GETTER ) ;
// Used in ASMByteCodeVisitor to track the last statement visited in a
// BlockStatement
static Attribute lastStatement = new Attribute(
Statement.class, "lastStatement", (Statement)null ) ;
// Used to hold the exception for the uncaught exception handler when
// generating code for a try statement with a finally block.
static Attribute uncaughtException = new Attribute(
Variable.class, "uncaughtException", (Variable)null ) ;
// Used to hold the local variable that holds the return address for
// a finally block.
static Attribute returnAddress = new Attribute(
Variable.class, "returnAddress", (Variable)null ) ;
// Used to track the last BlockStatement visited while generating
// bytecode for a TryStatement.
static Attribute lastBlock = new Attribute(
BlockStatement.class, "lastBlock", (BlockStatement)null ) ;
// Used to label the start of the uncaught exception handler.
static Attribute uncaughtExceptionHandler = new Attribute(
MyLabel.class, "uncaughtExceptionHandler", makeLabel ) ;
static Attribute ctr = new Attribute(
Integer.class, "ctr", 0 ) ;
public static class LineNumberTable extends HashMap {
public LineNumberTable() {
super() ;
}
}
private static NullaryFunction tableMaker =
new NullaryFunction() {
public LineNumberTable evaluate() {
return new LineNumberTable() ;
}
} ;
// Attribute on MethodGenerator that contains the LineNumberTable.
static Attribute lineNumberTable = new Attribute(
LineNumberTable.class, "lineNumberTable", tableMaker ) ;
public static class VariablesInMethod extends HashSet {
public VariablesInMethod() {
super() ;
}
}
private static NullaryFunction vmMaker =
new NullaryFunction() {
public VariablesInMethod evaluate() {
return new VariablesInMethod() ;
}
} ;
static Attribute variablesInMethod = new Attribute(
VariablesInMethod.class, "variablesInMethod", vmMaker ) ;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy