/* * java_stmt.tc - Semantic analysis of Java statements * * Copyright (C) 2001 Southern Storm Software, Pty Ltd. * Copyright (C) 2003 Gopal.V * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ %{ /* * Determine if a tree node corresponds to the empty statement. */ #define IsEmpty(node) ((node) == 0 || yykind((node)) == yykindof(ILNode_Empty)) /* * determine if a type1 implements type2 */ #if USED static int ILTypeImplements(ILGenInfo *info, ILType *type1, ILType *type2) { ILClass *classInfo1 = ILTypeToClass(info, type1); ILClass *classInfo2 = ILTypeToClass(info, type2); if(classInfo1 && classInfo2) { return ILClassImplements(classInfo1, classInfo2); } else { return 0; } } /* * determine if a type1 inherits type2 */ static int ILTypeInherits(ILGenInfo *info, ILType *type1, ILType *type2) { ILClass *classInfo1 = ILTypeToClass(info, type1); ILClass *classInfo2 = ILTypeToClass(info, type2); if(classInfo1 && classInfo2) { return ILClassInheritsFrom(classInfo1, classInfo2); } else { return 0; } } #endif /* * Obtain the default scope of the class */ #define GetDefaultScope(info) (((ILNode_ClassDefn*)(info->currentClass))->classInfo) /* * Perform semantic analysis on a sub-statement. */ static void StmtSem(ILNode *node, ILGenInfo *info, ILNode **parent) { JavaSemValue value; if(node) { value = ILNode_JSemAnalysis(node, info, parent); if(!JavaSemIsVoid(value) && !JavaSemIsValue(value)) { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "invalid statement"); } } } /* * Statement context flags. */ #define JAVA_STMT_LOOP 1 #define JAVA_STMT_SWITCH 2 #define JAVA_STMT_TRY 4 #define JAVA_STMT_CATCH 8 #define JAVA_STMT_FINALLY 16 /* * Push a statement context. */ static void PushStmtContext(ILGenInfo *info, int context) { if(info->contextStackSize >= info->contextStackMax) { /* Increase the size of the context stack */ int *newstack; newstack = (int *)ILRealloc(info->contextStack, sizeof(int) * (info->contextStackMax + 4)); if(!newstack) { ILGenOutOfMemory(info); } info->contextStack = newstack; info->contextStackMax += 4; } info->contextStack[info->contextStackSize] = context; ++(info->contextStackSize); } /* * Pop the top-most statement context. */ static void PopStmtContext(ILGenInfo *info) { --(info->contextStackSize); } /* * Determine if we are within a specific context, but not * within a "stop" context. e.g. "break" can be used * within a loop, but not in the body of a "finally" * within the loop. "finally" is the "stop" context. */ static int StmtContextOK(ILGenInfo *info, int context, int stopContext) { long posn = info->contextStackSize; while(posn > 0) { --posn; if((info->contextStack[posn] & stopContext) != 0) { return 0; } if((info->contextStack[posn] & context) != 0) { return 1; } } if(!context) { /* We are checking a "return" statement */ return 1; } else { /* We are checking some other kind of statement */ return 0; } } %} /* * Perform semantic analysis for the empty statement. */ ILNode_JSemAnalysis(ILNode_Empty) { return JavaSemValueDefault; } /* * Perform semantic analysis for the compound statement. */ ILNode_JSemAnalysis(ILNode_Compound), ILNode_JSemAnalysis(ILNode_List) { ILNode_ListIter iter; ILNode *child; ILNode *replace; ILNode_ListIter_Init(&iter, node); while((child = ILNode_ListIter_Next(&iter)) != 0) { replace = child; StmtSem(child, info, &replace); if(replace != child) { *(iter.last) = replace; } } return JavaSemValueDefault; } /* * Perform semantic analysis for the "return" statement. */ ILNode_JSemAnalysis(ILNode_Return) { /* Emit an error if the return type is not "void" */ ILType *returnType = ILTypeGetReturn(ILMethod_Signature (((ILNode_MethodDeclaration *)(info->currentMethod))->methodInfo)); if(returnType != ILType_Void) { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "`return' with no value, in method returning non-void"); } if(!StmtContextOK(info, 0, JAVA_STMT_FINALLY)) { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "`return' used inside `finally' clause"); } return JavaSemValueDefault; } /* * Perform semantic analysis for the "return expression" statement. */ ILNode_JSemAnalysis(ILNode_ReturnExpr) { JavaSemValue value; ILType *returnType; /* Perform semantic analysis on the expression */ if(!JavaSemExpectValue(node->expr, info, &(node->expr), &value)) { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "invalid return value"); return JavaSemValueDefault; } /* Determine if the value is compatible with the return type */ returnType = ILTypeGetReturn(ILMethod_Signature (((ILNode_MethodDeclaration *)(info->currentMethod))->methodInfo)); if(returnType == ILType_Void) { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "`return' with a value, in method returning void"); } else { /* Coerce to the return type */ if(!ILCoerce(info, node->expr, &(node->expr), JavaSemGetType(value), returnType,1)) { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "incompatible types in return: " "no conversion from `%s' to `%s'", JavaTypeToName(JavaSemGetType(value)), JavaTypeToName(returnType)); } } /* Check that the return was not used inside a "finally" clause */ if(!StmtContextOK(info, 0, JAVA_STMT_FINALLY)) { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "`return' used inside `finally' clause"); } /* Done */ return JavaSemValueDefault; } /* * Perform semantic analysis for local variable declarations. */ ILNode_JSemAnalysis(ILNode_LocalVarDeclaration) { ILType *type; ILNode_ListIter iter; ILNode *nameNode; const char *name; ILScopeData *data; ILNode_MethodDeclaration *method; ILNode *errorNode; /* Locate the method that this local is declared within */ method = (ILNode_MethodDeclaration *)(info->currentMethod); /* Perform semantic analysis on the local variable type */ type = JavaSemType(node->type, info, &(node->type)); /* Scan through the variable names and declare them in the current scope */ ILNode_ListIter_Init(&iter, node->varNames); while((nameNode = ILNode_ListIter_Next(&iter)) != 0) { name = ILQualIdentName(nameNode, 0); data = ILScopeLookup(info->currentScope, name, 0); if(data) { /* The name is already declared in this scope */ CCErrorOnLine(yygetfilename(nameNode), yygetlinenum(nameNode), "`%s' is already declared in this scope", name); errorNode = ILScopeDataGetNode(data); if(errorNode) { CCErrorOnLine(yygetfilename(errorNode), yygetlinenum(errorNode), "previous declaration here"); } } else { /* Add the type to the local variable signature for this method */ if(!(method->localVarSig)) { method->localVarSig = ILTypeCreateLocalList(info->context); if(!(method->localVarSig)) { CCOutOfMemory(); } } if(!ILTypeAddLocal(info->context, method->localVarSig, type)) { CCOutOfMemory(); } /* Create a local variable entry in the current scope */ ILScopeDeclareLocal(info->currentScope, name, ILTypeNumLocals(method->localVarSig) - 1, nameNode); } } /* Return the default value to the caller */ return JavaSemValueDefault; } /* * Perform semantic analysis for the "if" statement. * * Note: we assume that the condition has been wrapped in * a "ToBool" node to take care of the semantic analysis * for the boolean cast. */ ILNode_JSemAnalysis(ILNode_If) { ILNode_JSemAnalysis(node->expr, info, &(node->expr)); StmtSem(node->thenClause, info, &(node->thenClause)); StmtSem(node->elseClause, info, &(node->elseClause)); return JavaSemValueDefault; } /* * Perform semantic analysis for the "while" statement. * * Note: we assume that the condition has been wrapped in * a "ToBool" node to take care of the semantic analysis * for the boolean cast. */ ILNode_JSemAnalysis(ILNode_While) { ILNode_JSemAnalysis(node->cond, info, &(node->cond)); PushStmtContext(info, JAVA_STMT_LOOP); StmtSem(node->stmt, info, &(node->stmt)); PopStmtContext(info); return JavaSemValueDefault; } /* * Perform semantic analysis for the "do" statement. * * Note: we assume that the condition has been wrapped in * a "ToBool" node to take care of the semantic analysis * for the boolean cast. */ ILNode_JSemAnalysis(ILNode_Do) { PushStmtContext(info, JAVA_STMT_LOOP); StmtSem(node->stmt, info, &(node->stmt)); PopStmtContext(info); ILNode_JSemAnalysis(node->cond, info, &(node->cond)); return JavaSemValueDefault; } /* * Perform semantic analysis for the "for" statement. * * Note: we assume that the condition has been wrapped in * a "ToBool" node to take care of the semantic analysis * for the boolean cast. */ ILNode_JSemAnalysis(ILNode_For) { StmtSem(node->init, info, &(node->init)); ILNode_JSemAnalysis(node->cond, info, &(node->cond)); StmtSem(node->incr, info, &(node->incr)); PushStmtContext(info, JAVA_STMT_LOOP); StmtSem(node->stmt, info, &(node->stmt)); PopStmtContext(info); return JavaSemValueDefault; } /* * Perform semantic analysis for the "try" statement. */ ILNode_JSemAnalysis(ILNode_Try) { /* Enter a "try" context */ PushStmtContext(info, JAVA_STMT_TRY); /* Perform semantic analysis on the "try" body */ StmtSem(node->stmt, info, &(node->stmt)); /* Perform semantic analysis on the "catch" clauses */ StmtSem(node->catchClauses, info, &(node->catchClauses)); /* Perform semantic analysis on the "finally" clause */ StmtSem(node->finallyClause, info, &(node->finallyClause)); /* Exit from the "try" context */ PopStmtContext(info); /* Done */ return JavaSemValueDefault; } /* * Perform semantic analysis for the "catch clause" statement. */ ILNode_JSemAnalysis(ILNode_CatchClause) { ILType *exceptionType = ILFindSystemType(info, "Exception"); ILType *thrownType; ILScope *newScope; ILNode_MethodDeclaration *method = (ILNode_MethodDeclaration *)(info->currentMethod); /* Enter a "catch" context */ PushStmtContext(info, JAVA_STMT_CATCH); /* Wrap the catch body in a new scope */ newScope = ILScopeCreate(info, info->currentScope); node->stmt = ILNode_NewScope_create(node->stmt); ((ILNode_NewScope *)(node->stmt))->scope = newScope; /* Validate the thrown value type */ if(node->type) { thrownType = JavaSemType(node->type, info, &(node->type)); if(!ILCanCoerce(info, thrownType, exceptionType,0)) { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "`%s' does not inherit from `%s'", JavaTypeToName(thrownType), JavaTypeToName(exceptionType)); thrownType = exceptionType; } } else { thrownType = exceptionType; } node->classInfo = ILTypeToClass(info, thrownType); /* Declare a new local variable for the catch value */ if(node->name) { /* Add the type to the local variable signature for this method */ if(!(method->localVarSig)) { method->localVarSig = ILTypeCreateLocalList(info->context); if(!(method->localVarSig)) { CCOutOfMemory(); } } if(!ILTypeAddLocal(info->context, method->localVarSig, thrownType)) { CCOutOfMemory(); } /* Create a local variable entry in the current scope */ ILScopeDeclareLocal(newScope, node->name, ILTypeNumLocals(method->localVarSig) - 1, node->nameNode); /* Record the local variable index for the code generator */ node->varIndex = ILTypeNumLocals(method->localVarSig) - 1; } /* Perform semantic analysis on the catch body */ StmtSem(node->stmt, info, &(node->stmt)); /* Exit from the "catch" context */ PopStmtContext(info); /* Done */ return JavaSemValueDefault; } /* * Perform semantic analysis for the "finally clause" statement. */ ILNode_JSemAnalysis(ILNode_FinallyClause) { /* Enter a "finally" context */ PushStmtContext(info, JAVA_STMT_FINALLY); /* Perform semantic analysis on the finally body */ StmtSem(node->stmt, info, &(node->stmt)); /* Exit from the "finally" context */ PopStmtContext(info); /* Done */ return JavaSemValueDefault; } /* * Perform semantic analysis for the "throw expression" statement. */ ILNode_JSemAnalysis(ILNode_ThrowExpr) { JavaSemValue value; ILType *exceptionClass; /* Perform semantic analysis on the value */ if(!JavaSemExpectValue(node->expr, info, &(node->expr), &value)) { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "invalid throw expression"); JavaSemSetRValue(value, ILType_Null); } /* Attempt to coerce the value to "System.Exception" */ exceptionClass = ILFindSystemType(info, "Exception"); if(!ILCoerce(info, node->expr, &(node->expr), JavaSemGetType(value), exceptionClass,1)) { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "no conversion from `%s' to `%s'", JavaTypeToName(JavaSemGetType(value)), JavaTypeToName(exceptionClass)); } /* Done */ return JavaSemValueDefault; } ILNode_JSemAnalysis(ILNode_Break) { if(!StmtContextOK(info, JAVA_STMT_LOOP | JAVA_STMT_SWITCH, JAVA_STMT_FINALLY)) { if(StmtContextOK(info, JAVA_STMT_FINALLY, 0)) { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "`break' used inside `finally' clause"); } else { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "`break' used outside loop or switch"); } } return JavaSemValueDefault; } %{ /* * Count the number of case labels in the body of a "switch" statement, * and find the default case. */ static unsigned long CountLabelsAndFindDefault(ILNode *sections, ILNode **defCase) { ILNode_ListIter iter1; ILNode_ListIter iter2; ILNode *section; ILNode *caseNode; unsigned long count; ILNode *prevDefLabel; /* Initialize the return information */ count = 0; *defCase = 0; prevDefLabel = 0; /* Iterate through the sections */ ILNode_ListIter_Init(&iter1, sections); while((section = ILNode_ListIter_Next(&iter1)) != 0) { /* Iterate through the case list for each section */ ILNode_ListIter_Init (&iter2, ((ILNode_SwitchSection *)section)->caseList); while((caseNode = ILNode_ListIter_Next(&iter2)) != 0) { if(yyisa(caseNode, ILNode_DefaultLabel)) { /* This is the switch's default label */ if(prevDefLabel) { CCErrorOnLine(yygetfilename(caseNode), yygetlinenum(caseNode), "multiple `default' labels in `switch' statement"); CCErrorOnLine(yygetfilename(prevDefLabel), yygetlinenum(prevDefLabel), "previous `default' label here"); } else { *defCase = section; prevDefLabel = caseNode; } } else { /* This is a particular case label */ ++count; } } } /* Return the count to the caller */ return count; } /* * Compare two case label values within "ILSwitchValue" objects. */ static int CompareCaseLabels(const void *e1, const void *e2) { const ILEvalValue *value1 = &(((const ILSwitchValue *)e1)->value); const ILEvalValue *value2 = &(((const ILSwitchValue *)e2)->value); switch(value1->valueType) { case ILMachineType_Int8: case ILMachineType_UInt8: case ILMachineType_Int16: case ILMachineType_UInt16: case ILMachineType_Char: case ILMachineType_Int32: { if(value1->un.i4Value < value2->un.i4Value) { return -1; } else if(value1->un.i4Value > value2->un.i4Value) { return 1; } } break; case ILMachineType_UInt32: { if(((ILUInt32)(value1->un.i4Value)) < ((ILUInt32)(value2->un.i4Value))) { return -1; } else if(((ILUInt32)(value1->un.i4Value)) > ((ILUInt32)(value2->un.i4Value))) { return 1; } } break; case ILMachineType_Int64: { if(value1->un.i8Value < value2->un.i8Value) { return -1; } else if(value1->un.i8Value > value2->un.i8Value) { return 1; } } break; case ILMachineType_UInt64: { if(((ILUInt64)(value1->un.i8Value)) < ((ILUInt64)(value2->un.i8Value))) { return -1; } else if(((ILUInt64)(value1->un.i8Value)) > ((ILUInt64)(value2->un.i8Value))) { return 1; } } break; case ILMachineType_String: { const char *str1 = value1->un.strValue.str; const char *str2 = value2->un.strValue.str; int len1 = value1->un.strValue.len; int len2 = value2->un.strValue.len; unsigned char ch1; unsigned char ch2; if(!str1) { /* The first value is "null" */ return (str2 ? -1 : 0); } else if(!str2) { /* The second value is "null" */ return 1; } while(len1 > 0 && len2 > 0) { ch1 = (unsigned char)(*str1++); ch2 = (unsigned char)(*str2++); if(ch1 < ch2) { return -1; } else if(ch1 > ch2) { return 1; } --len1; --len2; } if(len1 > 0) { return 1; } else if(len2 > 0) { return -1; } } break; default: break; } return 0; } static ILNode* GetFallThroughNode(ILGenInfo *info, ILNode *nextSection) { if(nextSection && yyisa(nextSection,ILNode_SwitchSection)) { ILNode_ListIter iter; ILNode *nextLabel; ILNode_ListIter_Init(&iter, (((ILNode_SwitchSection*)nextSection)->caseList)); nextLabel = ILNode_ListIter_Next(&iter); if(yyisa(nextLabel,ILNode_CaseLabel)) { return ILNode_GotoCase_create(((ILNode_CaseLabel*)nextLabel)->expr); } else if(yyisa(nextLabel,ILNode_DefaultLabel)) { return ILNode_GotoDefault_create(); } } return ILNode_Break_create(); } /* * Collect up all of the case labels for a "switch" statement, * and sort them into ascending order. This also performs * semantic analysis on each of the sections. Returns the new * size of the case label list, after errors have been removed. */ static unsigned long CollectCaseLabels(ILGenInfo *info, ILNode *sections, ILType *switchType, ILSwitchValue *values) { ILNode_ListIter iter1; ILNode_ListIter iter2; ILNode *section; ILNode *caseNode; ILNode_CaseLabel *caseLabel; unsigned long size; unsigned long posn; JavaSemValue value; ILEvalValue evalValue; ILMachineType switchMachineType; /* Initialize */ size = 0; switchMachineType = ILTypeToMachineType(switchType); /* Iterate through the sections */ ILNode_ListIter_Init(&iter1, sections); while((section = ILNode_ListIter_Next(&iter1)) != 0) { /* Iterate through the case list for each section */ ILNode_ListIter_Init (&iter2, ((ILNode_SwitchSection *)section)->caseList); while((caseNode = ILNode_ListIter_Next(&iter2)) != 0) { if(yyisa(caseNode, ILNode_CaseLabel)) { /* Perform semantic analysis on the case expression */ caseLabel = (ILNode_CaseLabel *)caseNode; if(!JavaSemExpectValue(caseLabel->expr, info, &(caseLabel->expr), &value)) { CCErrorOnLine(yygetfilename(caseLabel->expr), yygetlinenum(caseLabel->expr), "invalid case label expression"); continue; } /* Coerce the case expression to the governing type */ if(!ILCoerce(info, caseLabel->expr, &(caseLabel->expr), JavaSemGetType(value), switchType,1)) { CCErrorOnLine(yygetfilename(caseLabel->expr), yygetlinenum(caseLabel->expr), "no conversion from `%s' to `%s'", JavaTypeToName(JavaSemGetType(value)), JavaTypeToName(switchType)); continue; } /* Evaluate the constant value for the case expression */ if(!ILNode_EvalConst(caseLabel->expr, info, &evalValue) || !ILGenCastConst(info, &evalValue, evalValue.valueType, switchMachineType)) { CCErrorOnLine(yygetfilename(caseLabel->expr), yygetlinenum(caseLabel->expr), "case expression is not constant"); continue; } /* Record the case node information for later */ values[size].value = evalValue; values[size].caseLabel = caseNode; values[size].section = section; ++size; } } /* The body must end in some kind of return or jump statement */ if(!ILNodeEndsInFlowChange(((ILNode_SwitchSection *)section)->stmt ,info)) { ILNode_ListIter lookAhead=iter1; ILNode *next=ILNode_ListIter_Next(&lookAhead); ILNode *jumpNext=GetFallThroughNode(info,next); /* We do the actual Semanalysis in the second phase */ ((ILNode_SwitchSection*)section)->stmt = ILNode_Compound_CreateFrom( ((ILNode_SwitchSection*)section)->stmt, jumpNext); } } /* split into 2 passes to facilitate goto case * So SemAnalysis is performed after all the CaseLabels are * collected. */ /* Iterate through the sections again */ ILNode_ListIter_Init(&iter1, sections); while((section = ILNode_ListIter_Next(&iter1)) != 0) { ((ILNode_SwitchSection*)section)->visited=ILVisitMode_Processing; /* Perform semantic analysis on the section's body */ ILNode_JSemAnalysis(((ILNode_SwitchSection *)section)->stmt, info, &(((ILNode_SwitchSection *)section)->stmt)); ((ILNode_SwitchSection*)section)->visited=ILVisitMode_Done; } /* Sort the case label list into ascending order */ if(size > 1) { #ifdef HAVE_QSORT qsort(values, (size_t)size, sizeof(ILSwitchValue), CompareCaseLabels); #else unsigned long i; unsigned long j; ILSwitchValue temp; for(i = 0; i < size - 1; ++i) { for(j = i + 1; j < size; ++j) { if(CompareCaseLabels(&(values[i]), &(values[j])) > 0) { temp = values[i]; values[i] = values[j]; values[j] = temp; } } } #endif } /* Check for duplicates in the case label list */ if(size > 1) { for(posn = 0; posn < size - 1; ++posn) { if(CompareCaseLabels(&(values[posn]), &(values[posn + 1])) == 0) { CCErrorOnLine(yygetfilename(values[posn + 1].caseLabel), yygetlinenum(values[posn + 1].caseLabel), "duplicate case expression"); CCErrorOnLine(yygetfilename(values[posn].caseLabel), yygetlinenum(values[posn].caseLabel), "previous definition here"); } } } /* Return the final table size to the caller */ return size; } /* * Determine the type of table to be used for a set of switch cases. */ static ILSwitchTableType SwitchTableType(ILType *switchType, ILSwitchValue *values, unsigned long numValues) { ILMachineType type; unsigned long range; /* Use simple "if" statements for 4 or less cases */ if(numValues <= 4) { return ILSwitchTableType_If; } /* If the switch type can fit in a 32-bit integer, and the cases fill up at least 80% of the range, then use an indexed table for the switch */ type = ILTypeToMachineType(switchType); switch(type) { case ILMachineType_Int8: case ILMachineType_UInt8: case ILMachineType_Int16: case ILMachineType_UInt16: case ILMachineType_Char: case ILMachineType_Int32: { range = (unsigned long)(values[numValues - 1].value.un.i4Value - values[0].value.un.i4Value); if(((range * 80) / 100) <= numValues) { return ILSwitchTableType_Indexed; } else { return ILSwitchTableType_Lookup; } } /* Not reached */ case ILMachineType_UInt32: { range = (unsigned long) (((ILUInt32)(values[numValues - 1].value.un.i4Value)) - ((ILUInt32)(values[0].value.un.i4Value))); if(((range * 80) / 100) <= numValues) { return ILSwitchTableType_Indexed; } else { return ILSwitchTableType_Lookup; } } /* Not reached */ default: break; } /* If we get here, then we need to use a binary tree of if statements */ return ILSwitchTableType_BinaryIf; } %} /* * Perform semantic analysis for the "switch" statement. */ ILNode_JSemAnalysis(ILNode_Switch) { static ILType * const governingTypes[] = { ILType_Int8, ILType_UInt8, ILType_Int16, ILType_UInt16, ILType_Int32, ILType_UInt32, ILType_Int64, ILType_UInt64, ILType_Char }; #define numGoverningTypes (sizeof(governingTypes) / sizeof(ILType *)) JavaSemValue value; ILType *actualType; ILType *switchType; int posn; unsigned long size; ILNode *savedSwitch; /* Perform semantic analysis on the expression */ if(!JavaSemExpectValue(node->expr, info, &(node->expr), &value)) { CCErrorOnLine(yygetfilename(node->expr), yygetlinenum(node->expr), "invalid expression for `switch' statement"); actualType = ILType_Int32; } else { actualType = JavaSemGetType(value); } /* Determine the governing type for the switch */ if(ILTypeIsEnum(actualType)) { switchType = actualType; } else { switchType = actualType; for(posn = 0; posn < numGoverningTypes; ++posn) { if(ILCanCoerceKind(info, actualType, governingTypes[posn], IL_CONVERT_USER_DEFINED,1)) { switchType = governingTypes[posn]; break; } } if(posn >= numGoverningTypes) { CCErrorOnLine(yygetfilename(node->expr), yygetlinenum(node->expr), "`%s' is invalid as the governing type for a `switch'", JavaTypeToName(actualType)); } } /* Cast the switch expression to the governing type */ ILCast(info, node->expr, &(node->expr), actualType, switchType,1); node->switchType=switchType; /* Enter the switch context */ PushStmtContext(info, JAVA_STMT_SWITCH); savedSwitch=info->currentSwitch; info->currentSwitch=(ILNode*)node; /* Count the number of case labels and find the default case */ node->numSwitchValues = CountLabelsAndFindDefault (node->sections, &(node->defaultSection)); /* Allocate space for the switch value table */ size = node->numSwitchValues * sizeof(ILSwitchValue); if(size > 1024) { /* Warning! This will leak memory! */ node->switchValues = (ILSwitchValue *)ILMalloc(size); } else { /* Allocate from the node pool, which will be cleaned up for us */ node->switchValues = (ILSwitchValue *)yynodealloc((unsigned int)size); } if(!(node->switchValues)) { CCOutOfMemory(); } /* Perform semantic analysis on the switch body and compute the values */ node->numSwitchValues = CollectCaseLabels(info, node->sections, switchType, node->switchValues); /* Determine the type of switch table to generate */ node->tableType = SwitchTableType(switchType, node->switchValues, node->numSwitchValues); info->currentSwitch=savedSwitch; /* Leave the switch context */ PopStmtContext(info); /* Done */ return JavaSemValueDefault; } /* * Perform semantic analysis for the switch helper nodes. */ ILNode_JSemAnalysis(ILNode_SwitchSectList), ILNode_JSemAnalysis(ILNode_SwitchSection), ILNode_JSemAnalysis(ILNode_CaseList), ILNode_JSemAnalysis(ILNode_CaseLabel), ILNode_JSemAnalysis(ILNode_DefaultLabel) { /* Nothing to do here: the real work is done in "ILNode_Switch" */ return JavaSemValueDefault; } /* * Perform semantic analysis for the "goto case" statement. */ ILNode_JSemAnalysis(ILNode_GotoCase) { JavaSemValue value; ILEvalValue evalValue; ILNode_Switch *switchExpr; ILMachineType switchMachineType; ILSwitchValue switchValue; unsigned long posn; if(!StmtContextOK(info, JAVA_STMT_SWITCH, JAVA_STMT_FINALLY)) { if(StmtContextOK(info, JAVA_STMT_FINALLY, 0)) { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "`goto' used inside `finally' clause"); return JavaSemValueDefault; } else { CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "`goto' used outside switch"); return JavaSemValueDefault; } } /* if node->currentSwitch is NULL, then somebody messed up the PushStmtContext . It is a "bug detection" bug */ /* Initialize */ switchExpr=(ILNode_Switch*)(info->currentSwitch); switchMachineType = ILTypeToMachineType(switchExpr->switchType); if(!JavaSemExpectValue(node->expr, info, &(node->expr), &value)) { CCErrorOnLine(yygetfilename(node->expr), yygetlinenum(node->expr), "invalid case label expression"); return JavaSemValueDefault; } /* Coerce the case expression to the governing type */ if(!ILCoerce(info, node->expr, &(node->expr), JavaSemGetType(value), switchExpr->switchType,1)) { CCErrorOnLine(yygetfilename(node->expr), yygetlinenum(node->expr), "no conversion from `%s' to `%s'", JavaTypeToName(JavaSemGetType(value)), JavaTypeToName(switchExpr->switchType)); return JavaSemValueDefault; } /* Evaluate the constant value for the case expression */ if(!ILNode_EvalConst(node->expr, info, &evalValue) || !ILGenCastConst(info, &evalValue, evalValue.valueType, switchMachineType)) { CCErrorOnLine(yygetfilename(node->expr), yygetlinenum(node->expr), "case expression is not constant"); return JavaSemValueDefault; } switchValue.value=evalValue; for(posn = 0; posn < switchExpr->numSwitchValues ; ++posn) { if(CompareCaseLabels(&(switchExpr->switchValues[posn]), &(switchValue)) == 0) { node->switchSection=(ILNode_SwitchSection*) (switchExpr->switchValues[posn].section); if(node->switchSection->visited==ILVisitMode_Processing) { CCWarningOnLine(yygetfilename(node), yygetlinenum(node), "circularity in 'goto' detected"); } return JavaSemValueDefault; } } /* if we get here , the case entry was not locatable */ CCErrorOnLine(yygetfilename(node), yygetlinenum(node), "could not find a matching case for 'goto'"); return JavaSemValueDefault; }