if (module == null) {
throw new IllegalArgumentException("lecc.CodeGenerator.generateSCCode(): module is null");
}
ModuleName moduleName = module.getName();
CompilerMessageLogger generateLogger = new MessageLogger();
//System.out.println ("Gencode: " + moduleName);
// Top level try catch block. All exceptions/errors should be caught and logged with the logger.
try {
// Since this module has been modified we need to tell the class loader to reset.
// NOTE! We must do this early so that nothing in the code generation process grabs
// a reference to a class loader that is discarded at a later point.
module.resetClassLoader(isForAdjunct());
GeneratedCodeInfo existingCodeInfo = null;
GeneratedCodeInfo newCodeInfo = getNewCodeInfo(module);
if (forceCodeRegen && !isForAdjunct() && !LECCMachineConfiguration.generateBytecode()) {
// If we are generating source code we just do a clean sweep of the target directory.
// If generating bytecode we will try to do a smarter cleanup after generating.
try {
resourceRepository.delete(getModuleResourceFolder(module));
// TODO: actually handle this.
} catch (IOException ioe) {
// There was a problem deleting one or more files.
// This used to be just ignored.
System.err.println("Problem deleting one or more files: " + ioe);
}
} else
if (!isForAdjunct()){
// Load the information about any existing generated code.
if (forImmediateUse) {
existingCodeInfo = immediateUseModuleNameToGeneratedCodeInfoMap.get(moduleName);
}
if (existingCodeInfo == null) {
existingCodeInfo = getExistingCodeInfo(moduleName, resourceRepository);
}
}
// Do we need to generate all sources.
boolean generateAll = isForAdjunct() || needToGenerateAll (newCodeInfo, existingCodeInfo) || forceCodeRegen;
// For the moment we assume that the timestamp is constant for all things in a given module.
if (!isForAdjunct()) {
long timeStamp = newCodeInfo.cal_source_timeStamp;
if (existingCodeInfo == null || timeStamp != existingCodeInfo.cal_source_timeStamp) {
// The source for this module has been changed. Add it to the changed module list.
changedModules.put (moduleName, new Long(timeStamp));
generateAll = true;
} else {
// Check imported modules.
ModuleTypeInfo mti = module.getModuleTypeInfo();
for (int i = 0; i < mti.getNImportedModules(); ++i) {
ModuleTypeInfo imti = mti.getNthImportedModule(i);
Long l = changedModules.get(imti.getModuleName());
if (l != null && l.longValue() > timeStamp) {
generateAll = true;
break;
}
}
}
}
// Get module and info
informStatusListeners(StatusListener.SM_GENCODE, moduleName);
JavaGenerator javaGenerator;
if (LECCMachineConfiguration.generateBytecode()) {
if (classFileWriter == null) {
classFileWriter = resourceRepository.getAsynchronousFileWriter();
}
try {
javaGenerator = new LECCJavaBytecodeGenerator(module, resourceRepository, isForAdjunct(), forImmediateUse, classFileWriter);
} catch (IOException e) {
generateLogger.logMessage(new CompilerMessage(new MessageKind.Error.CodeGenerationAbortedWithException(moduleName, e)));
return CompilerMessage.Severity.ERROR;
}
} else {
// Generate a list of package names for all the modules associated with this module.
Set<ModuleName> dependeeModuleNameSet = new HashSet<ModuleName>();
dependeeModuleNameSet.add(moduleName);
buildDependeeModuleNameSet(module.getModuleTypeInfo(), dependeeModuleNameSet);
try {
javaGenerator = new LECCJavaSourceGenerator(module, resourceRepository, dependeeModuleNameSet);
} catch (IOException e) {
generateLogger.logMessage(new CompilerMessage(new MessageKind.Error.CodeGenerationAbortedWithException(moduleName, e)));
return CompilerMessage.Severity.ERROR;
}
}
// Pass the status listeners to the java generator.
javaGenerator.setStatusListeners(getStatusListeners());
if (codeGenerationStats != null) {
// Check if this is a module we want to generate stats for.
String statsModuleNameString = System.getProperty("org.openquark.cal.machine.lecc.code_generation_stats");
if (statsModuleNameString != null &&
(statsModuleNameString.equals("") || statsModuleNameString.equals(moduleName.toSourceText()) || statsModuleNameString.trim().toLowerCase().equals("all"))) {
// Set the object used to collect code generation info.
javaGenerator.setCodeGenerationStatsCollector(codeGenerationStats);
// Inform the stats collector that we're generating stats for new module.
codeGenerationStats.startNewModule(moduleName);
}
}
// Retrieve the custom class loader that is stored in the Program object.
CALClassLoader calLoader = module.getClassLoader();
if (calLoader == null) {
// This should never be null.
throw (new CodeGenerationException ("Null class loader in LECCProgram."));
}
// Create a new Set to hold the TypeConstructor instances associated with this module
Set<TypeConstructor> typeConsSet = new HashSet<TypeConstructor>();
// Emit supercombinator symbols and collate data definitions
// For every symbol, we generate its code, which determines what it is, and builds auxiliary entities
for (final MachineFunction machineFunction : module.getFunctions()) {
// Get the label.
MachineFunctionImpl label = (MachineFunctionImpl)machineFunction;
if (label.isCodeGenerated()) {
continue;
}
if (label.getAliasOf() != null || label.getLiteralValue() != null) {
label.setCodeGenerated(true);
//System.out.println("**** Skipping: " + label.getQualifiedName() + " -> alias of: " + label.getCoreFunction().getAliasOf());
continue;
}
// Check to see if this is a primitive function.
// Functions marked as primitive can be one of two things:
// 1) a primitive function implemented as a machine specific operator
// 2) a primitive function for which a hard coded machine specific implementation is provided.
// If the function falls into category two we don't want to generate anything for it.
if (label.isPrimitiveFunction()) {
// Check to see if this is considered a primitiv op.
if (PrimOps.fetchInfo(label.getQualifiedName()) == null && !label.getName().equals("unsafeCoerce")) {
// don't need to generate code.
continue;
}
}
// This can either be a supercombinator or a data declaration
if (label.getExpressionForm().asPackCons() != null) {
// This is a data declaration
// Add the associated TypeConstructor to the set for later code generation.
DataConstructor dc = label.getExpressionForm().asPackCons().getDataConstructor();
TypeConstructor typeCons = dc.getTypeConstructor();
typeConsSet.add(typeCons);
} else {
// This is a supercombinator. Emit the definition if necessary.
try {
LECCModule.FunctionGroupInfo fgi = module.getFunctionGroupInfo(label);
javaGenerator.createFunction(fgi, generateAll, logger);
fgi.setCodeGenerated(true);
} catch (CodeGenerationException e) {
try {
// Note: The code generation could potentially have failed because a foreign type or a foreign function's corresponding Java entity
// could not be resolved. (In this case the CodeGenerationException would be wrapping an UnableToResolveForeignEntityException)
final Throwable cause = e.getCause();
if (cause instanceof UnableToResolveForeignEntityException) {
generateLogger.logMessage(((UnableToResolveForeignEntityException)cause).getCompilerMessage());
}
generateLogger.logMessage(new CompilerMessage(new MessageKind.Error.CodeGenerationAborted(label.getQualifiedName().getQualifiedName()), e));
} catch (CompilerMessage.AbortCompilation e2) {/* Ignore and continue. */}
return CompilerMessage.Severity.ERROR;
} catch (CompilerMessage.AbortCompilation e) {
try {
generateLogger.logMessage(new CompilerMessage(new MessageKind.Error.CodeGenerationAborted(label.getQualifiedName().getQualifiedName())));
} catch (CompilerMessage.AbortCompilation e2) {/* Ignore and continue. */}
return CompilerMessage.Severity.ERROR;
}
if (isForAdjunct()) {
String fullClassName = CALToJavaNames.createFullClassNameFromSC(label.getQualifiedName(), module);
calLoader.markAdjunctClass(fullClassName);
}
}
// Clear the expression form, since we're finished with it.
label.setCodeGenerated(true);
}
// Emit any data definitions as necessary.
for (final TypeConstructor typeCons : typeConsSet) {
if (LECCMachineConfiguration.TREAT_ENUMS_AS_INTS && TypeExpr.isEnumType(typeCons)) {
//System.out.println ("**** skipping data type because it is enum: " + typeConsName);
continue;
}
if (isForAdjunct()) {
String className = CALToJavaNames.createFullClassNameFromType (typeCons, module);
calLoader.markAdjunctClass(className);
}
try {
javaGenerator.createTypeDefinition(typeCons, generateAll, logger);
} catch (CodeGenerationException e) {
try {
// Note: The code generation could potentially have failed because a foreign type or a foreign function's corresponding Java entity
// could not be resolved. (In this case the CodeGenerationException would be wrapping an UnableToResolveForeignEntityException)
final Throwable cause = e.getCause();
if (cause instanceof UnableToResolveForeignEntityException) {
generateLogger.logMessage(((UnableToResolveForeignEntityException)cause).getCompilerMessage());
}
String className = CALToJavaNames.createFullClassNameFromType (typeCons, module);
generateLogger.logMessage(new CompilerMessage(new MessageKind.Error.CodeGenerationAborted(className), e));
} catch (CompilerMessage.AbortCompilation e2) {/* Ignore and continue. */}
return CompilerMessage.Severity.ERROR;
}
}
informStatusListeners(StatusListener.SM_GENCODE_DONE, moduleName);
// Now compile the java sources for this module.
try {
javaGenerator.wrap();
} catch (CodeGenerationException e) {
try {
generateLogger.logMessage(new CompilerMessage(new MessageKind.Error.FailedToFinalizeJavaCode(moduleName), e));
} catch (CompilerMessage.AbortCompilation e2) {/* Ignore and continue. */}
return CompilerMessage.Severity.ERROR;
}
// Update the generated code info map. Write out the generated code info if it's new.
if (!isForAdjunct()) {
if (forImmediateUse) {
immediateUseModuleNameToGeneratedCodeInfoMap.put(module.getName(), newCodeInfo);
} else {
immediateUseModuleNameToGeneratedCodeInfoMap.remove(module.getName());
if (!newCodeInfo.equals(existingCodeInfo)) {
// Get the module folder. Ensure that it exists.
ProgramResourceLocator.Folder moduleFolder = getModuleResourceFolder(module);
try {
resourceRepository.ensureFolderExists(moduleFolder);
} catch (IOException e) {
// the folder couldn't be created.
generateLogger.logMessage(new CompilerMessage(new MessageKind.Error.CodeGenerationAbortedWithException(moduleName, e)));
}
}
if (forceCodeRegen && LECCMachineConfiguration.generateBytecode()) {
// Delete any extraneous files in the target directory.
deleteExtraneousCode(module, javaGenerator.getGeneratedClassFiles());
}
// write out the compiled module definition.
writeCompiledModuleInfo (module, generateLogger);
}
}
} catch (Exception e) {
try {
if (generateLogger.getNErrors() > 0) {
//if an error occurred previously, we continue to compile the program to try to report additional
//meaningful compilation errors. However, this can produce spurious exceptions related to the fact
//that the program state does not satisfy preconditions because of the initial error(s). We don't
//report the spurious exception as an internal coding error.
generateLogger.logMessage(new CompilerMessage(new MessageKind.Fatal.UnableToRecoverFromCodeGenErrors(module.getName())));
} else {
generateLogger.logMessage(new CompilerMessage(new MessageKind.Fatal.CodeGenerationAbortedDueToInternalCodingError(module.getName()), e));
}
} catch (CompilerMessage.AbortCompilation ace) {/* Ignore and continue. */}
} catch (Error e) {
try {
generateLogger.logMessage(new CompilerMessage(new MessageKind.Error.CodeGenerationAbortedWithException(module.getName(), e), null));
} catch (CompilerMessage.AbortCompilation e2) {/* Ignore and continue. */}
} finally {
// Log messages to the passed-in logger.
if (logger != null) {
try {
logger.logMessages(generateLogger);
} catch (CompilerMessage.AbortCompilation e) {
/* Ignore and continue. */
}
}
}
return generateLogger.getMaxSeverity();
}