/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* EntryPointGenerator.java
* Created: Feb 19, 2003 at 12:15:01 PM
* By: Raymond Cypher
*/
package org.openquark.cal.compiler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.openquark.cal.compiler.SourceModel.Parameter;
import org.openquark.cal.compiler.io.EntryPoint;
import org.openquark.cal.compiler.io.EntryPointSpec;
import org.openquark.cal.compiler.io.InputPolicy;
import org.openquark.cal.compiler.io.OutputPolicy;
import org.openquark.cal.internal.machine.EntryPointImpl;
import org.openquark.cal.machine.CodeGenerator;
import org.openquark.cal.machine.Module;
import org.openquark.cal.machine.Program;
import org.openquark.cal.machine.StatusListener;
/**
* Warning- this class should only be used by the CAL compiler implementation. It is not part of the
* external API of the CAL platform.
* <p>
* This is the EntryPointGenerator class.
*
* This class is used to generate entry points for adjuncts.
* <p>
* Created: Feb 19, 2003 at 12:15:01 PM
* @author Raymond Cypher
*/
public abstract class EntryPointGenerator implements Compiler {
public static final String SHOW_ADJUNCT_PROP = "org.openquark.cal.show_adjunct";
private final CALCompiler compiler;
private final Program program;
/** Collection of status listeners */
private final List<StatusListener> statusListeners = new ArrayList<StatusListener>();
/** Flag used to indicate that all code should be regenerated. */
private boolean forceCodeRegen;
/** Flag to indicate that the code being compiled will be executing immediately.
* this allows for optimizations in generating/loading.
*/
private boolean forImmediateUse;
/**
* Constructor for EntryPointGenerator.
* @param program
*/
protected EntryPointGenerator(Program program) {
compiler = new CALCompiler();
if (program == null) {
throw new NullPointerException("Argument 'program' cannot be null.");
}
this.program = program;
// makes sure that the CALCompiler has a copy of the packager with the program
// from the very beginning
compiler.setPackager(makePackager(program));
}
/**
* Accessor so that the CALCompiler can be used by unit tests. Should remain package scope.
*/
CALCompiler getCompiler() {
return compiler;
}
abstract protected Packager makePackager(Program program);
abstract protected CodeGenerator makeCodeGenerator();
private final CompilerMessage.Severity compileAdjunct(AdjunctSource adjunctSource, ModuleName adjunctModule, CompilerMessageLogger logger) {
// Use a compiler-specific logger.
CompilerMessageLogger compilerLogger = new MessageLogger(true);
compiler.setCompilerMessageLogger(compilerLogger);
Packager pkgr = makePackager(program);
for (final StatusListener sl : statusListeners) {
pkgr.addStatusListener(sl);
}
try {
// Compile it, indicating that this is an adjunct
compiler.compileAdjunct(adjunctSource, pkgr, adjunctModule);
compiler.setCompilerMessageLogger(null);
if (compilerLogger.getNErrors() > 0) {
// Errors
return compilerLogger.getMaxSeverity();
}
// Now we want to generate code for the program.
boolean bfor = forImmediateUse;
forImmediateUse = true;
CodeGenerator cg = makeCodeGenerator();
forImmediateUse = bfor;
Module module = program.getModule(adjunctModule);
cg.generateSCCode(module, compilerLogger);
cg.finishedGeneratingCode(compilerLogger);
} catch (CompilerMessage.AbortCompilation e) {
// Aborted compilation.
} finally {
// Log any messages from the compiler-specific logger.
logger.logMessages(compilerLogger);
}
return compilerLogger.getMaxSeverity();
}
/**
* Generate an entry point for the entryPointSpec.
* @param entryPointSpec - Information about the entry point.
* @param currentModule - name of the current module
* @param logger
* @return an EntryPoint
* @see EntryPointSpec
*/
public final EntryPoint getEntryPoint (EntryPointSpec entryPointSpec, ModuleName currentModule, CompilerMessageLogger logger) {
if (entryPointSpec == null || currentModule == null) {
throw new IllegalArgumentException("Invalid argument in EntryPointGenerator.(EntryPointSpec EntryPointSpec, String currentModule, CompilerMessageLogger logger).");
}
List<EntryPoint> entryPoints = getEntryPoints( Collections.singletonList(entryPointSpec), currentModule, logger);
if (entryPoints == null) {
return null;
}
return entryPoints.get(0);
}
/**
* Generate a list of entry points, one for each of the input entryPointSpecs
* @param entryPointSpecs - list of EntryPointSpecs
* @param currentModule - name of the current module
* @param logger
* @return a list of EntryPoints
*/
public final List<EntryPoint> getEntryPoints (List<EntryPointSpec> entryPointSpecs, ModuleName currentModule, CompilerMessageLogger logger) {
if (entryPointSpecs == null || entryPointSpecs.size() == 0 || currentModule == null) {
throw new IllegalArgumentException ("Invalid argument in EntryPointGenerator.getEntryPoint(List targetInfo, String currentModule, CompilerMessageLogger logger)");
}
return getEntryPointsFromEntryPointSpecs (entryPointSpecs, currentModule, logger);
}
/**
* Generate an entry point for each of the entry point specs
* @param entryPointSpecs - A list of EntryPointSpec.
* @param currentModule - name of the current module
* @param logger
* @return a list of EntryPoint
*/
private final List<EntryPoint> getEntryPointsFromEntryPointSpecs (List<EntryPointSpec> entryPointSpecs, ModuleName currentModule, CompilerMessageLogger logger) {
Module cmod = program.getModule(currentModule);
ArrayList<SourceModel.FunctionDefn.Algebraic> adjunctSource = new ArrayList<SourceModel.FunctionDefn.Algebraic>();
int targetCount = 0;
ArrayList<EntryPointSpec> adjuntEntryPointSpecs = new ArrayList<EntryPointSpec> (entryPointSpecs.size());
for (final EntryPointSpec entryPointSpec : entryPointSpecs) {
String adjunctName = "rt_" + entryPointSpec.getFunctionalAgentName().getUnqualifiedName() + "_" + (targetCount++) + "_";
String baseAdjunctName = adjunctName;
int i = 0;
while (cmod.getFunction(adjunctName) != null) {
adjunctName = baseAdjunctName + (i++);
}
adjunctSource.add(
SourceModel.FunctionDefn.Algebraic.make(
adjunctName, Scope.PRIVATE, null, SourceModel.Expr.makeGemCall(entryPointSpec.getFunctionalAgentName())));
adjuntEntryPointSpecs.add (EntryPointSpec.make(QualifiedName.make(currentModule, adjunctName), entryPointSpec.getInputPolicies(), entryPointSpec.getOutputPolicy()));
}
return generateEntryPoints(
adjuntEntryPointSpecs,
new AdjunctSource.FromSourceModel(
adjunctSource.toArray(new SourceModel.FunctionDefn[0])),
currentModule,
logger);
}
/**
* {@inheritDoc}
*/
public final EntryPoint getEntryPoint (AdjunctSource adjunctSource, EntryPointSpec entryPointSpec, ModuleName targetModuleName, CompilerMessageLogger logger) {
if (entryPointSpec == null || adjunctSource == null) {
throw new IllegalArgumentException("Invalid argument in EntryPointGenerator.getEntryPoint (AdjunctSource adjunctSource, EntryPointSpec entryPointSpec, String targetModuleName, CompilerMessageLogger logger).");
}
List<EntryPoint> entryPoints = getEntryPoints (adjunctSource,
Collections.singletonList(entryPointSpec),
targetModuleName, logger);
if (entryPoints == null) {
return null;
}
return entryPoints.get(0);
}
/**
* {@inheritDoc}
*/
public final List<EntryPoint> getEntryPoints (AdjunctSource adjunctSource, List<EntryPointSpec> entryPointSpecs, ModuleName targetModuleName, CompilerMessageLogger logger) {
if (adjunctSource == null || entryPointSpecs == null || entryPointSpecs.size() == 0 || targetModuleName == null) {
throw new IllegalArgumentException("Invalid argument in EntryPointGenerator.getEntryPoint (AdjunctSource adjunctSource, List info, String targetModuleName, CompilerMessageLogger logger).");
}
return generateEntryPoints (entryPointSpecs, adjunctSource, targetModuleName, logger);// getEntryPointsHelper (adjunctSource, entryPointSpecs, targetModuleName, logger);
}
private final List<EntryPoint> generateEntryPoints (List<EntryPointSpec> entryPointSpecs, AdjunctSource adjunctSource, ModuleName targetModuleName, CompilerMessageLogger logger) {
compiler.setCompilerMessageLogger(logger);
if (logger == null) {
logger = compiler.getMessageLogger();
}
List<EntryPoint> entryPoints = new ArrayList<EntryPoint>(entryPointSpecs.size());
AdjunctAugmenter aug = new AdjunctAugmenter();
for (final EntryPointSpec entryPointSpec : entryPointSpecs) {
AugmentedAdjunct aa = aug.augmentAdjunct (entryPointSpec, adjunctSource);
if (aa == null || aa.getEntryPoint() == null || aa.getSource() == null) {
return null;
}
adjunctSource = aa.getSource();
entryPoints.add (aa.getEntryPoint());
}
CompilerMessage.Severity sev = compileAdjunct (adjunctSource, targetModuleName, logger);
if (sev.compareTo(CompilerMessage.Severity.ERROR) >= 0) {
return null;
}
return entryPoints;
}
/**
* Add a status listener.
* @param listener StatusListener
*/
public final void addStatusListener(StatusListener listener) {
if (!statusListeners.contains(listener)) {
statusListeners.add(listener);
}
}
/**
* Remove a status listener.
* @param listener StatusListener
*/
public final void removeStatusListener(StatusListener listener) {
statusListeners.remove(listener);
}
/**
* {@inheritDoc}
*/
public final void setForceCodeRegen (boolean b) {
forceCodeRegen = b;
}
/**
* {@inheritDoc}
*/
public final boolean getForceCodeRegen () {
return forceCodeRegen;
}
/**
* {@inheritDoc}
*/
public final void setForImmediateUse (boolean b) {
forImmediateUse = b;
}
/**
* {@inheritDoc}
*/
public final boolean isForImmediateUse() {
return forImmediateUse;
}
/**
* @return an iterator over the registered status listeners.
*/
protected final List<StatusListener> getStatusListeners () {
return statusListeners;
}
private final class AdjunctAugmenter {
int adjunctCount = 0;
/**
* Augment the adjunct source with marshalers and Inputable/Outputable instances.
* @param entryPointSpec - The information about the run target.
* @param adjunctSource - The source for the adjunct.
* @return An AugmentedAdjunct. i.e. Augmented source and a EntryPoint
*/
public AugmentedAdjunct augmentAdjunct (EntryPointSpec entryPointSpec, AdjunctSource adjunctSource){
QualifiedName adjunctTargetName = entryPointSpec.getFunctionalAgentName();
OutputPolicy outputPolicy = entryPointSpec.getOutputPolicy();
InputPolicy[] inputPolicies = entryPointSpec.getInputPolicies();
if (adjunctTargetName == null || adjunctSource == null || outputPolicy == null) {
throw new IllegalArgumentException ("Null argument in AdjunctAugmenter.augmentAdjunct.");
}
//if the input policies are null - create an array of default input policies
if (inputPolicies == null) {
// get the arity of the target function
CALTypeChecker.AdjunctInfo adjunctInfo = compiler.getTypeChecker().checkAdjunct(adjunctSource, adjunctTargetName.getModuleName(), adjunctTargetName.getUnqualifiedName());
if (adjunctInfo == null || adjunctInfo.getTargetType() == null) {
return null;
}
TypeExpr targetType = adjunctInfo.getTargetType();
inputPolicies = new InputPolicy[targetType.getArity()];
for(int i=0; i<inputPolicies.length; i++) {
inputPolicies[i] = InputPolicy.DEFAULT_INPUT_POLICY;
}
}
// Add a function which will be the new execution target. This function
// marshals the input arguments and then calls the original target functionalAgent,
// and finally marshals the output.
QualifiedName policyTargetName = generateIOPolicyAdjunctName(adjunctTargetName, adjunctTargetName.getModuleName());
AdjunctSource target = generateIOPolicyAdjunctSource(adjunctTargetName, policyTargetName, outputPolicy, inputPolicies);
if (target == null) {
return null;
}
adjunctSource = adjunctSource.concat(target);
if (System.getProperty(SHOW_ADJUNCT_PROP) != null) {
System.out.println ("\n\nAugmented Adjunct:\n" + adjunctSource + "\n\n");
}
return new AugmentedAdjunct (adjunctSource, new EntryPointImpl (policyTargetName));
}
/**
* Given a functional agent name (i.e. a function, data constructor or class method) and a target module name generate a name for
* a corresponding hidden adjunct in the target module.
* @param functionalAgentToRun
* @param targetModuleName
* @return The new function name.
*/
private QualifiedName generateIOPolicyAdjunctName (QualifiedName functionalAgentToRun, ModuleName targetModuleName) {
Module targetModule = program.getModule(targetModuleName);
String baseName = "io_" + functionalAgentToRun.getUnqualifiedName() + "_";
int z = 0;
while (targetModule.getModuleTypeInfo().getFunctionalAgent(baseName + z) != null) {
z++;
}
String targetName = baseName + z + "_" + adjunctCount++;
return QualifiedName.make(targetModuleName, targetName);
}
/**
* Generate 'wrapper' that marshals arguments from java to CAL and calls
* the client supplied functionalAgent.
* @param functionalAgentName - name of the client supplied functionalAgent
* @param ioAdjunctQN - name of the generated IO policy wrapper
* @param outputPolicy - output policy for the result
* @param inputPolicies - input policies for the inputs
* @return The adjunct source of the wrapper function.
*/
private AdjunctSource generateIOPolicyAdjunctSource (QualifiedName functionalAgentName, QualifiedName ioAdjunctQN, OutputPolicy outputPolicy, InputPolicy[] inputPolicies) {
//this creates a source model function of the following form:
// ioAdjunctQN arg0 arg1 ... = outputPolicy.marshaler ( functionalAgentName (inputPolicy1.marshaler arg0 arg1 ... argN) (inputPolicy1.marshaler argN+1 ...) ... )
List/*SourceModel.Parameter*/<Parameter> ioPolicyParams= new ArrayList<Parameter>();
SourceModel.Expr[] args= new SourceModel.Expr[inputPolicies.length];
//this counter is used to number all the arguments - normally each input policy has 1 argument
//but more complex input polices can have 0 or more arguments.
int policyArgCount = 0;
//create an application for each input policy
for(int i=0; i < args.length; i++) {
InputPolicy inputPolicy = inputPolicies[i];
int nArgs = inputPolicy.getNArguments();
SourceModel.Expr[] inputPolicyParts = new SourceModel.Expr[inputPolicy.getNArguments() + 1];
inputPolicyParts[0] = inputPolicy.getMarshaler();
//this loop is from 1 as the first element is always the inputPolicy itself, and then any arguments
for (int j = 1; j <= nArgs; ++j) {
String argName = "arg_" + (policyArgCount);
ioPolicyParams.add(SourceModel.Parameter.make(argName, false));
policyArgCount++;
inputPolicyParts[j] = SourceModel.Expr.Var.makeUnqualified(argName);
}
if (inputPolicyParts.length >= 2) {
args[i] = SourceModel.Expr.Application.make(inputPolicyParts);
} else {
args[i] = inputPolicyParts[0];
}
}
SourceModel.Expr ioPolicyExpression = SourceModel.Expr.makeGemCall(functionalAgentName, args);
//create an application for the output policy if non-null
if (outputPolicy != null) {
ioPolicyExpression = SourceModel.Expr.Application.make(new SourceModel.Expr[] {outputPolicy.getMarshaler(), ioPolicyExpression});
}
//create the function definition
return new AdjunctSource.FromSourceModel(
SourceModel.FunctionDefn.Algebraic.make(ioAdjunctQN.getUnqualifiedName(), Scope.PRIVATE, ioPolicyParams.toArray(new SourceModel.Parameter[ioPolicyParams.size()]), ioPolicyExpression));
}
}
/**
* A class to encapsulate the source of an augmented adjunct
* (i.e. an adjunct with additional marshaling, etc) and
* the associated EntryPoint.
*
* Created: Mar 19, 2004
* @author RCypher
*/
private static final class AugmentedAdjunct {
/** The augmented adjunct source. */
private final AdjunctSource source;
/** The EntryPoint associated with the target in the adjunct. */
private final EntryPoint entryPoint;
public AugmentedAdjunct (AdjunctSource source, EntryPoint entryPoint) {
this.source = source;
this.entryPoint = entryPoint;
}
public AdjunctSource getSource () {
return source;
}
public EntryPoint getEntryPoint () {
return entryPoint;
}
}
}