TypeConstructor typeCons = null;
boolean isEnumDataType = false;
for (int i = 0; i < alts.length; ++i) {
if (!alts[i].isDefaultAlt()) {
DataConstructor dc = (DataConstructor)alts[i].getFirstAltTag();
typeCons = dc.getTypeConstructor();
isEnumDataType = SCJavaDefn.isEnumDataType(dc);
break;
}
}
if (typeCons == null) {
throw new CodeGenerationException ("Unable to retrieve TypeConstructor for switch in " + getFunctionName() + ".");
}
int nDataConstructorsForType = typeCons.getNDataConstructors();
if (nDataConstructorsForType == 0) {
throw new CodeGenerationException ("Encountered a data type with zero data constructors in a switch in " + getFunctionName() + ".");
}
DataConstructor[] allDCs = new DataConstructor [nDataConstructorsForType];
for (int i = 0; i < nDataConstructorsForType; ++i ) {
DataConstructor dc = typeCons.getNthDataConstructor(i);
allDCs[dc.getOrdinal()] = dc;
}
// If all the case alternatives return a boolean literal we may
// be able to optimize this.
boolean isa = true;
for (int i = 0; i < alts.length; ++i) {
SwitchAlt switchAlt = alts[i];
if (!switchAlt.isDefaultAlt() &&
!(switchAlt.getFirstAltTag() instanceof DataConstructor)) {
isa = false;
break;
}
Expression altExpr = switchAlt.getAltExpr();
if (altExpr.asLiteral() != null) {
if (!(altExpr.asLiteral().getLiteral() instanceof Boolean)) {
isa = false;
break;
}
} else if (altExpr.asVar() != null) {
DataConstructor dcv = altExpr.asVar().getDataConstructor();
if (dcv == null || !isTrueOrFalseDataCons(dcv)) {
isa = false;
break;
}
} else {
isa = false;
break;
}
}
// We either need to have a default alt or an alt for every data
// constructor for the type.
if (isa && (eswitch.hasDefaultAlt() || nDataConstructorsForType == alts.length)) {
return generateIsAFunctionFromSwitch (eswitch, variableContext);
}
// Determining if any of the alternates have alt vars that need to be extracted from the
// switch value.
boolean noAltVars = true;
for (int i = 0; i < alts.length; ++i) {
if (alts[i].hasVars()) {
noAltVars = false;
break;
}
}
if (LECCMachineConfiguration.OPTIMIZE_SINGLE_DC_CASES && nDataConstructorsForType == 1) {
// If there is only one DataConstructor we can eliminate the switch.
if (codeGenerationStats != null) {
codeGenerationStats.incrementSingleDCCases();
}
if (codeGenerationStats != null) {
codeGenerationStats.incrementSingleDCCases();
}
return generateSingleAltSwitch(eswitch, variableContext);
}
// Create a boolean array to determine which cases we have.
boolean[] caseExistsArray = new boolean[nDataConstructorsForType]; // false by default.
for (int i = 0; i < alts.length; ++i) {
if (!alts[i].isDefaultAlt()) {
List<Object> tags = alts[i].getAltTags();
for (final Object tag : tags) {
DataConstructor dc = (DataConstructor)tag;
caseExistsArray[dc.getOrdinal()] = true;
}
}
}
// Generate the switch conditional.
LocalVariable caseVar = null;
SwitchStatement switchStatement;
if (noAltVars /*&& (defaultAltProvided || !missingCases)*/) {
// If there are no alt vars and we don't have to fill in any missing cases we don't need a local
// variable holding the switchexpression. This means we can generate something like:
// switch (expression.evaluate().getOrdinal())
ExpressionContextPair ecp = generateUnboxedArgument(JavaTypeName.INT, eswitch.getSwitchExpr(), variableContext);
switchBlock.addStatement(ecp.getContextBlock());
JavaExpression conditionExpression = ecp.getJavaExpression();
switchStatement = new SwitchStatement(conditionExpression);
switchBlock.addStatement(switchStatement);
} else {
// If there are alternates that have alt vars we generate something like:
// RTValue caseVar;
// switch ((caseVar = expression.evaluate()).getIntValue())
// We do the assignment of the local in the actual switch statement
// because analysis of the generated bytecode has shown this to be
// slightly more efficient than initializing the local as part of the
// declaration.
JavaStatement caseVarDeclaration = null;
Expression switchExpression = eswitch.getSwitchExpr();
// Generate a local variable and assign the evaluated value of the expression
// we are switching on.
JavaTypeName typeClassName = isEnumDataType ? JavaTypeNames.RTVALUE : CALToJavaNames.createTypeNameFromType(typeCons, module);
caseVar = new LocalVariable("$case" + nestedCaseLevel, typeClassName);
// Add the local variable declaration.
caseVarDeclaration = new LocalVariableDeclaration(caseVar);
switchBlock.addStatement(caseVarDeclaration);
// Compile the expression we are switching on strictly.
ExpressionContextPair pair = genS_E(switchExpression, variableContext);
switchBlock.addStatement(pair.getContextBlock());
JavaExpression caseExpression = pair.getJavaExpression();
//caseExpression = releaseVarsInSwitchCondition(eswitch, caseExpression, variableContext);
// We may need to cast the result of the case expression to the type of the local variable.
caseExpression = (isEnumDataType || caseExpression instanceof ClassInstanceCreationExpression) ? caseExpression : new CastExpression(typeClassName, caseExpression);
// Assign the result of the switch expression to the local an then get the ordinal value.
JavaExpression assignLocal = new JavaExpression.Assignment(caseVar, caseExpression);
JavaExpression getOrdinal = SCJavaDefn.createInvocation(assignLocal, SCJavaDefn.GETORDINALVALUE);
switchStatement = new SwitchStatement(getOrdinal);
switchBlock.addStatement(switchStatement);
}
// Populate the switch statement with case statement groups.
for (final SwitchAlt alt : alts) {
List<Object> altTags = alt.getAltTags();
// If no variables are used, we can share the code among all data constructors for this alt.
if (!alt.hasVars()) {
Block caseBlock = new Block();
// Add a comment for the data constructors in the group if any (ie. if not the default alt).
if (alt.getFirstAltTag() instanceof DataConstructor) {
StringBuilder commentSB = new StringBuilder();
boolean firstDC = true;
for (final Object tag : altTags) {
DataConstructor tagDC = (DataConstructor)tag;
if (firstDC) {
firstDC = false;
} else {
commentSB.append(", ");
}
commentSB.append(tagDC.getName().getQualifiedName());
}
caseBlock.addStatement(new LineComment(commentSB.toString()));
}
// Create a new child variable scope to handle the alternate and any let variables it contains.
variableContext.pushJavaScope();
// Compile the body of the alternate.
JavaStatement altStatement = genS_R(alt.getAltExpr(), variableContext);
caseBlock.addStatement(variableContext.popJavaScope());
caseBlock.addStatement(altStatement);
if (alt.isDefaultAlt()) {
switchStatement.addCase(new SwitchStatement.DefaultCase(caseBlock));
} else {
int[] caseLabels = new int[altTags.size()];
int index = 0;
for (final Object tag : altTags) {
if (!(tag instanceof DataConstructor)) {
throw new CodeGenerationException ("Unknown tag type in DC case statement in " + getFunctionName() + ": " + tag.getClass().getName());
}
caseLabels[index] = ((DataConstructor)tag).getOrdinal();
index++;
}
switchStatement.addCase(new SwitchStatement.IntCaseGroup(caseLabels, caseBlock));
}
} else {
// The alts use variables.
if (alt instanceof SwitchAlt.Positional) {
// Positional notation for extracted variables.
// For now, a separate code block must be generated for each data constructor in the case.
Collection<List<DataConstructor>> tagGroups = consolidatePositionalSwitchAlt((SwitchAlt.Positional)alt);
for (final List<DataConstructor> group : tagGroups) {
// Must be a data constructor tag, since there are field names (see Expression.Switch.SwitchAlt).
Block caseBlock = new Block();
int[] caseLabels = new int[group.size()];
int index = 0;
DataConstructor firstDC = null;
// Must be a data constructor tag, since there are field names (see Expression.Switch.SwitchAlt).
for (final DataConstructor tagDC : group) {
if (firstDC == null) {
firstDC = tagDC;
} else
if (tagDC.getOrdinal() < firstDC.getOrdinal()) {
firstDC = tagDC;
}
caseBlock.addStatement(new LineComment(tagDC.getName().getQualifiedName()));
caseLabels[index] = tagDC.getOrdinal();
index++;
}
caseBlock.addStatement(new LineComment("Decompose data type to access members."));
// Create a new child variable scope to handle the alternate and any let variables it contains.
variableContext.pushJavaScope();
// Get this alternative's variables. These have to be added to the active list of scope variables
TypeExpr fieldTypes[] = SCJavaDefn.getFieldTypesForDC(firstDC);
for (final AltVarIndexPair altVarIndexPair : getAltVarIndexList(alt, firstDC)) {
String altVar = altVarIndexPair.getAltVar();
int fieldIndex = altVarIndexPair.getIndex();
QualifiedName qn = QualifiedName.make(currentModuleName, altVar);
VarInfo.DCMember vi = variableContext.addDCField(qn, fieldTypes[fieldIndex]);
boolean fieldIsStrict =
!LECCMachineConfiguration.IGNORE_STRICTNESS_ANNOTATIONS;
for (final DataConstructor tagDC : group) {
fieldIsStrict = fieldIsStrict && tagDC.isArgStrict(fieldIndex);
}
if (fieldIsStrict) {
vi.setEvaluated(true);
}
String fieldName = SCJavaDefn.getJavaFieldNameFromDC(firstDC, fieldIndex);
String fieldGetterName = "get" + fieldName;
// Generate the code defining the variable.
if (fieldIsStrict) {
if (SCJavaDefn.canTypeBeUnboxed(fieldTypes[fieldIndex])) {
// This is a strict field of a primitive type so has both a boxed and unboxed form.
JavaExpression unboxedInitializer =
new JavaExpression.MethodInvocation.Instance(caseVar,
fieldGetterName + "_As_" + SCJavaDefn.getNameForPrimitive(fieldTypes[fieldIndex]),
SCJavaDefn.typeExprToTypeName(fieldTypes[fieldIndex]),
JavaExpression.MethodInvocation.InvocationType.VIRTUAL);
vi.updateUnboxedVarDef(unboxedInitializer);
JavaExpression localVar = new LocalVariable(vi.getJavaName()+"$U", vi.getUnboxedType());
vi.updateUnboxedReference(localVar);
JavaExpression boxedDef = SCJavaDefn.boxExpression(vi.getUnboxedType(), localVar);
vi.updateStrictReference(boxedDef);
vi.updateLazyReference(boxedDef);
} else {
// RTValue altVarName = ((DCClass)caseVar).getFieldn();
JavaExpression initializer = new JavaExpression.MethodInvocation.Instance(caseVar, fieldGetterName, JavaTypeNames.RTVALUE, JavaExpression.MethodInvocation.InvocationType.VIRTUAL);
vi.updateStrictVarDef (initializer);
JavaExpression localVar = new LocalVariable(vi.getJavaName(), JavaTypeNames.RTVALUE);
vi.updateStrictReference(localVar);
vi.updateLazyReference(localVar);
}
} else {
// RTValue altVarName = ((DCClass)caseVar).getFieldn();
JavaExpression initializer = new JavaExpression.MethodInvocation.Instance(caseVar, fieldGetterName, JavaTypeNames.RTVALUE, JavaExpression.MethodInvocation.InvocationType.VIRTUAL);
vi.updateLazyVarDef (initializer);
JavaExpression localVar = new LocalVariable(vi.getJavaName(), JavaTypeNames.RTVALUE);
vi.updateLazyReference(localVar);
JavaExpression evaluatedVar = SCJavaDefn.createInvocation(localVar, SCJavaDefn.EVALUATE, SCJavaDefn.EXECUTION_CONTEXT_VAR);
vi.updateStrictReference(evaluatedVar);
if (SCJavaDefn.canTypeBeUnboxed(fieldTypes[fieldIndex])) {
vi.updateUnboxedReference(SCJavaDefn.unboxValue(vi.getUnboxedType(), evaluatedVar));
}
}
}
// Compile the actual body of the alternate.
JavaStatement altStatement = genS_R(alt.getAltExpr(), variableContext);
caseBlock.addStatement(variableContext.popJavaScope());
caseBlock.addStatement(altStatement);
switchStatement.addCase(new SwitchStatement.IntCaseGroup(caseLabels, caseBlock));
}
} else {
// Matching notation for switch alternate.
Map<FieldName, String> fieldNameToVarNameMap = ((SwitchAlt.Matching)alt).getFieldNameToVarNameMap();
Block caseBlock = new Block();
int[] caseLabels = new int[altTags.size()];
int index = 0;
DataConstructor firstDC = null;
// Must be a data constructor tag, since there are field names (see Expression.Switch.SwitchAlt).
for (final Object altTag : altTags) {
DataConstructor tagDC = (DataConstructor)altTag;
if (firstDC == null) {
firstDC = tagDC;
} else if (tagDC.getOrdinal() < firstDC.getOrdinal()) {
firstDC = tagDC;
}
caseBlock.addStatement(new LineComment(tagDC.getName().getQualifiedName()));
caseLabels[index] = tagDC.getOrdinal();
index++;
}
caseBlock.addStatement(new LineComment("Decompose data type to access members."));
// Create a new child variable scope to handle the alternate and any let variables it contains.
variableContext.pushJavaScope();
for (int iField = 0; iField < firstDC.getArity(); ++iField) {
FieldName fn = firstDC.getNthFieldName(iField);
String altVar = fieldNameToVarNameMap.get(fn);
if (altVar == null) {
continue;
}
QualifiedName qn = QualifiedName.make(currentModuleName, altVar);
TypeExpr fieldType = SCJavaDefn.getFieldTypeForDC(firstDC, fn);
VarInfo.DCMember vi = variableContext.addDCField(qn, fieldType);
boolean fieldIsStrict = !LECCMachineConfiguration.IGNORE_STRICTNESS_ANNOTATIONS;
for (final Object altTag : altTags) {
DataConstructor tagDC = (DataConstructor)altTag;
fieldIsStrict = fieldIsStrict & tagDC.isArgStrict(tagDC.getFieldIndex(fn));
}
if (fieldIsStrict) {
vi.setEvaluated(true);
}