All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.pfl.dynamic.codegen.spi.Wrapper Maven / Gradle / Ivy

There is a newer version: 5.0.0
Show newest version
/*
 * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0, which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

package org.glassfish.pfl.dynamic.codegen.spi;

import static java.util.Arrays.asList;

import java.io.IOException;
import java.io.PrintStream;
import java.security.ProtectionDomain;
import java.util.List;
import java.util.Properties;
import java.util.Stack;
import org.glassfish.pfl.basic.contain.Pair;
import org.glassfish.pfl.basic.fsm.FSM;
import org.glassfish.pfl.basic.fsm.FSMImpl;
import org.glassfish.pfl.basic.fsm.Input;
import org.glassfish.pfl.basic.fsm.Runner;
import org.glassfish.pfl.basic.fsm.State;
import org.glassfish.pfl.basic.fsm.StateEngine;
import org.glassfish.pfl.dynamic.codegen.impl.BlockStatement;
import org.glassfish.pfl.dynamic.codegen.impl.ClassGeneratorImpl;
import org.glassfish.pfl.dynamic.codegen.impl.CodeGenerator;
import org.glassfish.pfl.dynamic.codegen.impl.CodeGeneratorUtil;
import org.glassfish.pfl.dynamic.codegen.impl.CurrentClassLoader;
import org.glassfish.pfl.dynamic.codegen.impl.ExpressionFactory;
import org.glassfish.pfl.dynamic.codegen.impl.FieldGenerator;
import org.glassfish.pfl.dynamic.codegen.impl.Identifier;
import org.glassfish.pfl.dynamic.codegen.impl.IfStatement;
import org.glassfish.pfl.dynamic.codegen.impl.ImportListImpl;
import org.glassfish.pfl.dynamic.codegen.impl.MethodGenerator;
import org.glassfish.pfl.dynamic.codegen.impl.SwitchStatement;
import org.glassfish.pfl.dynamic.codegen.impl.TryStatement;
import org.glassfish.pfl.dynamic.codegen.impl.Util;
import org.glassfish.pfl.dynamic.codegen.impl.WhileStatement;
import org.glassfish.pfl.dynamic.copyobject.impl.ClassCopierOrdinaryImpl;

/** Main API for runtime code generation.
 * This API generates bytecode dynamically at runtime, allowing direct construction
 * of Class instances from the generated bytecodes.  Such classes can then
 * be instantiated and used in a running program. This can be used for a variety of
 * advanced applications aimed at extending the Java language or providing
 * significant performance enhancements.
 * 

* The supported language is best thought of as a subset of Java 1.0.2. * The following features from Java 5 and earlier are currently NOT supported: *

    *
  • Generics. *
  • Annotations (this may be added at some point). *
  • For loops (of any sort, but this may be added through extension at some * point). *
  • Enums. *
  • Autoboxing. *
  • Varargs methods. *
  • Nested classes. *
  • Compiler generated default constructors. *
  • Compiler generated calls to super() in a constructor. *
  • Anonymous classes. *
  • do loops. *
  • Static fields in interfaces. *
  • Labels, or labelled break or continue. *
* The intent is that dynamically generated code should be a simplified form of * Java, much like the original Java 1.0 definition. This moves dynamic code * generation to a level much closer to Java than to raw bytecode manipulation. *

* As much as possible, this class uses standard Java keywords prefixed by "_" as * method names. This helps to remember what the method name is for creating * an if statement (_if of course). All public methods are static, and * using static imports for this class is advisable: simple use *

 * import static org.glassfish.dynamic.codegen.spi.Wrapper.*
 * 
* to get easy access to all of the _xxx methods. *

A typical use of this class looks something like: *

 * _clear() ;		    // clean up any state from previous uses
 *			    // optional:
 * _setClassLoader(cl) ;    // If you need a specific ClassLoader, set it early
			    // so that your generated code can be checked against 
			    // those classes that it references.
 * _package( "YYY" ) ;	    // set the package to use for the generated code
 * _import( "AAA.BBB" ) ;   // repeat as needed.  As in Java, this makes "BBB"
 *			    // a synonym for "AAA.BBB".  Collisions are not 
 *			    // permitted.
 * _class( ... ) ;	    // _interface is used for interfaces.
 * _data( ... )	;	    // define the class data members (repeat as needed)
 * _initializer() ;	    // define a static initializer
 *
 *
 * _method( ... ) ;	    // define a method (use _constructor for a constructor) 
 * _arg( ... ) ;	    // repeat as needed for the method arguments.
 * _body() ;		    // begin the method body definition.
 *     // define the contents of the body using any of the available 
 *     // statements.
 * _end() ; // of method
 * _end() ; // of class
 * _end() ; // of package
 * Class<?>  newClass = _generate( ... ) ;
 * 
* Alternatively, the last line could be *
{@code
 * GenericClass gc = _generate( T.class, props ) ;}
 * 
* which makes it easy to create an instance of the generated class * by calling: *
{@code
 * T instance = gc.create(  ) ;}
 * 
*

* Currently only one class can be defined at a time. * There is a grammar defined below that gives more detail. * One small note: it is necessary to call _expr( expr ) in order to * convert expr into a statement that is incorporated into a body. * If this call is omitted, the generated code will NOT contain expr, * leading to a difficult to find bug. I will probably fix this * eventually, and treat all dangling expressions as errors. *

* This class provides many short * utility methods that operate against a current context that * is maintained in a ThreadLocal. This removes the * bookkeeping that would otherwise be needed to keep track * of various internal factories needed for generating statements and expressions. * Note that this class is designed to be statically imported. *

* The point of view of the API of this class is that calls to create * expressions are nested, while calls to create statements are not. * That is, constructing an expression * will often lead to nested method calls, but constructing a * statement is done with a sequence of method calls. Note that * everything could be done as a single nested method call per * class, but this is unnatural and hard to debug in * Java (Java is not Lisp, but you could make it look sort of * like Lisp if you tried hard enough). *

* All of the expression methods can be called any place where * an expression is valid, which basically means anywhere a * statement can be defined. The other methods can only be called * if the sequence of method calls is contained in the language * defined by the following grammar: * *

 * START	    : _package IMPORTS CLASS _end ;
 *
 * IMPORTS	    : _import IMPORTS
 *		    | empty ;
 *
 * CLASS	    : _class CDECLS _end ;
 *
 * CDECLS	    : CDECL CDECLS
 *		    | empty ;
 *
 * CDECL	    : _data
 *		    | _initializer STMTS _end
 *		    | METHOD
 *		    | CONSTRUCTOR ;
 *
 * METHOD	    : _method ARGS _body STMTS _end ;
 *
 * CONSTRUCTOR      : _constructor ARGS _body STMTS _end ;
 *
 * ARGS		    : _arg ARGS 
 *		    | empty ;
 *
 * STMTS	    : STMT STMTS
 *		    | empty ;
 *
 * STMT		    : IF
 *		    | TRY
 *		    | SWITCH
 *		    | WHILE
 *		    | SIMPLE ;
 *
 * IF		    : _if STMTS _end
 *		    | _if STMTS _else STMTS _end ;
 *
 * TRY		    : _try STMTS CATCHES _end
 *		    | _try STMTS _finally STMTS _end
 *		    | _try STMTS CATCHES _finally STMTS _end ;
 *
 * CATCHES	    : _catch STMTS CATCHES
 *		    | empty ; 
 *
 * SWITCH	    : _switch CASES 
 *		    | _switch CASES _default STMTS _end ;
 *
 * CASES	    : _case STMTS CASES
 *		    | empty ;
 * XXX modify this to support multiple case labels per statement list
 *
 * WHILE	    : _while STMTS _end ;
 *
 * SIMPLE	    : _assign 
 *		    | _define
 *		    | _return
 *		    | _throw
 *		    | _expr ;
 * 
*

* Any method that requires an expression can use any expression so long as * all variables references in the expression are still in scope. This means * that: *

    *
  1. Variables representing data members can be referenced anywhere * between the _class _body and _end. *
  2. Variables representing method parameters can be referenced anywhere * between the _method _body and _end. *
  3. Variables representing _define statements can be referenced with in the * scope of their block (that is, until the corresponding "}" in java. Here we * don't use "{}" to delimit scope in the generated code). *
  4. Variables representing _catch statements can be referenced until the next * corresponding _catch, _finally, or _end. * Any attempt to use an Expression that references a Variable that is out of scope * will result in an IllegalStateException. *
*

* The _classGenerator() method is used here to get the ClassGeneratorImpl, * which can then be used in the CodeGenerator API to create source * code or byte code directly. Also note that Util.display can be used * to dump the contents of the ClassGeneratorImpl for debugging purposes. *

* For convenience, methods are provided to display the AST (@see _displayAST), * generate source code (@see _sourceCode), and generate byteCode * (@see _byteCode). */ @SuppressWarnings({"unused", "WeakerAccess"}) public final class Wrapper { private Wrapper() { } // Checking the sequence of operations in the class // is equivalent to parsing a sentence in the grammar defined // above. Some productions in the grammar have side effects // such as updating the current ExpressionFactory or // BlockStatement that is used in other places. Probably // the simplest implementation here would be a recursive // decent parser, but this assumes that the parser controls // the call flow, and simply asks for the next token from // a lexical analyzer in the usual way. But here the parser // is not in control: the external program is. // // The implementation I have chosen here is to define a FSM // with a stack that can effectively parse the above grammar. // J2SE 5.0 provides some useful facilities (notably enums) // that can be used in conjunction with the ORB FSM framework. // This defines enums that implement the FSM state interface that // can be used in the FSM private enum Operation implements Input { PACKAGE, // _package method IMPORT, // _import method CLASS, // _class method DATA, // _data method INITIALIZER, // _initializer method METHOD, // _method method ARG, // _arg method BODY, // _body method IF, // _if method ELSE, // _else method TRY, // _try method CATCH, // _catch method FINALLY, // _finally method SWITCH, // _switch method CASE, // _case method DEFAULT, // _default method WHILE, // _while method SIMPLE, // _return, _throw, _assign, _define, // and _expr methods END } // SIMPLE represents simple statements: return, throw, expr, assign // and define. // Note that the state objects must extend State, so we cannot // use enum here. private static final State S_INIT = new State("S_INIT", State.Kind.INITIAL); private static final State S_DONE = new State("S_DONE", State.Kind.FINAL); private static final State S_PACKAGE = new State("S_PACKAGE"); private static final State S_CLASS = new State("S_CLASS", State.Kind.INITIAL); private static final State S_METHOD = new State("S_METHOD", State.Kind.INITIAL); private static final State S_BODY = new State("S_BODY", State.Kind.INITIAL); private static final State S_IF = new State("S_IF", State.Kind.INITIAL); private static final State S_ELSE = new State("S_ELSE"); private static final State S_TRY = new State("S_TRY", State.Kind.INITIAL); private static final State S_FINAL = new State("S_FINAL"); private static final State S_SWITCH = new State("S_SWITCH", State.Kind.INITIAL); private static final State S_DEFAULT = new State("S_DEFAULT"); private static final StateEngine engine = StateEngine.create(); // This set of transitions is used in several states, // so we use this method to avoid repetition. private static void addCommonTransitions(State state) { engine.add(state, Operation.SIMPLE, null, state); engine.add(state, Operation.IF, null, state); engine.add(state, Operation.TRY, null, state); engine.add(state, Operation.SWITCH, null, state); engine.add(state, Operation.WHILE, null, state); engine.add(state, Operation.END, null, S_DONE); } static { // Table of legal state transitions in each state: // Any input not in the table is illegal. // No transitions are legal in S_DONE. // // XXX We should provide a default action that prints out an appropriate // error message (rather than the default FSM-specific one). // // State Input Action New State engine.add(S_INIT, Operation.PACKAGE, null, S_PACKAGE); engine.add(S_PACKAGE, Operation.IMPORT, null, S_PACKAGE); engine.add(S_PACKAGE, Operation.CLASS, null, S_CLASS); engine.add(S_CLASS, Operation.DATA, null, S_CLASS); engine.add(S_CLASS, Operation.INITIALIZER, null, S_CLASS); engine.add(S_CLASS, Operation.METHOD, null, S_CLASS); engine.add(S_CLASS, Operation.END, null, S_DONE); engine.add(S_METHOD, Operation.BODY, null, S_BODY); engine.add(S_METHOD, Operation.ARG, null, S_METHOD); engine.add(S_METHOD, Operation.END, null, S_DONE); addCommonTransitions(S_BODY); addCommonTransitions(S_IF); engine.add(S_IF, Operation.ELSE, null, S_ELSE); addCommonTransitions(S_ELSE); addCommonTransitions(S_TRY); engine.add(S_TRY, Operation.CATCH, null, S_TRY); engine.add(S_TRY, Operation.FINALLY, null, S_FINAL); addCommonTransitions(S_FINAL); addCommonTransitions(S_SWITCH); engine.add(S_SWITCH, Operation.CASE, null, S_SWITCH); engine.add(S_SWITCH, Operation.DEFAULT, null, S_DEFAULT); addCommonTransitions(S_DEFAULT); } // We need to represent the current state as a stack of contexts. // The contexts are stacked as follows: class, method or body // (for static initializers), statement* // (any number of statement contexts can be nested). // The context stack is used to manage ExpressionFactories and // to support lookup of variable identifiers. // // We will not handle the push/pop interaction in the FSM. // Instead, each Environment method that needs to push or // pop will first update the current state machine, then (assuming // there is no error) construct the type-specific context node. // The constructor of the abstract base class Context actually // pushes the Context onto the stack. // // Note that the sole purpose of the state machine is to make // sure that operations are called in the correct order. // // Context Design // // All contexts are constructed with a reference to the Environment // They are stacked in the following order: // 1. ClassContext // 2. MethodContext (or just a BodyContext for a static initializer) // 3. If/Try/Switch/While StatementContext as needed. // private static abstract class Context { private final Runner runner; private final Context parent; final Stack contexts; private ExpressionFactory expressionFactory; private BlockStatement blockStatement; final ExpressionFactory ef() { if (expressionFactory == null) throw new IllegalStateException( "No ExpressionFactory is currently available"); return expressionFactory; } final BlockStatement bs() { if (blockStatement == null) throw new IllegalStateException( "No BlockStatement is currently available"); return blockStatement; } final void setBlockStatement(BlockStatement bs) { blockStatement = bs; if (bs == null) expressionFactory = null; else expressionFactory = bs.exprFactory(); } Context(Stack contexts, State start) { FSM fsm = new FSMImpl(engine, start); this.runner = new Runner(fsm); this.contexts = contexts; if (contexts.empty()) this.parent = null; else this.parent = contexts.peek(); contexts.push(this); expressionFactory = null; blockStatement = null; } final void stateTransition(Operation op) { runner.doIt(op); } public final Context parent() { return parent; } // Overridden in subclasses that have other places to // find variables besides the BlockStatement protected Expression alternateLookup(String ident) { return null; } // The main method used to search for variables. // This works for all types of contexts. First, // we look in the BlockStatement, if present. // Some subclasses of Context override alternate // lookup, which is called next. If the local // call fails, we delegate to the parent (if any). // Once all options fail, an exception is thrown. Expression getVariable(String ident) { Expression result = null; if (blockStatement != null) result = blockStatement.getVar(ident); if (result == null) result = alternateLookup(ident); if ((result == null) && (parent != null)) result = parent.getVariable(ident); if (result == null) throw new IllegalArgumentException( "Identifier " + ident + " not found."); return result; } public void _end() { contexts.pop(); } } // Context used for _package and _import. private static final class PackageContext extends Context { PackageContext(Stack contexts) { // start of no-codegen copier super(contexts, S_INIT); ClassCopierOrdinaryImpl.setCodegenCopierAllowed(false); } } // Context used for _class until // we start defining methods or static initializers. private static class ClassContext extends Context { private final ClassGeneratorImpl cg; ClassContext(Stack contexts, ClassGeneratorImpl cg) { super(contexts, S_CLASS); this.cg = cg; } FieldGenerator _data(int modifiers, Type type, String name) { return cg.addField(modifiers, type, name); } void _method(int modifiers, Type type, String name, List exceptions) { MethodGenerator mg = cg.startMethod(modifiers, type, name, exceptions); new MethodContext(contexts, mg); } void _constructor(int modifiers, List exceptions) { MethodGenerator mg = cg.startConstructor(modifiers, exceptions); new MethodContext(contexts, mg); // XXX somewhere we need to force the first statement in // a constructor to be an expression that is either this or super. } // This method returns a field access expression. This is the // case of a variable lookup that resolves to a static or // non-static field access expression. This just finds the field; // access checks are handled elsewhere. @Override protected Expression alternateLookup(String ident) { FieldInfo fld = cg.findFieldInfo(ident); if (fld == null) throw new IllegalArgumentException(ident + " not found in " + cg.name()); return ((FieldGenerator) fld).getExpression(); } @Override public void _end() { super._end(); // end of no-codegen copier ClassCopierOrdinaryImpl.setCodegenCopierAllowed(true); } } // Context used for all methods and constructors. private static class MethodContext extends Context { private MethodGenerator mg; public MethodGenerator methodGenerator() { return mg; } MethodContext(Stack contexts, MethodGenerator mg) { super(contexts, S_METHOD); this.mg = mg; setBlockStatement(null); } Expression _arg(Type type, String ident) { return mg.addArgument(type, ident); } void _body() { setBlockStatement(mg.body()); } @Override protected Expression alternateLookup(String ident) { for (Variable var : mg.arguments()) if (ident.equals(var.ident())) return var; return null; } @Override public void _end() { super._end(); ClassGeneratorImpl cg = (ClassGeneratorImpl) mg.parent(); cg.methodComplete(mg); } } // Context used for static initializers. private static class BodyContext extends Context { BodyContext(Stack contexts, BlockStatement bs) { super(contexts, S_BODY); setBlockStatement(bs); } } // Context used for if statements. private static class IfStatementContext extends Context { private final IfStatement ifstmt; IfStatementContext(Stack contexts, Expression expr) { super(contexts, S_IF); this.ifstmt = parent().bs().addIf(expr); setBlockStatement(ifstmt.truePart()); } void _else() { setBlockStatement(ifstmt.falsePart()); } } // Context used for switch statements. private static class SwitchStatementContext extends Context { private final SwitchStatement swstmt; SwitchStatementContext(Stack contexts, Expression expr) { super(contexts, S_SWITCH); this.swstmt = parent().bs().addSwitch(expr); setBlockStatement(null); } void _case(int value) { setBlockStatement(swstmt.addCase(value)); } void _default() { setBlockStatement(swstmt.defaultCase()); } } // Context used for try statements. private static class TryStatementContext extends Context { private final TryStatement trystmt; private Variable currentCaseVariable; TryStatementContext(Stack contexts) { super(contexts, S_TRY); this.trystmt = parent().bs().addTry(); currentCaseVariable = null; setBlockStatement(trystmt.bodyPart()); } Expression _catch(Type type, String name) { Pair pair = trystmt.addCatch(type, name); setBlockStatement(pair.second()); currentCaseVariable = pair.first(); return currentCaseVariable; } void _finally() { setBlockStatement(trystmt.finalPart()); currentCaseVariable = null; } @Override protected Expression alternateLookup(String ident) { if (currentCaseVariable != null) if (ident.equals(currentCaseVariable.ident())) return currentCaseVariable; return null; } @Override public void _end() { super._end(); if (trystmt.catches().entrySet().isEmpty() && trystmt.finalPart().isEmpty()) throw new IllegalStateException( "A try statement must have at least one catch clause or a final part"); } } // Context used for while statements. private static class WhileStatementContext extends Context { private final WhileStatement whilestmt; WhileStatementContext(Stack contexts, Expression expr) { super(contexts, S_BODY); this.whilestmt = parent().bs().addWhile(expr); setBlockStatement(whilestmt.body()); } } // The Environment is stored in a ThreadLocal and used for // all of the Wrapper public methods. private static class Environment { private Stack contexts; private ImportList imports; private String _package; private ClassGeneratorImpl root; ImportList imports() { return imports; } private T top(Class cls) { return cls.cast(contexts.peek()); } Environment() { _clear(); } void _clear() { contexts = new Stack<>(); contexts.push(new PackageContext(contexts)); imports = new ImportListImpl(); _package = ""; root = null; } Type _t(String name) { // look up the type in the table of imports. // If not found, treat as a fully qualified name. Type type = imports.lookup(name); if (type == null) return Type._class(name); else return type; } Expression _v(String name) { return contexts.peek().getVariable(name); } ClassGeneratorImpl classGenerator() { return root; } Type _thisClass() { return root.thisType(); } ExpressionFactory ef() { return contexts.peek().ef(); } BlockStatement bs() { return contexts.peek().bs(); } private void checkState(Operation op) { contexts.peek().stateTransition(op); } final void _package(String name) { checkState(Operation.PACKAGE); if (!"".equals(name)) Identifier.isValidFullIdentifier(name); _package = name; } final Type _import(final String name) { checkState(Operation.IMPORT); final Type type = Type._class(name); final Type itype = imports.lookup(type.className()); if (itype == null) { imports.addImport(type); } else { if (!type.equals(itype)) throw new IllegalArgumentException(type.name() + " conflicts with " + itype.name()); } return type; } final void _import(ImportList importList) { checkState(Operation.IMPORT); imports = importList.copy(); } final ImportList _import() { checkState(Operation.IMPORT); return imports.copy(); } public final void _class(int modifiers, String name, Type superClass, List impls) { checkState(Operation.CLASS); String cname = name; if (!_package.equals("")) cname = _package + "." + name; root = CodeGenerator.defineClass(modifiers, cname, superClass, impls); new ClassContext(contexts, root); } final void _interface(int modifiers, String name, List impls) { checkState(Operation.CLASS); String cname = name; if (!_package.equals("")) cname = _package + "." + name; root = CodeGenerator.defineInterface(modifiers, cname, impls); new ClassContext(contexts, root); } final FieldGenerator _data(int modifiers, Type type, String name) { checkState(Operation.DATA); ClassContext cc = top(ClassContext.class); return cc._data(modifiers, type, name); } final void _method(int modifiers, Type type, String name, List exceptions) { checkState(Operation.METHOD); ClassContext cc = top(ClassContext.class); cc._method(modifiers, type, name, exceptions); } final void _constructor(int modifiers, List exceptions) { checkState(Operation.METHOD); ClassContext cc = top(ClassContext.class); cc._constructor(modifiers, exceptions); } final Expression _arg(Type type, String name) { checkState(Operation.ARG); MethodContext mc = top(MethodContext.class); return mc._arg(type, name); } final void _body() { checkState(Operation.BODY); MethodContext mc = top(MethodContext.class); mc._body(); } void _if(Expression expr) { checkState(Operation.IF); new IfStatementContext(contexts, expr); } void _else() { checkState(Operation.ELSE); IfStatementContext isc = top(IfStatementContext.class); isc._else(); } void _try() { checkState(Operation.TRY); new TryStatementContext(contexts); } Expression _catch(Type type, String name) { checkState(Operation.CATCH); TryStatementContext tsc = top(TryStatementContext.class); return tsc._catch(type, name); } void _finally() { checkState(Operation.FINALLY); TryStatementContext tsc = top(TryStatementContext.class); tsc._finally(); } public void _switch(Expression expr) { checkState(Operation.SWITCH); new SwitchStatementContext(contexts, expr); } public void _case(int value) { checkState(Operation.CASE); SwitchStatementContext ssc = top(SwitchStatementContext.class); ssc._case(value); } public void _default() { checkState(Operation.DEFAULT); SwitchStatementContext ssc = top(SwitchStatementContext.class); ssc._default(); } public void _while(Expression expr) { checkState(Operation.WHILE); new WhileStatementContext(contexts, expr); } public void _end() { checkState(Operation.END); contexts.peek()._end(); } } // This ThreadLocal maintains the environment for using the // Wrapper methods in a thread. private static ThreadLocal tl = ThreadLocal.withInitial(Environment::new); private static Environment env() { return tl.get(); } //-------------------- Public API starts here ------------------------------- /** * Obtain the ClassGeneratorImpl that is constructed by the Wrapper * methods. This is only needed for using Wrapper with custom * vistors. */ public static ClassGenerator _classGenerator() { return env().classGenerator(); } private static final String CODEGEN_PREFIX = "org.glassfish.dynamic.codegen"; private static final String DEBUG_PREFIX = CODEGEN_PREFIX + ".debug"; /** * Set this to enable dumping the generated byte codes to a * class file in the given directory. */ public static final String CLASS_GENERATION_DIRECTORY = CODEGEN_PREFIX + ".classGenerationDirectory"; /** * Option used to enable generation of source files while * generating bytecode. Set to name of directory that should * contain the generated source file. */ public static final String SOURCE_GENERATION_DIRECTORY = CODEGEN_PREFIX + ".sourceGenerationDirectory"; /** * Debugging option used to dump the contents of the AST after * the setup visitor runs. */ public static final String DUMP_AFTER_SETUP_VISITOR = DEBUG_PREFIX + ".dumpAfterSetupVisitor"; /** * Debugging option used to trace the byte code generation. */ public static final String TRACE_BYTE_CODE_GENERATION = DEBUG_PREFIX + ".traceByteCodeGeneration"; /** * Causes contents of constant pool to be dumped. */ public static final String DUMP_CONSTANT_POOL = DEBUG_PREFIX + ".dumpConstantPool"; /** * Debugging option used to enable the ASM verifier, which can be * helpful for debugging errors in the code generation. */ public static final String USE_ASM_VERIFIER = DEBUG_PREFIX + ".useAsmVerifier"; /** * Set the ClassLoader for this thread that will be used for validating * references to pre-existing classes from generated code. Applications * that use special ClassLoaders (such as the app server) should call this * method before starting to generate a new Class. A good place to do this * is just before the _package call. */ public static void _setClassLoader(ClassLoader cl) { CurrentClassLoader.set(cl); } /** * Generate byte codes for the current ClassGenerator. * cl is used to resolve any references * to other classes. options may be used * to control some aspects of the code generation, such as * debugging options. Supported options include * DUMP_AFTER_SETUP_VISITOR, TRACE_BYTE_CODE_GENERATION, * USE_ASM_VERIFIER. */ public static byte[] _byteCode(ClassLoader cl, Properties options) { return _byteCode(env().classGenerator(), cl, options); } /** * Generate byte codes for the ClassGenerator. * cl is used to resolve any references * to other classes. options may be used * to control some aspects of the code generation, such as * debugging options. Supported options include * DUMP_AFTER_SETUP_VISITOR, TRACE_BYTE_CODE_GENERATION, * USE_ASM_VERIFIER. */ public static byte[] _byteCode(ClassGenerator cgen, ClassLoader cl, Properties options) { ClassGeneratorImpl cg = env().classGenerator(); ImportList imports = env().imports(); return CodeGenerator.generateBytecode(cg, cl, imports, options, System.out); } /** * Generate a class for the current ClassGenerator. * Basically equivalent to _byteCode followed by _makeClass. * cl is used to resolve any references * to other classes and to load the class given by * the current ClassGenerator. options may be used * to control some aspects of the code generation, such as * debugging options. * * @deprecated as of Java 11, use {@link #_generate(Class, Properties, PrintStream)} */ public static Class _generate(ClassLoader cl, ProtectionDomain pd, Properties props, PrintStream ps) { ClassGenerator cg = env().classGenerator(); return _generate(cg, cl, pd, props, ps); } /** * Generate a class for the current ClassGenerator. * Basically equivalent to _byteCode followed by _makeClass. * cl is used to resolve any references * to other classes and to load the class given by * the current ClassGenerator. options may be used * to control some aspects of the code generation, such as * debugging options. * * @deprecated as of Java 11, use {@link #_generate(Class, Properties)} */ public static Class _generate(ClassLoader cl, ProtectionDomain pd, Properties props) { ClassGenerator cg = env().classGenerator(); return _generate(cg, cl, pd, props, System.out); } /** * Generate a class for the current ClassGenerator. * Basically equivalent to _byteCode followed by _makeClass. * cl is used to resolve any references * to other classes and to load the class given by * the current ClassGenerator. options may be used * to control some aspects of the code generation, such as * debugging options. */ private static Class _generate(ClassGenerator cg, ClassLoader cl, ProtectionDomain pd, Properties props, PrintStream ps) { ImportList imports = env().imports(); byte[] data = CodeGenerator.generateBytecode((ClassGeneratorImpl) cg, cl, imports, props, ps); return CodeGeneratorUtil.makeClass(cg.name(), data, pd, cl); } /** * Generate a class for the current ClassGenerator, in the same classloader and package * as a specified "anchor" class to which the caller has access. * * @param anchorClass an existing class used as a reference for the new one. * @param props options to control some aspects of the code generation, such as debugging. * @param ps a stream to which debug messages should be written, if any * * @since since 4.1.0 */ public static Class _generate(Class anchorClass, Properties props, PrintStream ps) { return _generate(env().classGenerator(), anchorClass, props, ps); } /** * Generate a class for the current ClassGenerator, in the same classloader and package * as a specified "anchor" class to which the caller has access. * * @param anchorClass an existing class used as a reference for the new one. * @param props options to control some aspects of the code generation, such as debugging. * * @since since 4.1.0 */ public static Class _generate(Class anchorClass, Properties props) { return _generate(env().classGenerator(), anchorClass, props, System.out); } /** * Generate a class for the current ClassGenerator. * Basically equivalent to _byteCode followed by _makeClass. * cl is used to resolve any references * to other classes and to load the class given by * the current ClassGenerator. options may be used * to control some aspects of the code generation, such as * debugging options. */ private static Class _generate(ClassGenerator cg, Class anchorClass, Properties props, PrintStream ps) { ImportList imports = env().imports(); byte[] data = CodeGenerator.generateBytecode((ClassGeneratorImpl) cg, anchorClass.getClassLoader(), imports, props, ps); return CodeGeneratorUtil.makeClass(cg.name(), data, anchorClass); } /** * Generate the Java source code for the current Class defined by * Wrapper calls. options may be used to control some aspects * of the formatting of the source code, but no options are currently * defined. The generate source code is written to the PrintStream. */ public static void _sourceCode(PrintStream ps, Properties options) throws IOException { ClassGenerator cg = env().classGenerator(); _sourceCode(cg, ps, options); } /** * Generate the Java source code for the ClassGenerator. * options may be used to control some aspects * of the formatting of the source code, but no options are currently * defined. The generate source code is written to the PrintStream. */ public static void _sourceCode(ClassGenerator cg, PrintStream ps, Properties options) throws IOException { ImportList imports = env().imports(); CodeGenerator.generateSourceCode(ps, (ClassGeneratorImpl) cg, imports, options); } /** * Generate source code into a specified output directory. * options must set SOURCE_GENERATION_DIRECTORY to the name * of the output directory. */ public static void _sourceCode(Properties options) throws IOException { ClassGenerator cg = env().classGenerator(); _sourceCode(cg, options); } /** * Generate source code into a specified output directory. * options must set SOURCE_GENERATION_DIRECTORY to the name * of the output directory. */ public static void _sourceCode(ClassGenerator cg, Properties options) throws IOException { ImportList imports = env().imports(); String sourceGenDir = options.getProperty( Wrapper.SOURCE_GENERATION_DIRECTORY); if (sourceGenDir == null) { throw new IllegalArgumentException( "options must specify SOURCE_GENERATION_DIRECTORY"); } else { CodeGenerator.generateSourceCode(sourceGenDir, (ClassGeneratorImpl) cg, imports, options); } } /** * Dump the contents of the AST for the current Class defined * by Wrapper calls. The AST is dumped to the PrintStream. */ public static void _displayAST(ClassGenerator cg, PrintStream ps) { Util.display((ClassGeneratorImpl) cg, ps); } /** * Discard the current Class generated by Wrapper calls, so that * another Class may be generated. */ public static void _clear() { env()._clear(); } /** * Create a signature that may be used for calling a method or * constructor. rtype is the return type, and types gives all * of the argument types. types is empty if there are no * arguments. */ public static Signature _s(Type rtype, Type... types) { return Signature.make(rtype, asList(types)); } /** * Create a signature that may be used for calling a method or * constructor. rtype is the return type, and types gives all * of the argument types. types is empty if there are no * arguments. */ public static Signature _s(Type rtype, List types) { return Signature.make(rtype, types); } // Types /** * Return the reference type for the given class name. * name may be either the Class name, if the full name has * been imported using an _import statement, or a * fully qualified Class name. */ public static Type _t(String name) { return env()._t(name); } /** * Return a representation of the void type. This is * used whenever a method has a void return type. */ public static Type _void() { return Type._void(); } /** * Return a representation of the boolean type. */ public static Type _boolean() { return Type._boolean(); } /** * Return a representation of the byte type. */ static Type _byte() { return Type._byte(); } /** * Return a representation of the short type. */ static Type _short() { return Type._short(); } /** * Return a representation of the char type. */ static Type _char() { return Type._char(); } /** * Return a representation of the int type. */ public static Type _int() { return Type._int(); } /** * Return a representation of the long type. */ static Type _long() { return Type._long(); } /** * Return a representation of the float type. */ static Type _float() { return Type._float(); } /** * Return a representation of the double type. */ static Type _double() { return Type._double(); } /** * Return a representation of the java.lang.Object type. */ public static Type _Object() { return Type._Object(); } /** * Return a representation of the java.lang.String type. */ public static Type _String() { return Type._String(); } /** * Return a representation of the java.lang.Class type. */ public static Type _Class() { return Type._Class(); } /** * Split the class name into a pair of the package name and the unqualified class * name. If name is the name of a class in the anonymous global package, * the package name is "". */ public static Pair splitClassName(String name) { int lastDot = name.lastIndexOf('.'); String pname = (lastDot == -1) ? "" : name.substring(0, lastDot); String cname = name.substring(lastDot + 1); return new Pair<>(pname, cname); } // Declarations /** * _package must be called first to set the package name for this * class. After _package, all required _import calls must be made, * followed by one _class call. name may be "", in which case * the generated class is in the default package. */ public static void _package(String name) { env()._package(name); } /** * Same as _package( "" ). Note that this method MUST be called * before _class or _interface. This is a bit surprising, * since Java allows omission of the package statement, but codegen * requires an explicit package call. */ public static void _package() { env()._package(""); } /** * Used to create short names for types. Name must be a fully * qualified class name. The last name becomes the short * name used in _t to look up the full type. The result Type can * also be used directly. */ public static Type _import(String name) { return env()._import(name); } /** * Return an ImportList that can be shared across multiple * class generations. */ public static ImportList _import() { return env()._import(); } /** * Set the ImportList for the current class generation. * Mainly useful for generating source code. */ public static void _import(ImportList ilist) { env()._import(ilist); } /** * Define a class. This must be followed by calls to * _data, _initializer, or _method in any order. */ public static void _class(int modifiers, String name, Type superClass, Type... impls) { env()._class(modifiers, name, superClass, asList(impls)); } /** * Define a class. This must be followed by calls to * _data, _initializer, or _method in any order. */ public static void _class(int modifiers, String name, Type superClass, List impls) { env()._class(modifiers, name, superClass, impls); } /** * Define an interface. This must be followed by calls to * _method only. All _method calls must define abstract methods. * Note that static data in interfaces is not currently supported. */ public static void _interface(int modifiers, String name, Type... impls) { env()._interface(modifiers, name, asList(impls)); } /** * Define an interface. This must be followed by calls to * _method only. All _method calls must define abstract methods. * Note that static data in interfaces is not currently supported. */ public static void _interface(int modifiers, String name, List impls) { env()._interface(modifiers, name, impls); } /** * Define a data member in a class. If static, it must be * initialized in the class initializer, otherwise it must be * initialized in a constructor. */ public static Expression _data(int modifiers, Type type, String name) { FieldGenerator fld = env()._data(modifiers, type, name); return fld.getExpression(); } /** * Begin defining a method in the current class. Must be followed * first be all _arg calls for the method, then by _body(), then * by any statements, and finally by _end(). _body() may be followed * immediately by _end(), in which case the method has an empty body. * Abstract methods must have an empty body. */ public static void _method(int modifiers, Type type, String name, Type... exceptions) { env()._method(modifiers, type, name, asList(exceptions)); } /** * Begin defining a method in the current class. Must be followed * first by all _arg calls for the method, then by _body(), then * by any statements, and finally by _end(). _body() may be followed * immediately by _end(), in which case the method has an empty body. * Abstract methods must have an empty body. */ public static void _method(int modifiers, Type type, String name, List exceptions) { env()._method(modifiers, type, name, exceptions); } /** * Begin defining a constructor in the current class. * Must be followed * first by all _arg calls for the constructor, then by _body(), then * by optional statements, and finally by _end(). * Note that the first statement in any constructor must be * a this() or super() call. Constructors may not have * empty bodies. */ public static void _constructor(int modifiers, Type... exceptions) { env()._constructor(modifiers, asList(exceptions)); } /** * Begin defining a constructor in the current class. * Must be followed * first by all _arg calls for the constructor, then by _body(), then * by any statements, and finally by _end(). * Note that the first statement in any constructor must be * a _this() or _super() call. Constructors may not have * empty bodies. * Note that no default constructor is automatically generated * using this API. */ public static void _constructor(int modifiers, List exceptions) { env()._constructor(modifiers, exceptions); } /** * Add an argument to the current method. */ public static Expression _arg(Type type, String name) { return env()._arg(type, name); } /** * Indicates the start of the definition of the body of a method. * Must be followed by 0 or more statements, and be terminated by _end(). */ public static void _body() { env()._body(); } /** * Terminates the definition of the current statement, method, * constructor, initializer, class, or package. * Restores the context for the previous definition. */ public static void _end() { env()._end(); } // Statements /** * Indicate that expr should be executed as a statement for its * side effects. */ public static void _expr(Expression expr) { env().bs().addExpression(expr); } /** * Indicates an assignment statement of the form var = expr. * Var must be an assignable expression, such as a local variable, * a non-final field reference, ior an array index expression. */ public static void _assign(Expression var, Expression expr) { env().bs().addAssign(var, expr); } /** * Indicates the introduction of a new local variable initialized to * the given expression. */ public static Expression _define(Type type, String name, Expression expr) { return env().bs().addDefinition(type, name, expr); } /** * Indicates the end of execution in a method. Can only occur inside * a definition of a method with a void return type. */ public static void _return() { env().bs().addReturn(); } /** * Indicates the end of execution in a method with a return of the * value of the expression. The type of the expression must be * assignment compatible with the return type of the enclosing method. */ public static void _return(Expression expr) { env().bs().addReturn(expr); } /** * Indicates a throw statement that throws the given expression. * The type of expr must support Throwable. */ public static void _throw(Expression expr) { env().bs().addThrow(expr); } /** * Indicate the start of an if statement with the given expression * as the condition. All subsequent statements until the next * matching _else are part of the true branch of this if statement. */ public static void _if(Expression expr) { env()._if(expr); } /** * Indicate the start of the false branch of an if statement. * All subsequent statements until the next matching _end are * part of the false branch of the corresponding if statement. */ public static void _else() { env()._else(); } /** * Indicate the start of a try statement. * All subsequent statements until the next matching _catch * or _finally are part of this try statement. */ public static void _try() { env()._try(); } /** * Indicate the start of a catch clause in a try statement. * All subsequent statements until the next matching _catch * or _finally are part of this catch clause. */ public static Expression _catch(Type type, String name) { return env()._catch(type, name); } /** * Indicate the start of a finally clause in a try statement. * All subsequent statements until the next matching _end * are part of this finally clause. */ public static void _finally() { env()._finally(); } // Expressions /** * Construct the expression that refers to * the variable named name. This is looked up * following the Java name scope rules. */ public static Expression _v(String name) { return env()._v(name); } /** * Return the null expression. */ public static Expression _null() { return env().ef()._null(); } /** * Return a constant expression representing the * value c. */ public static Expression _const(boolean c) { return env().ef()._const(c); } /** * Return a constant expression representing the * value c. */ public static Expression _const(char c) { return env().ef()._const(c); } /** * Return a constant expression representing the * value c. */ public static Expression _const(byte c) { return env().ef()._const(c); } /** * Return a constant expression representing the * value c. */ public static Expression _const(short c) { return env().ef()._const(c); } /** * Return a constant expression representing the * value c. */ public static Expression _const(int c) { return env().ef()._const(c); } /** * Return a constant expression representing the * value c. */ public static Expression _const(long c) { return env().ef()._const(c); } /** * Return a constant expression representing the * value c. */ public static Expression _const(float c) { return env().ef()._const(c); } /** * Return a constant expression representing the * value c. */ public static Expression _const(double c) { return env().ef()._const(c); } /** * Return a constant expression representing the * value c. */ public static Expression _const(String c) { return env().ef()._const(c); } /** * Return a constant expression representing the * value c. Here c is a type, and this corresponds * to the Java ".class" reference. */ public static Expression _const(Type c) { return env().ef()._const(c); } /** * Generate a call to an instance method. The full signature * must be specified. */ public static Expression _call(Expression target, String ident, Signature signature, Expression... args) { return env().ef().call(target, ident, signature, asList(args)); } /** * Generate a call to an instance method. The full signature * must be specified. */ public static Expression _call(Expression target, String ident, Signature signature, List args) { return env().ef().call(target, ident, signature, args); } /** * Generate a call to an instance method, using the Java method * overload resolution algorithm to determine the signature. */ public static Expression _call(Expression target, String ident, Expression... args) { return env().ef().call(target, ident, asList(args)); } /** * Generate a call to an instance method, using the Java method * overload resolution algorithm to determine the signature. */ public static Expression _call(Expression target, String ident, List args) { return env().ef().call(target, ident, args); } /** * Generate a call to a static method. The full signature * must be specified. */ public static Expression _call(Type target, String ident, Signature signature, Expression... args) { return env().ef().staticCall(target, ident, signature, asList(args)); } /** * Generate a call to a static method. The full signature * must be specified. */ public static Expression _call(Type target, String ident, Signature signature, List args) { return env().ef().staticCall(target, ident, signature, args); } /** * Generate a call to a static method, using the Java method * overload resolution algorithm to determine the signature. */ public static Expression _call(Type target, String ident, Expression... args) { return env().ef().staticCall(target, ident, asList(args)); } /** * Generate a call to a static method, using the Java method * overload resolution algorithm to determine the signature. */ public static Expression _call(Type target, String ident, List args) { return env().ef().staticCall(target, ident, args); } /** * Generate a call to an instance method in the current super * class. The full signature must be specified. */ public static Expression _super(String ident, Signature signature, Expression... exprs) { return env().ef().superCall(ident, signature, asList(exprs)); } /** * Generate a call to an instance method in the current super * class. The full signature must be specified. */ public static Expression _super(String ident, Signature signature, List exprs) { return env().ef().superCall(ident, signature, exprs); } /** * Generate a call to an instance method in the current super * class using the Java method overload resolution algorithm to * determine the signature. */ public static Expression _super(String ident, Expression... exprs) { return env().ef().superCall(ident, asList(exprs)); } /** * Generate a call to an instance method in the current super * class using the Java method overload resolution algorithm to * determine the signature. */ public static Expression _super(String ident, List exprs) { return env().ef().superCall(ident, exprs); } /** * Invoke a superclass constructor as the first statement * in a constructor for a class. The full signature must * be specified. * This may only be used as the first expression * in a constructor. Every constructor must begin with * either a super(...) call or a this(...) call. */ public static Expression _super(Signature signature, Expression... exprs) { return env().ef().superObj(signature, asList(exprs)); } /** * Invoke a superclass constructor as the first statement * in a constructor for a class. The full signature must * be specified. * This may only be used as the first expression * in a constructor. Every constructor must begin with * either a super(...) call or a this(...) call. */ public static Expression _super(Signature signature, List exprs) { return env().ef().superObj(signature, exprs); } /** * Invoke a superclass constructor as the first statement * in a constructor for a class using the Java method overload * resolution algorithm to determine the signature. * This may only be used as the first expression * in a constructor. Every constructor must begin with * either a super(...) call or a this(...) call. */ public static Expression _super(List exprs) { return env().ef().superObj(exprs); } /** * Invoke a superclass constructor as the first statement * in a constructor for a class using the Java method overload * resolution algorithm to determine the signature. * This may only be used as the first expression * in a constructor. Every constructor must begin with * either a super(...) call or a this(...) call. */ public static Expression _super(Expression... exprs) { return env().ef().superObj(asList(exprs)); } /** * Invoke another constructor as the first statement * in a constructor for a class. The full signature must * be specified. * This may only be used as the first expression * in a constructor. Every constructor must begin with * either a super(...) call or a this(...) call. */ public static Expression _this(Signature signature, Expression... exprs) { return env().ef().thisObj(signature, asList(exprs)); } /** * Invoke another constructor as the first statement * in a constructor for a class. The full signature must * be specified. * This may only be used as the first expression * in a constructor. Every constructor must begin with * either a super(...) call or a this(...) call. */ public static Expression _this(Signature signature, List exprs) { return env().ef().thisObj(signature, exprs); } /** * Invoke another constructor as the first statement * in a constructor for a class using the Java method overload * resolution algorithm to determine the signature. * This may only be used as the first expression * in a constructor. Every constructor must begin with * either a super(...) call or a this(...) call. */ public static Expression _this(Expression... exprs) { return env().ef().thisObj(asList(exprs)); } /** * Invoke another constructor as the first statement * in a constructor for a class using the Java method overload * resolution algorithm to determine the signature. * This may only be used as the first expression * in a constructor. Every constructor must begin with * either a super(...) call or a this(...) call. */ public static Expression _this(List exprs) { return env().ef().thisObj(exprs); } @SuppressWarnings("SameParameterValue") private static Expression _binary(Expression left, ExpressionFactory.BinaryOperator op, Expression right) { return env().ef().binaryOperator(left, op, right); } // Abbreviated forms for _binary /** * Create an expression representing the application of the * != operator to the left and right expressions in the * form (left op right). */ public static Expression _ne(Expression left, Expression right) { return _binary(left, ExpressionFactory.BinaryOperator.NE, right); } /** * Create an expression representing the type cast of expr * to type. */ public static Expression _cast(Type type, Expression expr) { return env().ef().cast(type, expr); } /** * Create an expression representing the construction of a * new instance of the given type using the constructor with the * given signature and the list of expressions as arguments. */ public static Expression _new(Type type, Signature signature, Expression... args) { return env().ef().newObj(type, signature, asList(args)); } /** * Create an expression representing the construction of a * new instance of the given type using the constructor with the * given signature and the list of expressions as arguments. */ public static Expression _new(Type type, Signature signature, List args) { return env().ef().newObj(type, signature, args); } /** * Create an expression representing the construction of a * new instance of the given type using the constructor with the * signature determined by the Java method overload resolution * algorithm and the list of expressions as arguments. */ public static Expression _new(Type type, Expression... args) { return env().ef().newObj(type, asList(args)); } /** * Create an expression representing the construction of a * new instance of the given type using the constructor with the * signature determined by the Java method overload resolution * algorithm and the list of expressions as arguments. */ public static Expression _new(Type type, List args) { return env().ef().newObj(type, args); } /** * Create an expression representing the construction of a * new array with the given component type using the given * expressions to initialize the array. We really only * support single dimensional arrays here. The size of the * resulting array is the number of expressions given here. */ public static Expression _new_array_init(Type type, Expression... args) { return env().ef().newArrInit(type, asList(args)); } /** * Create an expression representing the construction of a * new array with the given component type using the given * expressions to initialize the array. We really only * support single dimensional arrays here. The size of the * resulting array is the number of expressions given here. * Equivalent to new A[] = { exprList }. */ public static Expression _new_array_init(Type type, List args) { return env().ef().newArrInit(type, args); } /** * Return the type of the current class. */ public static Type _thisClass() { return env()._thisClass(); } /** * Return an expression representing "this". */ public static Expression _this() { return env().ef()._this(); } /** * Return an expression used to access a field in an object * given by expr. This expression may appear on the left side * of an assignment statement. */ public static Expression _field(Expression expr, String fieldName) { return env().ef().fieldAccess(expr, fieldName); } /** * Return an expression used to access a static data member in * a class given by the type. This expression may appear on the * left side of an assignment statement (but probably shouldn't). */ public static Expression _field(Type type, String fieldName) { return env().ef().fieldAccess(type, fieldName); } /** * Return an expression used to access an element in an array * given by expr. This expression may appear on the left side * of an assignment statement. */ public static Expression _index(Expression expr, Expression index) { return env().ef().arrayIndex(expr, index); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy