/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser.expressions; import gw.internal.gosu.parser.Expression; import gw.internal.gosu.parser.IGosuProgramInternal; import gw.internal.gosu.parser.CannotExecuteGosuException; import gw.lang.parser.GosuParserTypes; import gw.lang.parser.ITypeUsesMap; import gw.lang.parser.ICapturedSymbol; import gw.lang.parser.expressions.IEvalExpression; import gw.lang.parser.expressions.ITypeVariableDefinition; import gw.lang.reflect.RefreshKind; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.IFunctionType; import gw.lang.reflect.ITypeRef; import java.util.Iterator; import java.util.Map; import java.util.List; import java.util.LinkedHashMap; /** * The 'eval' operator as an expression: * <pre> * <i>eval-expression</i> * <b>eval</b> <b>(</b> <expression> <b>)</b> * </pre> * * @see gw.lang.parser.IGosuParser */ public final class EvalExpression extends Expression implements IEvalExpression { private ITypeUsesMap _typeUsesMap; private Expression _expression; private List<ICapturedSymbol> _capturedForBytecode; private Map<String, ITypeVariableDefinition> _capturedTypeVars; private Map<String, IGosuProgramInternal> _cacheProgramByFingerprint; private int _refreshChecksum = 0; /** * Constructs an 'eval' expression. */ public EvalExpression( ITypeUsesMap typeUsesMap ) { _typeUsesMap = typeUsesMap; _type = GosuParserTypes.GENERIC_BEAN_TYPE(); // Note, not a concurrent map because the type sys lock is acquired during access/modification (see GosuProgramParse.parseEval) _cacheProgramByFingerprint = new ProgramCache(); } public void setCapturedSymbolsForBytecode( List<ICapturedSymbol> captured ) { _capturedForBytecode = captured; } public List<ICapturedSymbol> getCapturedForBytecode() { return _capturedForBytecode; } public void cacheProgram( String strTypeName, IGosuProgramInternal gsClass ) { clearCacheOnChecksumChange(); _cacheProgramByFingerprint.put( strTypeName, gsClass ); } public IGosuProgramInternal getCachedProgram( String strTypeName ) { clearCacheOnChecksumChange(); return _cacheProgramByFingerprint.get( strTypeName ); } private void clearCacheOnChecksumChange() { if (_refreshChecksum != TypeSystem.getRefreshChecksum()) { _cacheProgramByFingerprint.clear(); _refreshChecksum = TypeSystem.getRefreshChecksum(); } } public ITypeUsesMap getTypeUsesMap() { return _typeUsesMap; } /** * The string expression containing Gosu code to evaluate/execute. */ public void setExpression( Expression expression ) { _expression = expression; } public Expression getExpression() { return _expression; } /** * Evaluates/executes the Gosu in the expression. * * @return The value of an expression or the return value of a program. */ public Object evaluate() { if( !isCompileTimeConstant() ) { return super.evaluate(); } throw new CannotExecuteGosuException(); } @Override public String toString() { return "eval( " + _expression.toString() + " )\n"; } public void setCapturedTypeVars( Map<String, ITypeVariableDefinition> typeVariables ) { for( Iterator<ITypeVariableDefinition> iter = typeVariables.values().iterator(); iter.hasNext(); ) { ITypeVariableDefinition tvd = iter.next(); if( !(tvd.getEnclosingType() instanceof IFunctionType) ) { iter.remove(); } } _capturedTypeVars = typeVariables; } public Map<String, ITypeVariableDefinition> getCapturedTypeVars() { return _capturedTypeVars; } static class ProgramCache extends LinkedHashMap<String, IGosuProgramInternal> { private static final int CACHE_SIZE = 100; public ProgramCache() { super( CACHE_SIZE ); } @Override protected boolean removeEldestEntry( Map.Entry<String, IGosuProgramInternal> eldest ) { TypeSystem.lock(); try { if( size() > CACHE_SIZE ) { IGosuProgramInternal program = eldest.getValue(); if( !program.getInnerClasses().isEmpty() ) { // Can't remove from type system since its inner classes may be returned as part of eval's results return true; } ITypeRef type = (ITypeRef)program; // Directly invalidate so as not to wreak havoc on type system at runtime. // Also avoids huge perf penalty. type._setStale(RefreshKind.MODIFICATION); //!!! NEVER! refresh types at runtime EVER! //IModule module = type.getTypeLoader().getModule(); //String strName = type.getName(); //System.out.println( "Removing Type: " + strName ); //TypeSystem.refresh( type, true ); //TypeSystem.notifyOfTypeDeletion( new String[] {strName}, module ); return true; } } finally { TypeSystem.unlock(); } return false; } } }