package org.rascalmpl.library.experiments.Compiler.RVM.Interpreter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import org.nustaq.serialization.FSTBasicObjectSerializer;
import org.nustaq.serialization.FSTClazzInfo;
import org.nustaq.serialization.FSTClazzInfo.FSTFieldInfo;
import org.nustaq.serialization.FSTConfiguration;
import org.nustaq.serialization.FSTObjectInput;
import org.nustaq.serialization.FSTObjectOutput;
import org.rascalmpl.interpreter.ITestResultListener;
import org.rascalmpl.library.experiments.Compiler.VersionInfo;
import org.rascalmpl.library.util.SemVer;
import org.rascalmpl.uri.URIResolverRegistry;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.IListWriter;
import org.rascalmpl.value.IMap;
import org.rascalmpl.value.ISet;
import org.rascalmpl.value.ISourceLocation;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeStore;
import org.rascalmpl.values.ValueFactoryFactory;
import org.rascalmpl.values.uptr.RascalValueFactory;
/**
* RVMExecutable contains all data needed for executing an RVM program.
*
* RVMExecutable is serialized by FSTRVMExecutableSerializer; make sure that
* all **non-static** fields declared here are synced with the serializer.
*/
public class RVMExecutable implements Serializable{
static private final FSTSerializableType serializableType;
static private final FSTSerializableIValue serializableIValue;
static private final FSTRVMExecutableSerializer rvmExecutableSerializer;
static private final FSTFunctionSerializer functionSerializer;
static private final FSTOverloadedFunctionSerializer overloadedFunctionSerializer;
static private final FSTCodeBlockSerializer codeblockSerializer;
static {
// set up FST serialization in gredients that will be reused across read/write calls
// PDB Types
serializableType = new FSTSerializableType();
// PDB values
serializableIValue = new FSTSerializableIValue();
// Specific serializers
rvmExecutableSerializer = new FSTRVMExecutableSerializer();
functionSerializer = new FSTFunctionSerializer();
overloadedFunctionSerializer = new FSTOverloadedFunctionSerializer();
codeblockSerializer = new FSTCodeBlockSerializer();
}
/**
* Create an FSTConfiguration depending on the used extension: ".json" triggers the JSON reader/writer.
* Note: the JSON version is somewhat larger and slower but is usefull for recovery during bootstrapping incidents.
* @param source or desination of executable
* @return an initialized FSTConfiguration
*/
private static FSTConfiguration makeFSTConfig(ISourceLocation path){
FSTConfiguration config = path.getURI().getPath().contains(".json") ?
FSTConfiguration.createJsonConfiguration() : FSTConfiguration.createDefaultConfiguration();
config.registerSerializer(FSTSerializableType.class, serializableType, false);
config.registerSerializer(FSTSerializableIValue.class, serializableIValue, false);
config.registerSerializer(RVMExecutable.class, rvmExecutableSerializer, false);
config.registerSerializer(Function.class, functionSerializer, false);
config.registerSerializer(OverloadedFunction.class, overloadedFunctionSerializer, false);
config.registerSerializer(CodeBlock.class, codeblockSerializer, false);
config.registerClass(OverloadedFunction.class);
return config;
}
private static final long serialVersionUID = -8966920880207428792L;
static final String RASCAL_MAGIC = "Rascal Vincit Omnia";
// transient fields
static IValueFactory vf;
static TypeStore store;
// Serializable fields
private ISet errors;
private String module_name;
private IMap moduleTags;
private IMap symbol_definitions;
private Function[] functionStore;
private Map<String, Integer> functionMap;
// Constructors
private ArrayList<Type> constructorStore;
private Map<String, Integer> constructorMap;
// Function overloading
private OverloadedFunction[] overloadedStore;
private Map<String, Integer> resolver;
private ArrayList<String> initializers;
private String uid_module_init;
private String uid_module_main;
private byte[] jvmByteCode;
private String fullyQualifiedDottedName;
public RVMExecutable(ISet errors){
this.errors = errors;
}
public RVMExecutable(
final String module_name,
final IMap moduleTags,
final IMap symbol_definitions,
final Map<String, Integer> functionMap,
final Function[] functionStore,
final Map<String, Integer> constructorMap,
final ArrayList<Type> constructorStore,
final Map<String, Integer> resolver,
final OverloadedFunction[] overloadedStore,
ArrayList<String> initializers,
String uid_module_init,
String uid_module_main,
TypeStore ts,
IValueFactory vfactory,
boolean jvm
) throws IOException{
vf = vfactory;
store = ts;
this.errors = vf.set();
this.module_name = module_name;
this.moduleTags = moduleTags;
this.symbol_definitions = symbol_definitions;
this.functionMap = functionMap;
this.functionStore = functionStore;
this.constructorMap = constructorMap;
this.constructorStore = constructorStore;
this.resolver = resolver;
this.overloadedStore = overloadedStore;
this.initializers = initializers;
this.uid_module_init = uid_module_init;
this.uid_module_main = uid_module_main;
if(jvm){
generateClassFile(false);
clearForJVM();
}
}
void clearForJVM(){
for(Function f : functionStore){
f.clearForJVM();
}
}
public Boolean isValid(){
return errors.size() == 0;
}
public ISet getErrors(){
return errors;
}
public String getModuleName() {
return module_name;
}
public IMap getModuleTags() {
return moduleTags;
}
IMap getSymbolDefinitions() {
return symbol_definitions;
}
public Function[] getFunctionStore() {
return functionStore;
}
public Map<String, Integer> getFunctionMap() {
return functionMap;
}
public ArrayList<Type> getConstructorStore() {
return constructorStore;
}
public Map<String, Integer> getConstructorMap() {
return constructorMap;
}
OverloadedFunction[] getOverloadedStore() {
return overloadedStore;
}
Map<String, Integer> getResolver() {
return resolver;
}
ArrayList<String> getInitializers() {
return initializers;
}
public ArrayList<Function> getTests(){
ArrayList<Function> tests = new ArrayList<>();
if(functionStore != null){
for(Function f : functionStore){
if(f.isTest){
tests.add(f);
}
}
}
return tests;
}
public IList executeTests(ITestResultListener testResultListener, RascalExecutionContext rex){
IListWriter w = vf.listWriter();
for(Function f : functionStore){
if(f.isTest){
w.append(f.executeTest(testResultListener, rex));
}
}
return w.done();
}
String getUidModuleInit() {
return uid_module_init;
}
String getUidModuleMain() {
return uid_module_main;
}
byte[] getJvmByteCode() {
return jvmByteCode;
}
void setJvmByteCode(byte[] jvmByteCode) {
this.jvmByteCode = jvmByteCode;
}
String getFullyQualifiedDottedName() {
return fullyQualifiedDottedName;
}
void setFullyQualifiedDottedName(String fullyQualifiedDottedName) {
this.fullyQualifiedDottedName = fullyQualifiedDottedName;
}
private String getGeneratedPackageName(){
String packageName = ""; //"org.rascalmpl.library";
int n = module_name.lastIndexOf("::");
if(n > 2){
if(!packageName.isEmpty()){
packageName += ".";
}
packageName += module_name.substring(0, n).replaceAll("::", ".");
}
return packageName;
}
private String getGeneratedClassName(){
String className;
int n = module_name.lastIndexOf("::");
if(n > 2){
className = module_name.substring(n + 2);
} else {
className = module_name;
}
className += "$Compiled";
return className;
}
String getGeneratedClassQualifiedName(){
String packageName = getGeneratedPackageName();
String className = getGeneratedClassName();
return packageName + (packageName.isEmpty() ? "" : ".") + className;
}
void generateClassFile(boolean debug) {
try {
BytecodeGenerator codeEmittor = new BytecodeGenerator(functionStore, overloadedStore, functionMap, constructorMap, resolver);
codeEmittor.buildClass(getGeneratedPackageName(), getGeneratedClassName(), debug) ;
jvmByteCode = codeEmittor.finalizeCode();
fullyQualifiedDottedName = codeEmittor.finalName().replace('/', '.') ;
if(debug){
codeEmittor.dumpClass();
}
} catch (Exception e) {
if (e.getMessage() != null && e.getMessage().startsWith("Method code too large")) {
// ASM does not provide an indication of _which_ method is too large, so let's find out:
Comparator<Function> c = ((x, y) -> x.codeblock.finalCode.length - y.codeblock.finalCode.length);
throw new RuntimeException("Function too large: " + Arrays.stream(functionStore).max(c).get(), e);
}
else {
throw e;
}
}
}
public void write(ISourceLocation rvmExecutable) throws IOException{
OutputStream fileOut;
TypeStore typeStore = RascalValueFactory.getStore(); //new TypeStore(RascalValueFactory.getStore());
FSTSerializableType.initSerialization(vf, typeStore);
FSTSerializableIValue.initSerialization(vf, typeStore);
FSTRVMExecutableSerializer.initSerialization(vf, typeStore);
FSTFunctionSerializer.initSerialization(vf, typeStore);
FSTCodeBlockSerializer.initSerialization(vf, typeStore);
ISourceLocation compOut = rvmExecutable;
fileOut = URIResolverRegistry.getInstance().getOutputStream(compOut, false);
FSTObjectOutput out = new FSTObjectOutput(fileOut, makeFSTConfig(rvmExecutable));
//long before = Timing.getCpuTime();
out.writeObject(this);
out.close();
//System.out.println("RVMExecutable.write: " + compOut.getPath() + " [" + (Timing.getCpuTime() - before)/1000000 + " msec]");
}
public static RVMExecutable read(ISourceLocation rvmExecutable, TypeStore typeStore) throws IOException {
vf = ValueFactoryFactory.getValueFactory();
// TypeStore typeStore = new TypeStore(RascalValueFactory.getStore());
FSTSerializableType.initSerialization(vf, typeStore);
FSTSerializableIValue.initSerialization(vf, typeStore);
FSTRVMExecutableSerializer.initSerialization(vf, typeStore);
FSTFunctionSerializer.initSerialization(vf, typeStore);
FSTCodeBlockSerializer.initSerialization(vf, typeStore);
try (InputStream fileIn = URIResolverRegistry.getInstance().getInputStream(rvmExecutable);
FSTObjectInput in = new FSTObjectInput(fileIn, makeFSTConfig(rvmExecutable))) {
return (RVMExecutable) in.readObject(RVMExecutable.class);
} catch (ClassNotFoundException c) {
throw new IOException("Class not found: " + c.getMessage(), c);
} catch (Throwable e) {
throw new IOException(e.getMessage(), e);
}
}
}
class FSTRVMExecutableSerializer extends FSTBasicObjectSerializer {
private static IValueFactory vf;
private static TypeStore store;
public static void initSerialization(IValueFactory vfactory, TypeStore ts){
vf = vfactory;
store = ts;
store.extendStore(RascalValueFactory.getStore());
}
@Override
public void writeObject(FSTObjectOutput out, Object toWrite,
FSTClazzInfo arg2, FSTFieldInfo arg3, int arg4)
throws IOException {
int n;
RVMExecutable ex = (RVMExecutable) toWrite;
// Write standard header
out.writeObject(RVMExecutable.RASCAL_MAGIC);
out.writeObject(VersionInfo.RASCAL_VERSION);
out.writeObject(VersionInfo.RASCAL_RUNTIME_VERSION);
out.writeObject(VersionInfo.RASCAL_COMPILER_VERSION);
// String[] errors
out.writeObject(new FSTSerializableIValue(ex.getErrors()));
if(!ex.isValid()){
return;
}
// public String module_name;
out.writeObject(ex.getModuleName());
// public IMap moduleTags;
out.writeObject(new FSTSerializableIValue(ex.getModuleTags()));
// public IMap symbol_definitions;
out.writeObject(new FSTSerializableIValue(ex.getSymbolDefinitions()));
// public ArrayList<Function> functionStore;
// public Map<String, Integer> functionMap;
out.writeObject(ex.getFunctionStore());
// public ArrayList<Type> constructorStore;
n = ex.getConstructorStore().size();
out.writeObject(n);
for(int i = 0; i < n; i++){
out.writeObject(new FSTSerializableType(ex.getConstructorStore().get(i)));
}
// public Map<String, Integer> constructorMap;
out.writeObject(ex.getConstructorMap());
// public ArrayList<OverloadedFunction> overloadedStore;
out.writeObject(ex.getOverloadedStore());
// public Map<String, Integer> resolver;
out.writeObject(ex.getResolver());
// ArrayList<String> initializers;
out.writeObject(ex.getInitializers());
// public String uid_module_init;
out.writeObject(ex.getUidModuleInit());
// public String uid_module_main;
out.writeObject(ex.getUidModuleMain());
// public byte[] jvmByteCode;
out.writeObject(ex.getJvmByteCode());
// public String fullyQualifiedDottedName;
out.writeObject(ex.getFullyQualifiedDottedName());
}
public void readObject(FSTObjectInput in, Object toRead, FSTClazzInfo clzInfo, FSTClazzInfo.FSTFieldInfo referencedBy)
{
}
@SuppressWarnings("unchecked")
public Object instantiate(@SuppressWarnings("rawtypes") Class objectClass, FSTObjectInput in, FSTClazzInfo serializationInfo, FSTClazzInfo.FSTFieldInfo referencee, int streamPosition) throws ClassNotFoundException, IOException
{
int n;
// Read and check standard header
// Check magic
String rascal_magic = (String) in.readObject();
if(!rascal_magic.equals(RVMExecutable.RASCAL_MAGIC)){
throw new RuntimeException("Cannot read incompatible Rascal executable");
}
// Check RASCAL_VERSION
String rascal_version = (String) in.readObject();
SemVer sv;
try {
sv = new SemVer(rascal_version);
} catch(Exception e){
throw new RuntimeException("Invalid value for RASCAL_VERSION in Rascal executable");
}
if(!sv.satisfiesVersion("~" + VersionInfo.RASCAL_VERSION)){
throw new RuntimeException("RASCAL_VERSION " + rascal_version + " in Rascal executable incompatible with current version " + VersionInfo.RASCAL_VERSION);
}
// Check RASCAL_RUNTIME_VERSION
String rascal_runtime_version = (String) in.readObject();
try {
sv = new SemVer(rascal_runtime_version);
} catch(Exception e){
throw new RuntimeException("Invalid value for RASCAL_RUNTIME_VERSION in Rascal executable");
}
if(!sv.satisfiesVersion("~" + VersionInfo.RASCAL_RUNTIME_VERSION)){
throw new RuntimeException("RASCAL_RUNTIME_VERSION " + rascal_runtime_version + " in Rascal executable incompatible with current version " + VersionInfo.RASCAL_RUNTIME_VERSION);
}
// Check RASCAL_COMPILER_VERSION
String rascal_compiler_version = (String) in.readObject();
try {
sv = new SemVer(rascal_runtime_version);
} catch(Exception e){
throw new RuntimeException("Invalid value for RASCAL_COMPILER_VERSION in Rascal executable");
}
if(!sv.satisfiesVersion("~" + VersionInfo.RASCAL_COMPILER_VERSION)){
throw new RuntimeException("RASCAL_COMPILER_VERSION " + rascal_compiler_version + " in Rascal executable incompatible with current version " + VersionInfo.RASCAL_COMPILER_VERSION);
}
// System.err.println("RascalShell: Rascal: " + VersionInfo.RASCAL_VERSION + "; Runtime: " + VersionInfo.RASCAL_RUNTIME_VERSION + "; Compiler: " + VersionInfo.RASCAL_COMPILER_VERSION);
// System.err.println("Executable : Rascal: " + rascal_version + "; Runtime: " + rascal_runtime_version + "; Compiler: " + rascal_compiler_version);
// String[] errors
ISet errors = (ISet) in.readObject();
if(errors.size() > 0){
return new RVMExecutable(errors);
}
// public String name;
String module_name = (String) in.readObject();
// public IMap moduleTags;
IMap moduleTags = (IMap) in.readObject();
// public IMap symbol_definitions;
IMap symbol_definitions = (IMap) in.readObject();
// public ArrayList<Function> functionStore;
// public Map<String, Integer> functionMap;
Function[] functionStore = (Function[]) in.readObject();
n = functionStore.length;
HashMap<String, Integer> functionMap = new HashMap<String, Integer>(n);
for(int i = 0; i < n; i++){
functionMap.put(functionStore[i].getName(), i);
}
// public ArrayList<Type> constructorStore;
n = (Integer) in.readObject();
ArrayList<Type> constructorStore = new ArrayList<Type>(n);
for(int i = 0; i < n; i++){
constructorStore.add(i, (Type) in.readObject());
}
// public Map<String, Integer> constructorMap;
Map<String, Integer> constructorMap = (Map<String, Integer>) in.readObject();
// public OverloadedFunction[] overloadedStore;
OverloadedFunction[] overloadedStore = (OverloadedFunction[]) in.readObject();
// public Map<String, Integer> resolver;
HashMap<String, Integer> resolver = (HashMap<String, Integer>) in.readObject();
// ArrayList<String> initializers;
ArrayList<String> initializers = (ArrayList<String>) in.readObject();
Object o = in.readObject(); //transient for boot
// ArrayList<String> testsuites;
if(o instanceof ArrayList<?>){
o = in.readObject(); // skip: ArrayList<String> testsuites;
}
// public String uid_module_init;
String uid_module_init = (String) o;
// public String uid_module_main;
String uid_module_main = (String) in.readObject();
o = in.readObject(); //transient for boot
if(o instanceof String){
o = in.readObject(); // skip: public String uid_module_main_testsuite;
}
// public byte[] jvmByteCode;
byte[] jvmByteCode = (byte[]) o;
// public String fullyQualifiedDottedName;
String fullyQualifiedDottedName = (String) in.readObject();
RVMExecutable ex = new RVMExecutable(module_name, moduleTags, symbol_definitions, functionMap, functionStore,
constructorMap, constructorStore, resolver, overloadedStore, initializers, uid_module_init,
uid_module_main, store, vf, false);
ex.setJvmByteCode(jvmByteCode);
ex.setFullyQualifiedDottedName(fullyQualifiedDottedName);
return ex;
}
}