/*
 * 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
GenericClass gc = _generate( T.class, props ) ;
which makes it easy to create an instance of the generated class by calling:
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).

/** 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. * <P> * 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: * <UL> * <LI>Generics. * <LI>Annotations (this may be added at some point). * <LI>For loops (of any sort, but this may be added through extension at some * point). * <LI>Enums. * <LI>Autoboxing. * <LI>Varargs methods. * <LI>Nested classes. * <LI>Compiler generated default constructors. * <LI>Compiler generated calls to super() in a constructor. * <LI>Anonymous classes. * <LI>do loops. * <LI>Static fields in interfaces. * <LI>Labels, or labelled break or continue. * </UL> * 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. * <P> * 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 * <PRE> * import static org.glassfish.dynamic.codegen.spi.Wrapper.* * </PRE> * to get easy access to all of the _xxx methods. * <P>A typical use of this class looks something like: * <PRE> * _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&lt;?&gt; newClass = _generate( ... ) ; * </PRE> * Alternatively, the last line could be * <PRE> * GenericClass<T> gc = _generate( T.class, props ) ; * </PRE> * which makes it easy to create an instance of the generated class * by calling: * <PRE> * T instance = gc.create( <constructor args> ) ; * </PRE> * <P> * 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. * <P> * 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. * <P> * 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). * <P> * 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: * * <PRE> * 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 ; * </PRE> * <P> * 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: * <OL> * <LI>Variables representing data members can be referenced anywhere * between the _class _body and _end. * <LI>Variables representing method parameters can be referenced anywhere * between the _method _body and _end. * <LI>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). * <LI>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. * </OL> * <P> * 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. * <p> * 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<Context> 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<Context> 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<Context> 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<Context> 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<Type> exceptions) { MethodGenerator mg = cg.startMethod(modifiers, type, name, exceptions); new MethodContext(contexts, mg); } void _constructor(int modifiers, List<Type> 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<Context> 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<Context> 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<Context> 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<Context> 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<Context> contexts) { super(contexts, S_TRY); this.trystmt = parent().bs().addTry(); currentCaseVariable = null; setBlockStatement(trystmt.bodyPart()); } Expression _catch(Type type, String name) { Pair<Variable, BlockStatement> 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<Context> 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<Context> contexts; private ImportList imports; private String _package; private ClassGeneratorImpl root; ImportList imports() { return imports; } private <T extends Context> T top(Class<T> 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<Type> 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<Type> 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<Type> exceptions) { checkState(Operation.METHOD); ClassContext cc = top(ClassContext.class); cc._method(modifiers, type, name, exceptions); } final void _constructor(int modifiers, List<Type> 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<Environment> 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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 _generate(Class<?>, Properties, PrintStream)
/** * 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 _generate(Class<?>, Properties)
/** * 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.
/** * 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.
Params:
  • anchorClass – an existing class used as a reference for the new one.
  • props – options to control some aspects of the code generation, such as debugging.
  • ps – a stream to which debug messages should be written, if any
Since:since 4.1.0
/** * 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.
Params:
  • anchorClass – an existing class used as a reference for the new one.
  • props – options to control some aspects of the code generation, such as debugging.
Since:since 4.1.0
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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<Type> 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.
/** * 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.
/** * 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.
/** * Return a representation of the boolean type. */
public static Type _boolean() { return Type._boolean(); }
Return a representation of the byte type.
/** * Return a representation of the byte type. */
static Type _byte() { return Type._byte(); }
Return a representation of the short type.
/** * Return a representation of the short type. */
static Type _short() { return Type._short(); }
Return a representation of the char type.
/** * Return a representation of the char type. */
static Type _char() { return Type._char(); }
Return a representation of the int type.
/** * Return a representation of the int type. */
public static Type _int() { return Type._int(); }
Return a representation of the long type.
/** * Return a representation of the long type. */
static Type _long() { return Type._long(); }
Return a representation of the float type.
/** * Return a representation of the float type. */
static Type _float() { return Type._float(); }
Return a representation of the double type.
/** * Return a representation of the double type. */
static Type _double() { return Type._double(); }
Return a representation of the java.lang.Object type.
/** * 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.
/** * 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.
/** * 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 "".
/** * 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<String, String> 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.
/** * _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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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<Type> 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.
/** * 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.
/** * 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<Type> 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.
/** * 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.
/** * 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.
/** * 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<Type> 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.
/** * 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.
/** * 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<Type> exceptions) { env()._constructor(modifiers, exceptions); }
Add an argument to the current method.
/** * 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().
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * Return the null expression. */
public static Expression _null() { return env().ef()._null(); }
Return a constant expression representing the value c.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * Generate a call to an instance method. The full signature * must be specified. */
public static Expression _call(Expression target, String ident, Signature signature, List<Expression> 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.
/** * 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.
/** * 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<Expression> args) { return env().ef().call(target, ident, args); }
Generate a call to a static method. The full signature must be specified.
/** * 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.
/** * Generate a call to a static method. The full signature * must be specified. */
public static Expression _call(Type target, String ident, Signature signature, List<Expression> 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.
/** * 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.
/** * 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<Expression> 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.
/** * 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.
/** * 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<Expression> 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.
/** * 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.
/** * 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<Expression> 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.
/** * 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.
/** * 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<Expression> 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.
/** * 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<Expression> 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.
/** * 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.
/** * 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.
/** * 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<Expression> 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.
/** * 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.
/** * 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<Expression> 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).
/** * 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.
/** * 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.
/** * 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.
/** * 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<Expression> 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.
/** * 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.
/** * 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<Expression> 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.
/** * 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 }.
/** * 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<Expression> args) { return env().ef().newArrInit(type, args); }
Return the type of the current class.
/** * Return the type of the current class. */
public static Type _thisClass() { return env()._thisClass(); }
Return an expression representing "this".
/** * 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.
/** * 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).
/** * 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.
/** * 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); } }