/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser; import gw.internal.gosu.ir.transform.expression.EvalExpressionTransformer; import gw.lang.parser.ExternalSymbolMapForMap; import gw.lang.parser.IDynamicFunctionSymbol; import gw.lang.parser.IParseIssue; import gw.lang.parser.IParseTree; import gw.lang.parser.IParsedElement; import gw.lang.parser.IParsedElementWithAtLeastOneDeclaration; import gw.lang.parser.IProgramClassFunctionSymbol; import gw.lang.parser.IStatement; import gw.lang.parser.ISymbol; import gw.lang.parser.ISymbolTable; import gw.lang.parser.StandardSymbolTable; import gw.lang.parser.exceptions.ParseResultsException; import gw.lang.parser.expressions.ILocalVarDeclaration; import gw.lang.parser.expressions.IParameterDeclaration; import gw.lang.parser.expressions.IVarStatement; import gw.lang.parser.statements.IFunctionStatement; import gw.lang.parser.statements.IStatementList; import gw.lang.reflect.IType; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IExternalSymbolMap; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.java.JavaTypes; import gw.util.GosuExceptionUtil; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; /** */ public class ContextSensitiveCodeRunner { //!! Needed to ensure this class is loaded so a debugger can call into it remotely static void ensureLoadedForDebuggerEval() { System.out.println( "~~~~~LOADED"); } //!! Do not remove! This is called from the debugger via jdwp. /** * Intended for use with a debugger to evaluate arbitrary expressions/programs * in the context of a source position being debugged, usually at a breakpoint. * * @param enclosingInstance The instance of the object immediately enclosing the source position. * @param extSyms An array of adjacent name/value pairs corresponding with the names and values of local symbols in scope. * @param strText The text of the expression/program. * @param strClassContext The name of the top-level class enclosing the the source position. * @param strContextElementClass The name of the class immediately enclosing the source position (can be same as strClassContext). * @param iSourcePosition The index of the source position within the containing file. * @return The result of the expression or, in the case of a program, the return value of the program. */ public static Object runMeSomeCode( Object enclosingInstance, ClassLoader cl, Object[] extSyms, String strText, final String strClassContext, String strContextElementClass, int iSourcePosition ) { // Must execute in caller's classloader try { Class<?> cls = Class.forName( ContextSensitiveCodeRunner.class.getName(), false, cl ); Method m = cls.getDeclaredMethod( "_runMeSomeCode", Object.class, Object[].class, String.class, String.class, String.class, int.class ); m.setAccessible( true ); return m.invoke( null, enclosingInstance, extSyms, strText, strClassContext, strContextElementClass, iSourcePosition ); } catch( Exception e ) { Throwable cause = GosuExceptionUtil.findExceptionCause( e ); if( cause instanceof ParseResultsException ) { List<IParseIssue> parseExceptions = ((ParseResultsException)cause).getParseExceptions(); if( parseExceptions != null && parseExceptions.size() >= 0 ) { throw GosuExceptionUtil.forceThrow( (Throwable)parseExceptions.get( 0 ) ); } } throw GosuExceptionUtil.forceThrow( cause ); } } private static Object _runMeSomeCode( Object enclosingInstance, Object[] extSyms, String strText, final String strClassContext, String strContextElementClass, int iSourcePosition ) { IType type = TypeSystem.getByFullName( strClassContext, TypeSystem.getGlobalModule() ); if( !(type instanceof IGosuClassInternal) ) { System.out.println( strClassContext + " is not a Gosu class" ); return null; } IGosuClassInternal gsClass = (IGosuClassInternal)type; gsClass.isValid(); IParsedElement ctxElem = findElemAt( gsClass, iSourcePosition ); ISymbolTable compileTimeLocalContextSymbols = findCompileTimeSymbols( gsClass, iSourcePosition ); IExternalSymbolMap runtimeLocalSymbolValues = makeRuntimeNamesAndValues( extSyms ); IGosuClassInternal gsImmediateClass = (IGosuClassInternal)TypeSystem.getByFullName( strContextElementClass ); return EvalExpressionTransformer.compileAndRunEvalSource( strText, enclosingInstance, null, null, gsImmediateClass, ctxElem, compileTimeLocalContextSymbols, runtimeLocalSymbolValues ); } private static IExternalSymbolMap makeRuntimeNamesAndValues( Object[] extSyms ) { HashMap<String, ISymbol> map = new HashMap(); for( int i = 0; i < extSyms.length; i++ ) { String name = (String)extSyms[i]; Object value = extSyms[++i]; map.put( (String)name, new Symbol( name, JavaTypes.OBJECT(), value ) ); } return new ExternalSymbolMapForMap( map ); } private static IParsedElement findElemAt( IGosuClassInternal gsClass, int iContextLocation ) { IParseTree elem = ((IGosuClass)TypeLord.getOuterMostEnclosingClass( gsClass )).getClassStatement().getClassFileStatement().getLocation().getDeepestLocation( iContextLocation, false ); return elem == null ? gsClass.getClassStatement().getClassFileStatement() : elem.getParsedElement(); } private static ISymbolTable findCompileTimeSymbols( IGosuClassInternal enclosingClass, int iLocation ) { ISymbolTable symTable = new StandardSymbolTable( false ); IParseTree deepestLocation = enclosingClass.getClassStatement().getClassFileStatement().getLocation().getDeepestLocation( iLocation, false ); collectLocalSymbols( enclosingClass, symTable, deepestLocation.getParsedElement(), iLocation ); return symTable; } public static void collectLocalSymbols( IType enclosingType, ISymbolTable symTable, IParsedElement parsedElement, int iOffset ) { if( parsedElement == null ) { return; } if( parsedElement instanceof IFunctionStatement ) { IFunctionStatement declStmt = (IFunctionStatement)parsedElement; if( !declStmt.getDynamicFunctionSymbol().isStatic() ) { addThisSymbolForEnhancement( enclosingType, symTable ); } for( IParameterDeclaration localVar : declStmt.getParameters() ) { if( localVar != null && localVar.getLocation().getOffset() < iOffset ) { ISymbol symbol = localVar.getSymbol(); symTable.putSymbol( symbol ); } } } else if( parsedElement instanceof IParsedElementWithAtLeastOneDeclaration ) { IParsedElementWithAtLeastOneDeclaration declStmt = (IParsedElementWithAtLeastOneDeclaration)parsedElement; for( String strVar : declStmt.getDeclarations() ) { ILocalVarDeclaration localVar = findLocalVarSymbol( strVar, declStmt ); if( localVar != null && localVar.getLocation().getOffset() < iOffset ) { ISymbol symbol = localVar.getSymbol(); symTable.putSymbol( symbol ); } } } else if( parsedElement instanceof IStatementList ) { IStatementList stmtList = (IStatementList)parsedElement; for( IStatement stmt : stmtList.getStatements() ) { if( stmt instanceof IVarStatement && !((IVarStatement)stmt).isFieldDeclaration() && stmt.getLocation().getOffset() < iOffset ) { ISymbol symbol = ((IVarStatement)stmt).getSymbol(); if( isProgramFieldVar( stmt ) ) { continue; } symTable.putSymbol( symbol ); } } } IParsedElement parent = parsedElement.getParent(); if( parent != parsedElement ) { collectLocalSymbols( enclosingType, symTable, parent, iOffset ); } } private static void addThisSymbolForEnhancement( IType enclosingType, ISymbolTable symTable ) { if( enclosingType instanceof IGosuEnhancementInternal ) { IType thisType = ((IGosuEnhancementInternal)enclosingType).getEnhancedType(); if( thisType != null ) { thisType = TypeLord.getConcreteType( thisType ); symTable.putSymbol( new ThisSymbol( thisType, symTable ) ); } } } private static boolean isProgramFieldVar( IStatement stmt ) { if( stmt.getParent() != null ) { IParsedElement parent = stmt.getParent().getParent(); if( parent instanceof IFunctionStatement ) { IDynamicFunctionSymbol dfs = ((IFunctionStatement)parent).getDynamicFunctionSymbol(); if( dfs instanceof IProgramClassFunctionSymbol ) { return true; } } } return false; } private static ILocalVarDeclaration findLocalVarSymbol( String strVar, IParsedElement pe ) { if( pe instanceof ILocalVarDeclaration ) { ISymbol symbol = ((ILocalVarDeclaration)pe).getSymbol(); if( symbol != null && symbol.getName().equals( strVar ) ) { return (ILocalVarDeclaration)pe; } return null; } if( pe == null ) { return null; } for( IParseTree child : pe.getLocation().getChildren() ) { ILocalVarDeclaration localVar = findLocalVarSymbol( strVar, child.getParsedElement() ); if( localVar != null ) { return localVar; } } return null; } }