/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.ir.transform.expression;
import gw.config.CommonServices;
import gw.internal.gosu.compiler.SingleServingGosuClassLoader;
import gw.internal.gosu.ir.transform.ExpressionTransformer;
import gw.internal.gosu.ir.transform.TopLevelTransformationContext;
import gw.internal.gosu.parser.IGosuProgramInternal;
import gw.internal.gosu.parser.expressions.EvalExpression;
import gw.internal.gosu.util.LRUMap;
import gw.lang.ir.IRExpression;
import gw.lang.parser.GosuParserFactory;
import gw.lang.parser.ICapturedSymbol;
import gw.lang.parser.IGosuProgramParser;
import gw.lang.parser.IParseResult;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.ISymbolTable;
import gw.lang.reflect.IType;
import gw.lang.reflect.gs.ICompilableType;
import gw.lang.reflect.gs.IExternalSymbolMap;
import gw.lang.reflect.gs.IGosuProgram;
import gw.lang.reflect.gs.IProgramInstance;
import gw.util.GosuExceptionUtil;
import gw.util.GosuStringUtil;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
*/
public class EvalExpressionTransformer extends EvalBasedTransformer<EvalExpression>
{
@SuppressWarnings("unchecked")
public static final Map<String, EvalExpression> EVAL_EXPRESSIONS = Collections.synchronizedMap( new LRUMap( 2000 ) );
public static IRExpression compile( TopLevelTransformationContext cc, EvalExpression expr )
{
EvalExpressionTransformer compiler = new EvalExpressionTransformer( cc, expr );
return compiler.compile();
}
private EvalExpressionTransformer( TopLevelTransformationContext cc, EvalExpression expr )
{
super( cc, expr );
}
protected IRExpression compile_impl()
{
putEvalExpression( _expr() );
return callStaticMethod( EvalExpressionTransformer.class, "compileAndRunEvalSource", new Class[]{Object.class, Object.class, Object[].class, IType[].class, IType.class, int.class, int.class, String.class},
exprList(
boxValue( _expr().getType(), ExpressionTransformer.compile( _expr().getExpression(), _cc() ) ),
pushEnclosingContext(),
pushCapturedSymbols( getGosuClass(), _expr().getCapturedForBytecode() ),
pushEnclosingFunctionTypeParamsInArray( _expr() ),
pushType( getGosuClass() ),
pushConstant( _expr().getLineNum() ),
pushConstant( _expr().getColumn() ),
pushConstant( _expr().toString() )
));
}
private void putEvalExpression( EvalExpression evalExpr )
{
synchronized( EVAL_EXPRESSIONS )
{
int iLine = _expr().getLineNum();
int iColumnNum = _expr().getColumn();
EVAL_EXPRESSIONS.put( makeEvalKey( getGosuClass(), iLine, iColumnNum, _expr().toString() ), evalExpr );
}
}
public static String makeEvalKey( IType enclosingClass, int iLineNum, int iColumnNum, String evalExprText ) {
return enclosingClass.getName() + '.' + IGosuProgram.NAME_PREFIX + "eval_" + iLineNum + ":" + iColumnNum + ":" + GosuStringUtil.getSHA1String( evalExprText );
}
public static Object compileAndRunEvalSource( Object source, Object outer, Object[] capturedValues,
IType[] immediateFuncTypeParams, IType enclosingClass,
int iLineNum, int iColumn, String evalExprText )
{
String evalExprKey = makeEvalKey( enclosingClass, iLineNum, iColumn, evalExprText );
EvalExpression evalExpr = EVAL_EXPRESSIONS.get( evalExprKey );
if( evalExpr == null && enclosingClass instanceof ICompilableType ) {
((ICompilableType)enclosingClass).compile(); // force compilation of enclosing class indirectly compiles eval-expr which caches the expr
evalExpr = EVAL_EXPRESSIONS.get( evalExprKey );
}
return compileAndRunEvalSource( source, outer, capturedValues, immediateFuncTypeParams, enclosingClass, evalExpr );
}
public static Object compileAndRunEvalSource( Object source, Object outer, Object[] capturedValues,
IType[] immediateFuncTypeParams, IType enclosingClass, IParsedElement evalExpr )
{
return compileAndRunEvalSource( source, outer, capturedValues, immediateFuncTypeParams, enclosingClass, evalExpr, null, null );
}
public static Object compileAndRunEvalSource( Object source, Object outer, Object[] capturedValues,
IType[] immediateFuncTypeParams, IType enclosingClass, IParsedElement evalExpr,
ISymbolTable compileTimeLocalContextSymbols, IExternalSymbolMap runtimeLocalSymbolValues )
{
String strSource = CommonServices.getCoercionManager().makeStringFrom( source );
IGosuProgramParser parser = GosuParserFactory.createProgramParser();
List<ICapturedSymbol> capturedSymbols = evalExpr instanceof EvalExpression ? ((EvalExpression)evalExpr).getCapturedForBytecode() : Collections.<ICapturedSymbol>emptyList();
//debugInfo( compileTimeLocalContextSymbols );
IParseResult res = parser.parseEval( strSource, capturedSymbols, enclosingClass, evalExpr, compileTimeLocalContextSymbols );
IGosuProgram gp = res.getProgram();
if( !gp.isValid() )
{
throw GosuExceptionUtil.forceThrow(gp.getParseResultsException());
}
Class<?> javaClass = gp.getBackingClass();
assert javaClass.getClassLoader() instanceof SingleServingGosuClassLoader;
List<Object> args = new ArrayList<Object>();
if( !gp.isStatic() )
{
args.add( outer );
}
addCapturedValues( (IGosuProgramInternal)gp, capturedSymbols, capturedValues, args );
addEnclosingTypeParams( immediateFuncTypeParams, args );
Constructor ctor = javaClass.getConstructors()[0];
Class[] parameterTypes = ctor.getParameterTypes();
if( parameterTypes.length != args.size() )
{
if( parameterTypes.length > args.size() &&
parameterTypes[parameterTypes.length-1].getName().equals( IExternalSymbolMap.class.getName() ) )
{
args.add( runtimeLocalSymbolValues );
}
else
{
throw new IllegalStateException( "Eval constructor param count is not " + args.size() + "\nPassed in args " + printArgs( args ) + "\nActual args: " + printArgs( ctor.getParameterTypes() ) );
}
}
try
{
// Class[] parameterTypes = ctor.getParameterTypes();
// for( int i = 0; i < parameterTypes.length; i++ ) {
// System.out.println( "PARAM: " + parameterTypes[i].getName() + " ARG: " + args.get( i ) );
// }
IProgramInstance evalInstance = (IProgramInstance)ctor.newInstance( args.toArray() );
return evalInstance.evaluate( runtimeLocalSymbolValues );
}
catch( Exception e )
{
throw GosuExceptionUtil.forceThrow( e );
}
}
private static String printArgs( List<Object> args ) {
String str = "";
for( Object a: args ) {
str += a + ", ";
}
return str;
}
private static String printArgs( Class[] parameterTypes ) {
String str = "";
for( Class c: parameterTypes ) {
str += c.getName() + ", ";
}
return str;
}
private static void debugInfo( ISymbolTable compileTimeLocalContextSymbols ) {
if( compileTimeLocalContextSymbols != null ) {
Map symbols = compileTimeLocalContextSymbols.getSymbols();
for( Object key : symbols.keySet() ) {
Object o = symbols.get( key );
System.out.println( "SYMBOL NAME: " + key + " SYMBOL: " + o );
}
}
}
private static void addCapturedValues( IGosuProgramInternal gp, List<ICapturedSymbol> capturedSymbols, Object[] capturedValues, List<Object> args )
{
if( capturedValues != null )
{
// Note: must add the captured symbols in the order of the eval class' ctor, which is the order of the values in its map of captured symbols.
Map<String, ICapturedSymbol> capturedSymbolsByName = gp.getCapturedSymbols();
if( capturedSymbolsByName != null )
{
for( ICapturedSymbol sym : capturedSymbolsByName.values() )
{
args.add( capturedValues[capturedSymbols.indexOf( sym )] );
}
}
if (requiresExternalSymbolCapture(gp)) {
args.add( capturedValues[capturedValues.length - 1] );
}
}
}
public static void clearEvalExpressions() {
EVAL_EXPRESSIONS.clear();
}
}