/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser; import gw.internal.gosu.parser.expressions.Identifier; import gw.lang.parser.ExternalSymbolMapForMap; import gw.lang.parser.IExpression; import gw.lang.parser.IParsedElement; import gw.lang.parser.IStatement; import gw.lang.parser.ISymbol; import gw.lang.parser.ISymbolTable; import gw.lang.parser.ITokenizerInstructor; import gw.lang.parser.ITypeUsesMap; import gw.lang.parser.expressions.IFieldAccessExpression; import gw.lang.reflect.IFunctionType; import gw.lang.reflect.IMethodInfo; import gw.lang.reflect.IType; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.*; import gw.lang.reflect.java.IJavaType; import gw.lang.reflect.java.JavaTypes; import gw.util.GosuExceptionUtil; import org.xml.sax.Attributes; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** */ public class GosuProgram extends GosuClass implements IGosuProgramInternal { private IType _expectedReturnType; private boolean _bGenRootExprAccess; private ITokenizerInstructor _tokenizerInstructor; private IProgramInstance _sharedInstance; private boolean _anonymous; private boolean _throwaway; private boolean _bStatementsOnly; private IType _contextType; private ContextInferenceManager _ctxInferenceMgr; private boolean _bParsingExecutableProgramStmts; private boolean _allowUses; private boolean _reloadable = true; public GosuProgram( String strNamespace, String strRelativeName, GosuClassTypeLoader classTypeLoader, ISourceFileHandle sourceFile, ITypeUsesMap typeUsesMap, ISymbolTable symTable ) { super( strNamespace, strRelativeName, classTypeLoader, sourceFile, typeUsesMap ); createNewParseInfo(); getParseInfo().setSymbolTable(symTable instanceof CompiledGosuClassSymbolTable ? ((CompiledGosuClassSymbolTable)symTable).getTargetSymbolTable() : symTable); addProgramInterfaces(); } protected void addProgramInterfaces() { addInterface( JavaTypes.IPROGRAM_INSTANCE() ); } /** * When changing the places from which this method is called run pc's * gw.smoketest.pc.job.common.effectivetime.VisibleEffectiveTimeTest * cause it will break! */ public GosuClassParseInfo createNewParseInfo() { if( getParseInfo() == null ) { _parseInfo = new GosuProgramParseInfo((IGosuClassInternal) getOrCreateTypeReference()); } return getParseInfo(); } public GosuProgramParseInfo getParseInfo() { return (GosuProgramParseInfo) super.getParseInfo(); } public ISymbolTable getSymbolTable() { return getParseInfo().getSymbolTable(); } public void addProgramEntryPoint( ISymbolTable symTable, GosuClassParser parser ) { IJavaType programInstance = JavaTypes.IPROGRAM_INSTANCE(); IType symbolMap = JavaTypes.IEXTERNAL_SYMBOL_MAP(); addProgramInstanceMethod( symTable, parser, programInstance, "evaluate", symbolMap); addProgramInstanceMethod( symTable, parser, programInstance, "evaluateRootExpr", symbolMap); } private void addProgramInstanceMethod( ISymbolTable symTable, GosuClassParser parser, IJavaType cls, String strMethod, IType params ) { IGosuProgramInternal pThis = (IGosuProgramInternal)getOrCreateTypeReference(); IType iface = TypeLord.getDefaultParameterizedType( cls ); IGosuClassInternal gsInterface = Util.getGosuClassFrom( iface ); IMethodInfo mi = gsInterface.getTypeInfo().getMethod( strMethod, params ); GosuMethodInfo gmi = (GosuMethodInfo)mi; if (gmi != null) { ReducedDynamicFunctionSymbol dfs = gmi.getDfs(); mi = (IMethodInfo) dfs.getMethodOrConstructorInfo(); if( !(iface instanceof IGosuClass) ) { String strMethodName = mi.getDisplayName(); mi = iface.getTypeInfo().getMethod( strMethodName, ((IFunctionType)dfs.getType()).getParameterTypes() ); if( mi == null ) { throw new IllegalStateException( "Did not find ProgramClass method info for: " + mi ); } } ProgramClassFunctionSymbol programClassFs = new ProgramClassFunctionSymbol( pThis, symTable, gmi ); parser.processFunctionSymbol( programClassFs, pThis ); } addCapturedProgramSymbols( symTable ); } public void addCapturedProgramSymbols( ISymbolTable classCompilationSymTable ) { ISymbolTable symTable = getSymbolTable(); if( symTable == null ) { return; } //External symbols are not allowed if there is an explicit super type if ( !Util.getGosuClassFrom( JavaTypes.OBJECT() ).equals( getSupertype() ) ) { return; } HashMap<String, ISymbol> externalSymbolsMap = new HashMap<String, ISymbol>( 8 ); getParseInfo().setExternalSymbols(new ExternalSymbolMapForMap(externalSymbolsMap)); Map symbols = symTable.getSymbols(); if( symbols == null ) { return; } //noinspection unchecked for( ISymbol sym : (Collection<ISymbol>)symbols.values() ) { if( !(sym instanceof CommonSymbolsScope.LockedDownSymbol) && sym != null ) { externalSymbolsMap.put( (String)sym.getName(), sym ); } } } @Override public boolean isAnonymous() { return _anonymous; } public boolean isThrowaway() { return _throwaway; } public void setThrowaway(boolean throwaway) { _throwaway = throwaway; } @Override public void setAllowUses(boolean b) { _allowUses = true; } @Override public boolean allowsUses() { return _allowUses || getName().contains(IGosuProgram.NAME_PREFIX); } public void setCtxInferenceMgr( Object ctxInferenceMgr ) { _ctxInferenceMgr = (ContextInferenceManager)ctxInferenceMgr; } @Override public ISymbol getExternalSymbol( String strName ) { ExternalSymbolMapForMap externalSymbols = getParseInfo().getExternalSymbols(); ISymbol sym = externalSymbols == null ? null : externalSymbols.getSymbol( strName ); if( sym == null ) { if( getSymbolTable() instanceof Attributes ) { //noinspection UnusedAssignment sym = getSymbolTable().getSymbol( strName ); } if( sym == null ) { sym = super.getExternalSymbol( strName ); } } return sym; } @Override public void setExpression( Expression expr ) { getParseInfo().setExpression(expr); } @Override public IExpression getExpression() { return getParseInfo().getExpression(); } @Override public void setStatement( Statement stmt ) { getParseInfo().setStatement(stmt); } @Override public IStatement getStatement() { return getParseInfo().getStatement(); } @Override public boolean isExpression() { return getParseInfo().getExpression() != null; } @Override public boolean isLhsExpression() { IExpression expression = getParseInfo().getExpression(); return expression instanceof IFieldAccessExpression || expression instanceof Identifier; } @Override public IParsedElement getEnclosingEvalExpression() { return getParseInfo().getEvalExpression(); } @Override public void setEnclosingEvalExpression( IParsedElement evalExprOrAnyExpr ) { getParseInfo().setEvalExpression( evalExprOrAnyExpr ); } @Override public Object evaluate(IExternalSymbolMap externalSymbols) { return runProgram(externalSymbols); } @Override public Object evaluateRoot(IExternalSymbolMap externalSymbols) { try { return getProgramInstance().evaluateRootExpr( externalSymbols ); } catch( Exception e ) { throw GosuExceptionUtil.forceThrow( e ); } } @Override public void assign( Object value ) { //## todo: implement l-value assignment } @Override public void setGenRootExprAccess( boolean bGenRootExprAccess ) { _bGenRootExprAccess = bGenRootExprAccess; } @Override public boolean isGenRootExprAccess() { return _bGenRootExprAccess; } @Override public ITokenizerInstructor getTokenizerInstructor() { return _tokenizerInstructor; } @Override public void setTokenizerInstructor( ITokenizerInstructor ti ) { _tokenizerInstructor = ti; } @Override public ISymbolTable getAdditionalDFSDecls() { return getParseInfo().getAdditionalDFSDecls(); } @Override public void setAdditionalDFSDecls(ISymbolTable symbolTable) { getParseInfo().setAdditionalDFSDecls(symbolTable); } @Override protected GosuParser getOrCreateParser(CompiledGosuClassSymbolTable symbolTable) { GosuParser parser = super.getOrCreateParser(symbolTable); parser.setTokenizerInstructor( getTokenizerInstructor() ); // This is a bit sketchy, so it might not be correct. This was done so that GosuExpressionAnalysisUtil could // pass through additional DFSs, i.e. those defined in the code block, that could be used during compilation. ISymbolTable additionalDFSDecls = getParseInfo().getAdditionalDFSDecls(); if (additionalDFSDecls != null) { parser.putDfsDeclsInTable( additionalDFSDecls ); } if( _ctxInferenceMgr != null ) { parser.setContextInferenceManager( _ctxInferenceMgr ); } return parser; } @Override public IType getExpectedReturnType() { return _expectedReturnType; } @Override public void setExpectedReturnType( IType expectedReturnType ) { if( expectedReturnType != null && expectedReturnType.isPrimitive() && expectedReturnType != JavaTypes.pVOID() ) { expectedReturnType = TypeSystem.getBoxType( expectedReturnType ); } _expectedReturnType = expectedReturnType; } @Override public IType getReturnType() { if( getExpectedReturnType() != null ) { return getExpectedReturnType(); } IStatement statement = getParseInfo().getStatement(); if( statement != null ) { return statement.getReturnType(); } return getParseInfo().getExpression().getType(); } @Override public IProgramInstance getProgramInstance() { if (_sharedInstance != null) { return _sharedInstance; } else { if (canShareProgramInstances()) { TypeSystem.lock(); try { if (_sharedInstance == null) { _sharedInstance = createNewInstance(); } return _sharedInstance; } finally { TypeSystem.unlock(); } } else { return createNewInstance(); } } } @Override public void setAnonymous( boolean b ) { _anonymous = b; } @Override public void setStatementsOnly( boolean bStatementsOnly ) { _bStatementsOnly = bStatementsOnly; } public boolean isStatementsOnly() { return _bStatementsOnly; } private IProgramInstance createNewInstance() { try { return (IProgramInstance) getBackingClass().newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } private boolean canShareProgramInstances() { // Note we check for existence of member fields from the Class and not the GosuClass // to avoid parsing the GosuClass at runtime. This method is only called at runtime. for( Field f : getBackingClass().getDeclaredFields() ) { if( !Modifier.isStatic( f.getModifiers() ) ) { return false; } } return true; } private Object runProgram(IExternalSymbolMap externalSymbols) { try { return getProgramInstance().evaluate( externalSymbols ); } catch( Exception e ) // not catching Throwable b/c we need to *not* ever catch ThreadDeath, which is thrown from the gosu editor to stop the program { throw GosuExceptionUtil.forceThrow( e ); } } public void setContextType(IType contextType) { _contextType = contextType; } public IType getContextType() { return _contextType; } @Override public boolean isParsingExecutableProgramStatements() { return _bParsingExecutableProgramStmts; } @Override public void setParsingExecutableProgramStatements( boolean b ) { _bParsingExecutableProgramStmts = b; } @Override public ClassType getClassType() { return ClassType.Program; } }