package org.rascalmpl.library.experiments.Compiler.RVM.Interpreter;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.rascalmpl.debug.IRascalMonitor;
import org.rascalmpl.interpreter.Configuration;
import org.rascalmpl.interpreter.Evaluator;
import org.rascalmpl.interpreter.TypeReifier;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.ideservices.BasicIDEServices;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.ideservices.IDEServices;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.observers.CallTraceObserver;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.observers.CoverageFrameObserver;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.observers.DebugFrameObserver;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.observers.IFrameObserver;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.observers.NullFrameObserver;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.observers.ProfileFrameObserver;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.observers.RVMTrackingObserver;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.traverse.DescendantDescriptor;
import org.rascalmpl.library.util.PathConfig;
import org.rascalmpl.uri.URIResolverRegistry;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IMap;
import org.rascalmpl.value.ISourceLocation;
import org.rascalmpl.value.IString;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeStore;
import org.rascalmpl.values.ValueFactoryFactory;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.stats.CacheStats;
/**
* Provides all context information that is needed during the execution of a compiled Rascal program
* and contains:
* - PathConfig
* - class loaders
* - I/O streams
* - execution flags
* - state variables and caches needed by RascalPrimitives
*/
public class RascalExecutionContext implements IRascalMonitor {
private IDEServices ideServices;
private final PrintWriter stderr;
private final Configuration config;
private final List<ClassLoader> classLoaders;
private final PrintWriter stdout;
private final IValueFactory vf;
private final TypeStore typeStore;
private final boolean debug;
private final boolean debugRVM;
private final boolean testsuite;
private final boolean profile;
private final boolean trace;
private boolean coverage;
private boolean jvm;
private boolean verbose;
private IFrameObserver frameObserver;
private final IMap symbol_definitions;
private String currentModuleName;
private RVMCore rvm;
private final IMap moduleTags;
private Map<IValue, IValue> moduleVariables;
private final TypeReifier reifier;
// Caches
private static Cache<String, Function> companionDefaultFunctionCache;
private static Cache<String, Function> companionFieldDefaultFunctionCache;
// private Cache<String, Class<IGTD<IConstructor, ITree, ISourceLocation>>> parserCache;
private static Cache<String, IValue> parsedModuleCache;
private static Cache<Type, IConstructor> typeToSymbolCache;
private static Cache<IValue, Type> symbolToTypeCache;
private static Cache<IString, DescendantDescriptor> descendantDescriptorCache;
private static Cache<Type, Type> sharedTypeConstantCache;
private static final String PATH_TO_LINKED_PARSERGENERATOR = "lang/rascal/grammar/ParserGenerator.rvm.ser.gz";
private static final String PATH_TO_LINKED_KERNEL = "lang/rascal/boot/Kernel.rvm.ser.gz";
private static final String PATH_TO_LINKED_RASCALEXTRACTION = "experiments/Compiler/RascalExtraction/RascalExtraction.rvm.ser.gz";
private static final String PATH_TO_LINKED_QUESTIONCOMPILER = "experiments/tutor3/QuestionCompiler.rvm.ser.gz";
private static final String PATH_TO_LINKED_WEBSERVER = "util/Webserver.rvm.ser.gz";
static {
createCaches(true);
}
// State for RascalPrimitive
private final ParsingTools parsingTools;
Stack<String> indentStack = new Stack<String>();
StringBuilder templateBuilder = null;
private final Stack<StringBuilder> templateBuilderStack = new Stack<StringBuilder>();
private final ISourceLocation bootDir;
private final PathConfig pcfg;
public RascalExecutionContext(
PathConfig pcfg,
PrintWriter stdout,
PrintWriter stderr,
IMap moduleTags,
IMap symbol_definitions,
TypeStore typeStore,
IFrameObserver frameObserver,
IDEServices ideServices,
boolean coverage,
boolean debug,
boolean debugRVM,
boolean jvm,
boolean profile,
boolean testsuite,
boolean trace,
boolean verbose
){
this.vf = pcfg.getValueFactory();
this.pcfg = pcfg;
this.bootDir = pcfg.getBoot();
if(bootDir != null && !(bootDir.getScheme().equals("boot") ||
bootDir.getScheme().equals("compressed+boot") ||
URIResolverRegistry.getInstance().isDirectory(bootDir))){
throw new RuntimeException("bootDir should be a directory, given " + bootDir);
}
this.moduleTags = moduleTags;
this.symbol_definitions = symbol_definitions == null ? vf.mapWriter().done() : symbol_definitions;
this.typeStore = typeStore == null ? /*RascalValueFactory.getStore()*/ new TypeStore() : typeStore;
this.debug = debug;
this.debugRVM = debugRVM;
this.testsuite = testsuite;
this.profile = profile;
this.coverage = coverage;
this.jvm = jvm;
this.trace = trace;
this.verbose = verbose;
currentModuleName = "UNDEFINED";
reifier = new TypeReifier(vf);
this.ideServices = ideServices == null ? new BasicIDEServices() : ideServices;
this.stdout = stdout;
this.stderr = stderr;
config = new Configuration();
this.classLoaders = new ArrayList<ClassLoader>(Collections.singleton(Evaluator.class.getClassLoader()));
if(frameObserver == null){
if(profile){
setFrameObserver(new ProfileFrameObserver(this));
} else if(coverage){
setFrameObserver(new CoverageFrameObserver(this));
} else if(debug){
setFrameObserver(new DebugFrameObserver(this));
} else if(trace){
setFrameObserver(new CallTraceObserver(this));
} else if(debugRVM){
setFrameObserver(new RVMTrackingObserver(this));
} else {
setFrameObserver(NullFrameObserver.getInstance());
}
} else {
setFrameObserver(frameObserver);
}
parsingTools = new ParsingTools(vf);
}
public static ISourceLocation getLocation(ISourceLocation givenBootDir, String desiredPath) {
IValueFactory vfac = ValueFactoryFactory.getValueFactory();
try {
if(givenBootDir == null){
return vfac.sourceLocation("compressed+boot", "", desiredPath);
}
String scheme = givenBootDir.getScheme();
if(!scheme.startsWith("compressed+")){
scheme = "compressed+" + scheme;
}
String basePath = givenBootDir.getPath();
if(!basePath.endsWith("/")){
basePath += "/";
}
return vfac.sourceLocation(scheme, givenBootDir.getAuthority(), basePath + desiredPath);
} catch (URISyntaxException e) {
throw new RuntimeException("Cannot create location for " + desiredPath);
}
}
public static ISourceLocation getKernel(ISourceLocation givenBootDir) {
return getLocation(givenBootDir, PATH_TO_LINKED_KERNEL);
}
public static ISourceLocation getParserGenerator(ISourceLocation givenBootDir) {
return getLocation(givenBootDir, PATH_TO_LINKED_PARSERGENERATOR);
}
public static ISourceLocation getQuestionCompiler(ISourceLocation givenBootDir) {
return getLocation(givenBootDir, PATH_TO_LINKED_QUESTIONCOMPILER);
}
public static ISourceLocation getWebserver(ISourceLocation givenBootDir) {
return getLocation(givenBootDir, PATH_TO_LINKED_WEBSERVER);
}
public ISourceLocation getBoot() {
return getLocation(bootDir, "");
}
public PathConfig getPathConfig(){
return pcfg;
}
public ISourceLocation getKernel() {
return getLocation(bootDir, PATH_TO_LINKED_KERNEL);
}
public ISourceLocation getParserGenerator(){
return getLocation(bootDir, PATH_TO_LINKED_PARSERGENERATOR);
}
public ISourceLocation getRascalExtraction(){
return getLocation(bootDir, PATH_TO_LINKED_RASCALEXTRACTION);
}
public ISourceLocation getQuestionCompiler() {
return getLocation(bootDir, PATH_TO_LINKED_QUESTIONCOMPILER);
}
// Cache related methods
private static void createCaches(boolean enabled){
sharedTypeConstantCache = Caffeine.newBuilder()
.weakKeys()
.weakValues()
//.recordStats()
//.maximumSize(enabled ? 10000 : 0)
.build();
typeToSymbolCache = Caffeine.newBuilder()
.weakKeys()
.weakValues()
//.recordStats()
//.maximumSize(enabled ? 1000 : 0)
.build();
symbolToTypeCache = Caffeine.newBuilder()
.weakKeys()
.weakValues()
//.recordStats()
//.maximumSize(enabled ? 10000 : 0)
.build();
descendantDescriptorCache = Caffeine.newBuilder()
.weakKeys()
.weakValues()
//.recordStats()
//.maximumSize(enabled ? 500000 : 0)
.build();
companionDefaultFunctionCache = Caffeine.newBuilder()
.weakKeys()
.weakValues()
//.recordStats()
// .maximumSize(enabled ? 300 : 0)
.build();
companionFieldDefaultFunctionCache = Caffeine.newBuilder()
.weakKeys()
.weakValues()
//.recordStats()
// .maximumSize(enabled ? 300 : 0)
.build();
// parserCache = Caffeine.newBuilder()
//// .weakKeys()
// .weakValues()
//// .recordStats()
// .maximumSize(enabled ? 30 : 0)
// .build();
parsedModuleCache = Caffeine.newBuilder()
.weakKeys()
.weakValues()
//.recordStats()
// .maximumSize(enabled ? 100 : 0)
.build();
}
public void clearCaches(){
createCaches(true);
}
public void noCaches(){
createCaches(false);
}
public void printCacheStat(String name, Cache<?,?> cache){
CacheStats s = cache.stats();
System.out.printf(
"%35s: %6d (EV) %6d (HC) %5.1f (HR) %6d (LC) %6d (LFC) %5.1f (LFR) %6d (LSC) %6d (MC) %5.1f (MR) %6d (RC) %10.1f (ALP) \n",
//"%35s: %12.0f (ALP) %f (HR) %d (RC)\n",
name,
s.evictionCount(),
s.hitCount(),
s.hitRate(),
s.loadCount(),
s.loadFailureCount(),
s.loadFailureRate(),
s.loadSuccessCount(),
s.missCount(),
s.missRate(),
s.requestCount(),
s.averageLoadPenalty());
}
public void printCacheStats(){
printCacheStat("sharedTypeConstantCache", sharedTypeConstantCache);
printCacheStat("typeToSymbolCache", typeToSymbolCache);
printCacheStat("symbolToTypeCache", symbolToTypeCache);
printCacheStat("descendantDescriptorCache", descendantDescriptorCache);
printCacheStat("companionDefaultFunctionCache", companionDefaultFunctionCache);
printCacheStat("companionFieldDefaultFunctionCache", companionFieldDefaultFunctionCache);
// printCacheStat("parserCache", parserCache);
printCacheStat("parsedModuleCache", parsedModuleCache);
System.out.println("");
}
public ParsingTools getParsingTools(){
return parsingTools;
}
public Cache<String, IValue> getParsedModuleCache() {
return parsedModuleCache;
}
public static IConstructor typeToSymbol(final Type t){
return typeToSymbolCache.get(t, k -> RascalPrimitive.$type2symbol(t));
}
public Type symbolToType(IConstructor v, final IMap definitions) {
return symbolToTypeCache.get(v, k -> reifier.symbolToType(v, definitions));
}
Cache<IString, DescendantDescriptor> getDescendantDescriptorCache() {
return descendantDescriptorCache;
}
public Function getCompanionDefaultsFunction(String name, Type ftype){
String key = name + ftype;
//return rvm.getCompanionDefaultsFunction(name, ftype);
Function result = companionDefaultFunctionCache.get(key, k -> rvm.getCompanionDefaultsFunction(name, ftype));
//System.err.println("RascalExecutionContext.getCompanionDefaultsFunction: " + key + " => " + result.name);
return result;
}
public Function getCompanionFieldDefaultFunction(Type adtType, String fieldName){
//return rvm.getCompanionFieldDefaultFunction(adtType, fieldName);
String key = adtType.toString() + fieldName;
Function result = companionFieldDefaultFunctionCache.get(key, k -> rvm.getCompanionFieldDefaultFunction(adtType, fieldName));
return result;
}
public static Type shareTypeConstant(Type t){
return sharedTypeConstantCache.get(t, k -> k);
}
public IValueFactory getValueFactory(){ return vf; }
public IMap getSymbolDefinitions() { return symbol_definitions; }
public TypeStore getTypeStore() {
return typeStore;
}
boolean getDebug() { return debug; }
IFrameObserver getFrameObserver(){
return frameObserver;
}
void setFrameObserver(IFrameObserver observer){
frameObserver = observer;
}
boolean getDebugRVM() { return debugRVM; }
boolean getTestSuite() { return testsuite; }
boolean getProfile(){ return profile; }
boolean getCoverage(){ return coverage; }
boolean getJVM() { return jvm; }
boolean getTrace() { return trace; }
boolean getVerbose() { return verbose; }
public RVMCore getRVM(){ return rvm; }
protected void setRVM(RVMCore rvmCore){
this.rvm = rvmCore;
if(frameObserver != null){
frameObserver.setRVM(rvmCore);
}
}
public Map<IValue, IValue> getModuleVariables(){
return moduleVariables;
}
void setModuleVariables(Map<IValue, IValue> moduleVariables){
this.moduleVariables = moduleVariables;
}
public void addClassLoader(ClassLoader loader) {
// later loaders have precedence
classLoaders.add(0, loader);
}
List<ClassLoader> getClassLoaders() { return classLoaders; }
IRascalMonitor getMonitor() {return ideServices;}
IDEServices getIDEServices(){
return ideServices;
}
public PrintWriter getStdErr() { return stderr; }
public PrintWriter getStdOut() { return stdout; }
Configuration getConfiguration() { return config; }
public String getFullModuleName(){ return currentModuleName; }
public String getFullModuleNameAsPath() { return currentModuleName.replaceAll("::", "/") + ".rsc"; }
public void setFullModuleName(String moduleName) { currentModuleName = moduleName; }
public Stack<String> getIndentStack() { return indentStack; }
StringBuilder getTemplateBuilder() { return templateBuilder; }
void setTemplateBuilder(StringBuilder sb) { templateBuilder = sb; }
Stack<StringBuilder> getTemplateBuilderStack() { return templateBuilderStack; }
boolean bootstrapParser(String moduleName){
if(moduleTags != null){
IMap tags = (IMap) moduleTags.get(vf.string(moduleName));
if(tags != null)
return tags.get(vf.string("bootstrapParser")) != null;
}
return false;
}
public int endJob(boolean succeeded) {
return ideServices.endJob(succeeded);
}
public void event(int inc) {
ideServices.event(inc);
}
public void event(String name, int inc) {
ideServices.event(name, inc);
}
public void event(String name) {
ideServices.event(name);
}
public void startJob(String name, int workShare, int totalWork) {
ideServices.startJob(name, workShare, totalWork);
}
public void startJob(String name, int totalWork) {
ideServices.startJob(name, totalWork);
}
public void startJob(String name) {
ideServices.startJob(name);
}
public void todo(int work) {
ideServices.todo(work);
}
@Override
public boolean isCanceled() {
return ideServices.isCanceled();
}
@Override
public void warning(String message, ISourceLocation src) {
ideServices.warning(message, src);;
}
}