org.glassfish.pfl.dynamic.codegen.spi.Wrapper Maven / Gradle / Ivy
Show all versions of pfl-dynamic Show documentation
/*
* 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:
*
* - Variables representing data members can be referenced anywhere
* between the _class _body and _end.
*
- Variables representing method parameters can be referenced anywhere
* between the _method _body and _end.
*
- 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).
*
- 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);
}
}