// Copyright (c) 1995  David Engberg  All rights reserved
// $Id: Statement.C,v 1.15 1997/08/21 16:30:11 geppetto Exp $
#pragma implementation
#include "Statement.h"
#include "Expression.h"
#include "VariableDeclaration.h"
#include "JavaAccessFlags.h"
#include "parser_decls.h"
#include "CompileError.h"
#include "JavaClassFile.h"
#include "JavaMethodInfo.h"
#include "JavaCodeAttribute.h"
#include "Compiler.h"
#include "CompileContext.h"
#include "CodeSequence.h"
#include <climits>

//
//  Method name : CStatement
//  Description : Default constructor.  Read the comment for CExpression() for
//    my lame attempt at justifying accessing the 'yylineno' variable like
//    this.
//
CStatement::CStatement()
  : fLineNumber(yylineno)
{
}

//
//  Method name : ~CStatement
//  Description : Destructor
//
CStatement::~CStatement()
{
}

//
//  Method name : MakeError
//  Description : This provides a convenient hook for children of CStatement
//    to create compiler errors from just a string.
//
CCompileError*
CStatement::MakeError(const unicode_string& message)
{
  return new CCompileError(message, fLineNumber);
}

//
//  Method name : MakeError
//  Description : This provides a convenient hook for children of CStatement
//    to create compiler errors from just a string.
//
CCompileError*
CStatement::MakeError(const string& message)
{
  return new CCompileError(::StringToUnicode(message), fLineNumber);
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CStatement::GenerateCode(CCodeSequence& code,
			 CCompileContext& context, unsigned short& stackUsed)
{
  CCompileError* error = 0;
  if (context.IsReachable()) {
    error = HandleGenerateCode(code, context, stackUsed);
  } else {
    error = MakeError("Code cannot be reached.");
  }
  return error;
}

//=========================== CExpressionStatement ============================

//
//  Method name : CExpressionStatement
//  Description : Constructs a CExpressionStatement out of an adopted
//    expression.
//
CExpressionStatement::CExpressionStatement(CExpression* adoptExpression)
  : fExpression(adoptExpression)
{
}

//
//  Method name : ~CExpressionStatement
//  Description : Destructor.
//
CExpressionStatement::~CExpressionStatement()
{
  delete fExpression;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CExpressionStatement::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  assert(fExpression != 0);
  stackUsed = 0;
  CJavaTypeSignature type;
  CCompileError* error =
    CExpression::EvaluateType(fExpression, context, type);
  if (error == 0) {
    error =
      fExpression->GenerateCode(code, context, stackUsed, true);
  }
  return error;
}

//============================ CCompoundStatement =============================

//
//  Method name : CCompoundStatement
//  Description : Constructs a CCompoundStatement from an adopted list of
//    sub-statements.
//
CCompoundStatement::CCompoundStatement(StatementList* adoptStatements)
  : fChildren(adoptStatements)
{
  if (fChildren == 0) {
    fChildren = new StatementList;
  }
}

//
//  Method name : ~CCompoundStatement
//  Description : Destructor.
//
CCompoundStatement::~CCompoundStatement()
{
  for (StatementList::iterator i = fChildren->begin();
       i != fChildren->end(); ++i) {
    delete *i;
  }
  delete fChildren;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CCompoundStatement::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  assert(fChildren != 0);
  CCompileError* error = 0;
  stackUsed = 0;
  for (StatementList::iterator i = fChildren->begin();
       error == 0 && i != fChildren->end(); ++i) {
    unsigned short childStack;
    assert(*i != 0);
    error = (*i)->GenerateCode(code, context, childStack);
    if (childStack > stackUsed) {
      stackUsed = childStack;
    }
  }
  return error;
}

//=========================== CDeclarationStatement ===========================

//
//  Method name : CDeclarationStatement
//  Description : Constructs a CDeclarationStatement out of a list of single
//    variable declarations.
//
CDeclarationStatement::CDeclarationStatement(
		       deque<CVariableDeclaration*>* adoptDeclarations,
		     CJavaAccessFlags* modifiers, bool deprecated)
   : fDeclarations(adoptDeclarations),
     fModifiers(modifiers),
     fDeprecated(deprecated)
{
  if (fDeclarations == 0) {
    fDeclarations = new deque<CVariableDeclaration*>;
  }
  if (fModifiers == 0) {
    fModifiers = new CJavaAccessFlags;
  }
}

//
//  Method name : ~CDeclarationStatement
//  Description : Destructor.
//
CDeclarationStatement::~CDeclarationStatement()
{
  for (deque<CVariableDeclaration*>::iterator i = fDeclarations->begin();
       i != fDeclarations->end(); ++i) {
    delete *i;
  }
  delete fDeclarations;
  delete fModifiers;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CDeclarationStatement::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  assert(fDeclarations != 0 && fModifiers != 0);
  CCompileError* error = 0;
  stackUsed = 0;
  CJavaTypeSignature type;
  for (deque<CVariableDeclaration*>::iterator i = fDeclarations->begin();
       error == 0 && i != fDeclarations->end(); ++i) {
    unsigned short stack;
    error = (*i)->GenerateCode(code, context, *fModifiers, stack);
    stackUsed = ::max(stackUsed, stack);
  }
  return error;
}

//=============================== CIfStatement ================================

//
//  Method name : CIfStatement
//  Description : Constructs a CIfStatement from its parts
//
CIfStatement::CIfStatement(CExpression* condition, CStatement* thenClause,
			   CStatement* elseClause)
   : fCondition(condition),
     fThenClause(thenClause),
     fElseClause(elseClause)
{
}

//
//  Method name : ~CIfStatement
//  Description : Destructor.
//
CIfStatement::~CIfStatement()
{
  delete fCondition;
  delete fThenClause;
  delete fElseClause;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CIfStatement::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  assert(fCondition != 0);
  CJavaTypeSignature type;
  CCompileError* error =
    CExpression::EvaluateType(fCondition, context, type);
  if (error == 0 && type != CJavaTypeSignature::kBoolean) {
    error = MakeError("Non-boolean value used as 'if' conditional.");
  }
  COrdinalLiteral* literal = DYNAMIC_CAST(COrdinalLiteral, fCondition);
  if (error == 0 && literal != 0) {
    stackUsed = 0;
    if (literal->GetBoolean()) {
      if (fThenClause != 0) {
	error = fThenClause->GenerateCode(code, context, stackUsed);
      }
    } else if (fElseClause != 0) {
      error = fElseClause->GenerateCode(code, context, stackUsed);
    }
  } else if (error == 0) {
    error =
      fCondition->GenerateCode(code, context, stackUsed, false);
    if (error == 0) {
      unsigned long branchInstruction = code.size();
      unsigned short stack = 0;
      CCompileContext elseContext = context;
      if (fThenClause == 0 && fElseClause == 0) {
	code.Append(CJavaCodeAttribute::pop, fLineNumber);
      } else if (fThenClause != 0 && fElseClause != 0) {
	// Have both a non-empty 'then' and 'else' part.
	code.Append(CJavaCodeAttribute::ifeq, fCondition->GetLineNumber());
	error = fThenClause->GenerateCode(code, context, stack);
	stackUsed = ::max(stack, stackUsed);
	unsigned long thenBranchInstruction = 0;
	if (context.IsReachable()) {
	  thenBranchInstruction = code.size();
	  code.Append(CJavaCodeAttribute::op_goto,
		      fThenClause->GetLineNumber());
	}
	code[branchInstruction].fArguments.u4 = code.CreateBlockLabel();
	if (error == 0) {
	  error = fElseClause->GenerateCode(code, elseContext, stack);
	  if (context.IsReachable()) {
	    code[thenBranchInstruction].fArguments.u4 =
	      code.CreateBlockLabel();
	  }
	}
      } else {
	CStatement* onlyClause = 0;
	if (fThenClause != 0) {  // only has a non-empty 'then' part
	  onlyClause = fThenClause;
	  code.Append(CJavaCodeAttribute::ifeq, fCondition->GetLineNumber());
	} else {  // only has a non-empty 'else' part
	  onlyClause = fElseClause;
	  code.Append(CJavaCodeAttribute::ifne, fCondition->GetLineNumber());
	}
	error = onlyClause->GenerateCode(code, context, stack);
	code[branchInstruction].fArguments.u4 = code.CreateBlockLabel();
      }
      stackUsed = ::max(stack, stackUsed);
      context.Merge(elseContext);
    }
  }
  return error;
}

//============================== CWhileStatement ==============================

//
//  Method name : CWhileStatement
//  Description : Constructs a CWhileStatement
//
CWhileStatement::CWhileStatement(CStatement* adoptLoop,
				 CExpression* adoptCondition)
   : fLoopStatement(adoptLoop),
     fCondition(adoptCondition)
{
}

//
//  Method name : ~CWhileStatement
//  Description : Destructor.
//
CWhileStatement::~CWhileStatement()
{
  delete fLoopStatement;
  delete fCondition;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CWhileStatement::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  CCompileError* error = 0;
  assert(fCondition != 0);
  CJavaTypeSignature type;
  error = CExpression::EvaluateType(fCondition, context, type);
  if (error == 0) {
    if (type != CJavaTypeSignature::kBoolean) {
      error = MakeError("Non-boolean value used as 'while' conditional.");
    } else {
      COrdinalLiteral* boolLiteral = DYNAMIC_CAST(COrdinalLiteral, fCondition);
      unsigned long conditionalBase = code.CreateBlockLabel();
      unsigned long branchInstruction;
      stackUsed = 0;
      bool reachableBody = true;
      if (boolLiteral != 0) {
	if (!boolLiteral->GetBoolean()) {
	  reachableBody = false;
	}
      } else {
	error = fCondition->GenerateCode(code, context, stackUsed);
	branchInstruction = code.size();
	code.Append(CJavaCodeAttribute::ifeq, fCondition->GetLineNumber());
      }
      if (error == 0 && fLoopStatement != 0 && reachableBody) {
	CCompileContext bodyContext = context;
	unsigned short bodyStack;
	bodyContext.GetCompiler().PushStatementContext(this);
	error = fLoopStatement->GenerateCode(code, bodyContext, bodyStack);
	stackUsed = ::max(stackUsed, bodyStack);
	bodyContext.GetCompiler().PopStatementContext();
      }
      if (error == 0 && reachableBody) {
	code.Append(CJavaCodeAttribute::op_goto, conditionalBase,
		    fCondition->GetLineNumber());
      }
      unsigned long endLabel = code.CreateBlockLabel();
      if (boolLiteral != 0) {
	if (boolLiteral->GetBoolean()) {
	  context.SetUnreachable();
	}
      } else {
	code[branchInstruction].fArguments.u4 = endLabel;
      }
      for (list<CNonlocalBranch>::iterator i = fNestedContinues.begin();
	   !(i == fNestedContinues.end()); ++i) {
	unsigned long address = (*i).GetAddress();
	code[address].fArguments.u4 = conditionalBase;
      }
      for (list<CNonlocalBranch>::iterator i = fNestedBreaks.begin();
	   !(i == fNestedBreaks.end()); ++i) {
	unsigned long address = (*i).GetAddress();
	code[address].fArguments.u4 = endLabel;
	context.Merge((*i).GetContext());
      }
    }
  }
  return error;
}

//
//  Method name : RegisterContinueBranch
//  Description : This method tells the loop construct that a continue
//    statement was found in its body and gives the loop the location of the
//    'goto' code in the instruction block.  The loop is then responsible for
//    adjusting the target of the 'goto' to jump to the right location.
//
void
CWhileStatement::RegisterContinueBranch(const CNonlocalBranch& instruction)
{
  fNestedContinues.push_front(instruction);
}

//
//  Method name : RegisterBreakBranch
//  Description : This method tells the loop construct that a break
//    statement was found in its body and gives the loop the location of the
//    'goto' code in the instruction block.  The loop is then responsible for
//    adjusting the target of the 'goto' to jump to the right location.
//
void
CWhileStatement::RegisterBreakBranch(const CNonlocalBranch& instruction)
{
  fNestedBreaks.push_front(instruction);
}

//============================== CDoStatement ==============================

//
//  Method name : CDoStatement
//  Description : Constructs a CDoStatement
//
CDoStatement::CDoStatement(CStatement* adoptLoop,
				 CExpression* adoptCondition)
   : fLoopStatement(adoptLoop),
     fCondition(adoptCondition)
{
}

//
//  Method name : ~CDoStatement
//  Description : Destructor.
//
CDoStatement::~CDoStatement()
{
  delete fLoopStatement;
  delete fCondition;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CDoStatement::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  CCompileError* error = 0;
  assert(fCondition != 0);
  unsigned long baseBodyInstruction = code.CreateBlockLabel();
  if (fLoopStatement != 0) {
    context.GetCompiler().PushStatementContext(this);
    error = fLoopStatement->GenerateCode(code, context, stackUsed);
    context.GetCompiler().PopStatementContext();
  }
  if (error == 0) {
    CJavaTypeSignature type;
    error = CExpression::EvaluateType(fCondition, context, type);
    if (error == 0 && type != CJavaTypeSignature::kBoolean) {
      error = MakeError("Non-boolean value used as 'do-while' conditional.");
    }
  }
  if (!fNestedContinues.empty()) {
    unsigned long beforeCondition = code.CreateBlockLabel();
    for (list<CNonlocalBranch>::iterator i = fNestedContinues.begin();
	 !(i == fNestedContinues.end()); ++i) {
      unsigned long address = (*i).GetAddress();
      code[address].fArguments.u4 = beforeCondition;
    }
  }
  if (error == 0) {
    unsigned short conditionStack;
    error = fCondition->GenerateCode(code, context, conditionStack);
    stackUsed = ::max(stackUsed, conditionStack);
    code.Append(CJavaCodeAttribute::ifne, baseBodyInstruction,
		fCondition->GetLineNumber());
  }
  if (!fNestedBreaks.empty()) {
    unsigned long afterLoop = code.CreateBlockLabel();
    for (list<CNonlocalBranch>::iterator i = fNestedBreaks.begin();
	 !(i == fNestedBreaks.end()); ++i) {
      context.Merge((*i).GetContext());
      unsigned long address = (*i).GetAddress();
      code[address].fArguments.u4 = afterLoop;
    }
  }
  return error;
}

//
//  Method name : RegisterContinueBranch
//  Description : This method tells the loop construct that a continue
//    statement was found in its body and gives the loop the location of the
//    'goto' code in the instruction block.  The loop is then responsible for
//    adjusting the target of the 'goto' to jump to the right location.
//
void
CDoStatement::RegisterContinueBranch(const CNonlocalBranch& instruction)
{
  fNestedContinues.push_front(instruction);
}

//
//  Method name : RegisterBreakBranch
//  Description : This method tells the loop construct that a break
//    statement was found in its body and gives the loop the location of the
//    'goto' code in the instruction block.  The loop is then responsible for
//    adjusting the target of the 'goto' to jump to the right location.
//
void
CDoStatement::RegisterBreakBranch(const CNonlocalBranch& instruction)
{
  fNestedBreaks.push_front(instruction);
}

//============================== CReturnStatement =============================

//
//  Method name : CReturnStatement
//  Description : Constructs a CReturnStatement that will return the given
//    value (if one is provided).
//
CReturnStatement::CReturnStatement(CExpression* returnValue)
   : fValue(returnValue)
{
}

//
//  Method name : ~CReturnStatement
//  Description : Destructor.
//
CReturnStatement::~CReturnStatement()
{
  delete fValue;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CReturnStatement::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  CCompileError* error = 0;
  CCompiler::FinallyHandlerStack::iterator finallyIterator =
    context.GetCompiler().GetHandlerBegin();
  CCompiler::FinallyHandlerStack::iterator finallyEnd =
    context.GetCompiler().GetHandlerEnd();
  for (; finallyIterator != finallyEnd; ++finallyIterator) {
    code.Append(CJavaCodeAttribute::jsr, *finallyIterator, fLineNumber);
  }
  CJavaTypeSignature returnType = context.GetMethod().GetSignature().GetType();
  if (fValue == 0) {
    if (returnType != CJavaTypeSignature::kVoid) {
      error = MakeError("Invalid empty return from non-void function.");
    } else {
      code.Append(CJavaCodeAttribute::op_return, fLineNumber);
    }
    stackUsed = 0;
  } else {
    CJavaTypeSignature type;
    error = CExpression::EvaluateType(fValue, context, type);
    if (error == 0) {
      if (!context.GetCompiler().SameType(returnType, type)) {
	error =
	  CCastExpression::ImplicitCastPointer(fValue, returnType, context);
      }
      if (error == 0) {
	error =
	  fValue->GenerateCode(code, context, stackUsed, false);
      }
      if (error == 0) {
	if (returnType.IsReference()) {
	  code.Append(CJavaCodeAttribute::areturn, fValue->GetLineNumber());
	} else {
	  switch (returnType.GetBaseType()) {
	  case CJavaTypeSignature::Double:
	    code.Append(CJavaCodeAttribute::dreturn, fValue->GetLineNumber());
	    break;
	  case CJavaTypeSignature::LongInteger:
	    code.Append(CJavaCodeAttribute::lreturn, fValue->GetLineNumber());
	    break;
	  case CJavaTypeSignature::Float:
	    code.Append(CJavaCodeAttribute::freturn, fValue->GetLineNumber());
	    break;
	  case CJavaTypeSignature::Integer:
	  case CJavaTypeSignature::Byte:
	  case CJavaTypeSignature::Character:
	  case CJavaTypeSignature::Short:
	  case CJavaTypeSignature::Boolean:
	    code.Append(CJavaCodeAttribute::ireturn, fValue->GetLineNumber());
	    break;
	  case CJavaTypeSignature::Void:
	    error =
	      MakeError("Invalid value returned from method declared 'void'");
	    break;
	  default:
	    assert(0);
	  }
	}
      }
    }
  }
  context.SetUnreachable();
  return error;
}

//============================== CBranchStatement =============================

//
//  Method name : CBranchStatement
//  Description : Constructs a CBranchStatement
//
CBranchStatement::CBranchStatement(CBranchStatement::Type type,
				   unicode_string* label)
   : fBranchType(type),
     fToLabel(label)
{
}

//
//  Method name : ~CBranchStatement
//  Description : Destructor.
//
CBranchStatement::~CBranchStatement()
{
  delete fToLabel;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CBranchStatement::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& compileContext, unsigned short& stackUsed)
{
  stackUsed = 0;
  CCompileError* error = 0;
  StatementList::iterator context =
    compileContext.GetCompiler().GetContextBegin();
  StatementList::iterator contextEnd =
    compileContext.GetCompiler().GetContextEnd();
  for (; error == 0 && context != contextEnd; ++context) {
    CTryStatement* tryStatement = DYNAMIC_CAST(CTryStatement, *context);
    if (tryStatement != 0) {
      tryStatement->EmitFinallyCall(code);
    }
    CSynchronized* synchronized = DYNAMIC_CAST(CSynchronized, *context);
    if (synchronized != 0) {
      synchronized->EmitMonitorExitCode(code);
    }
    CNonlocalBranch thisBranch(code.size(), compileContext);
    if (fToLabel != 0) {
      CLabelStatement* label = DYNAMIC_CAST(CLabelStatement, *context);
      if (label != 0 && label->GetLabel() != 0 &&
	  *label->GetLabel() == *fToLabel) {
	code.Append(CJavaCodeAttribute::op_goto, fLineNumber);
	if (fBranchType == kBreak) {
	  label->RegisterBreakBranch(thisBranch);
	  break;
	} else {
	  CStatement* labelChild = label->GetChildStatement();
	  assert(labelChild != 0);
	  CWhileStatement* whileStatement =
	    DYNAMIC_CAST(CWhileStatement, labelChild);
	  CDoStatement* doStatement =
	    DYNAMIC_CAST(CDoStatement, labelChild);
	  CForStatement* forStatement =
	    DYNAMIC_CAST(CForStatement, labelChild);
	  if (whileStatement != 0) {
	    whileStatement->RegisterContinueBranch(thisBranch);
	    break;
	  } else if (doStatement != 0) {
	    doStatement->RegisterContinueBranch(thisBranch);
	    break;
	  } else if (forStatement != 0) {
	    forStatement->RegisterContinueBranch(thisBranch);
	    break;
	  } else {
	    error = MakeError("Invalid 'continue' to non-loop statement.");
	  }
	}
      }
    } else {
      if (fBranchType == kBreak) {
	CSwitch* switchStatement = DYNAMIC_CAST(CSwitch, *context);
	if (switchStatement != 0) {
	  switchStatement->RegisterBreakBranch(thisBranch);
	  code.Append(CJavaCodeAttribute::op_goto, fLineNumber);
	  break;
	}
      }
      CWhileStatement* whileStatement =
	DYNAMIC_CAST(CWhileStatement, *context);
      if (whileStatement != 0) {
	if (fBranchType == kBreak) {
	  whileStatement->RegisterBreakBranch(thisBranch);
	} else {
	  whileStatement->RegisterContinueBranch(thisBranch);
	}
	code.Append(CJavaCodeAttribute::op_goto, fLineNumber);
	break;
      }
      CDoStatement* doStatement =
	DYNAMIC_CAST(CDoStatement, *context);
      if (doStatement != 0) {
	if (fBranchType == kBreak) {
	  doStatement->RegisterBreakBranch(thisBranch);
	} else {
	  doStatement->RegisterContinueBranch(thisBranch);
	}
	code.Append(CJavaCodeAttribute::op_goto, fLineNumber);
	break;
      }
      CForStatement* forStatement = DYNAMIC_CAST(CForStatement, *context);
      if (forStatement != 0) {
	if (fBranchType == kBreak) {
	  forStatement->RegisterBreakBranch(thisBranch);
	} else {
	  forStatement->RegisterContinueBranch(thisBranch);
	}
	code.Append(CJavaCodeAttribute::op_goto, fLineNumber);
	break;
      }
    }
  }
  if (error == 0 && context == contextEnd) {
    if (fBranchType == kBreak) {
      error = MakeError("Invalid 'break' statement without matching loop.");
    } else {
      error = MakeError("Invalid 'continue' statement outside loop.");
    }
  }
  compileContext.SetUnreachable();
  return error;
}

//=============================== CForStatement ===============================

//
//  Method name : CForStatement
//  Description : Constructs a CForStatement
//
CForStatement::CForStatement(CStatement* initializer, CExpression* conditional,
			     ExpressionList* incrementor, CStatement* body)
   : fInitializer(initializer),
     fConditional(conditional),
     fIncrementor(incrementor),
     fBody(body)
{
}

//
//  Method name : ~CForStatement
//  Description : Destructor.
//
CForStatement::~CForStatement()
{
  delete fInitializer;
  delete fConditional;
  delete fBody;
  if (fIncrementor != 0) {
    for (ExpressionList::iterator i = fIncrementor->begin();
	 i != fIncrementor->end(); ++i) {
      delete *i;
    }
    delete fIncrementor;
  }
}


//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CForStatement::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  CCompileError* error = 0;
  stackUsed = 0;
  if (fInitializer != 0) {
    error = fInitializer->GenerateCode(code, context, stackUsed);
  }
  if (error == 0 && fConditional != 0) {
    CJavaTypeSignature type;
    error = CExpression::EvaluateType(fConditional, context, type);
    if (error == 0 && type != CJavaTypeSignature::kBoolean) {
      error = MakeError("Non-boolean value used as 'for' conditional.");
    }
  }
  unsigned long conditionalBase = code.CreateBlockLabel();
  unsigned long branchInstruction;
  if (error == 0 && fConditional != 0) {
    unsigned short conditionStack;
    error =
      fConditional->GenerateCode(code, context, conditionStack);
    stackUsed = ::max(stackUsed, conditionStack);
    branchInstruction = code.size();
    code.Append(CJavaCodeAttribute::ifeq, fConditional->GetLineNumber());
  }
  CCompileContext bodyContext = context;
  if (error == 0 && fBody != 0) {
    unsigned short bodyStack;
    bodyContext.GetCompiler().PushStatementContext(this);
    error = fBody->GenerateCode(code, bodyContext, bodyStack);
    stackUsed = ::max(stackUsed, bodyStack);
    bodyContext.GetCompiler().PopStatementContext();
  }
  if (!fNestedContinues.empty()) {
    unsigned long beforeIncrementor = code.CreateBlockLabel();
    for (list<CNonlocalBranch>::iterator i = fNestedContinues.begin();
	 !(i == fNestedContinues.end()); ++i) {
      unsigned long address = (*i).GetAddress();
      code[address].fArguments.u4 = beforeIncrementor;
      bodyContext.Merge((*i).GetContext());
    }
  }
  if (error == 0 && fIncrementor != 0) {
    for (ExpressionList::iterator i = fIncrementor->begin();
	 error == 0 && i != fIncrementor->end(); ++i) {
      CJavaTypeSignature type;
      error = CExpression::EvaluateType(*i, bodyContext, type);
      if (error == 0) {
	unsigned short incrementorStack;
	error = (*i)->GenerateCode(code, bodyContext, incrementorStack, true);
	stackUsed = ::max(stackUsed, incrementorStack);
      }
    }
  }
  code.Append(CJavaCodeAttribute::op_goto, conditionalBase, fLineNumber);
  unsigned long endLabel = code.CreateBlockLabel();
  if (error == 0) {
    if (fConditional != 0) {
      code[branchInstruction].fArguments.u4 = endLabel;
    } else {
      context.SetUnreachable();
    }
  }
  for (list<CNonlocalBranch>::iterator i = fNestedBreaks.begin();
       !(i == fNestedBreaks.end()); ++i) {
    unsigned long address = (*i).GetAddress();
    code[address].fArguments.u4 = endLabel;
    context.Merge((*i).GetContext());
  }
  return error;
}

//
//  Method name : RegisterContinueBranch
//  Description : This method tells the loop construct that a continue
//    statement was found in its body and gives the loop the location of the
//    'goto' code in the instruction block.  The loop is then responsible for
//    adjusting the target of the 'goto' to jump to the right location.
//
void
CForStatement::RegisterContinueBranch(const CNonlocalBranch& instruction)
{
  fNestedContinues.push_front(instruction);
}

//
//  Method name : RegisterBreakBranch
//  Description : This method tells the loop construct that a break
//    statement was found in its body and gives the loop the location of the
//    'goto' code in the instruction block.  The loop is then responsible for
//    adjusting the target of the 'goto' to jump to the right location.
//
void
CForStatement::RegisterBreakBranch(const CNonlocalBranch& instruction)
{
  fNestedBreaks.push_front(instruction);
}

//============================== CThrowStatement =============================

//
//  Method name : CThrowStatement
//  Description : Constructs a CThrowStatement that will throw the given
//    value.
//
CThrowStatement::CThrowStatement(CExpression* throwValue)
   : fValue(throwValue)
{
}

//
//  Method name : ~CThrowStatement
//  Description : Destructor.
//
CThrowStatement::~CThrowStatement()
{
  delete fValue;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CThrowStatement::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  CJavaTypeSignature type;
  CCompileError* error =
    CExpression::EvaluateType(fValue, context, type);
  if (error == 0 && !context.GetCompiler().IsThrowable(type)) {
    error = MakeError("Attempt to throw a non-throwable value.");
  } else if (!context.Throwable(type)) {
    unicode_string message = type.Disassemble();
    message += ::UTFToUnicode(" must be caught or declared in this method's "
			      " 'throws' list.");
    error = MakeError(message);
  }
  if (error == 0) {
    error = fValue->GenerateCode(code, context, stackUsed, false);
  }
  code.Append(CJavaCodeAttribute::athrow, fValue->GetLineNumber());
  context.SetUnreachable();
  return error;
}

//=============================== CSynchronized ===============================

//
//  Method name : CSynchronized
//  Description : Constructs a CSynchronized statement block.
//
CSynchronized::CSynchronized(CExpression* adoptCondition,
			     CStatement* adoptBlock,
			     unsigned short synchronizeVariable)
   : fCondition(adoptCondition),
     fBlock(adoptBlock),
     fSynchronizeVariable(synchronizeVariable)
{
}

//
//  Method name : ~CSynchronized
//  Description : Destructor.
//
CSynchronized::~CSynchronized()
{
  delete fCondition;
  delete fBlock;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CSynchronized::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  assert(fCondition != 0 && fBlock != 0);
  CJavaTypeSignature type;
  CCompileError* error =
    CExpression::EvaluateType(fCondition, context, type);
  if (error == 0 && !type.IsReference()) {
    error = MakeError("'synchronized' used with non-reference expression");
  }
  if (error == 0) {
    error = fCondition->GenerateCode(code, context, stackUsed, false);
  }
  if (error == 0) {
    code.Append(CJavaCodeAttribute::astore, fSynchronizeVariable, fLineNumber);
    code.Append(CJavaCodeAttribute::aload, fSynchronizeVariable, fLineNumber);
    code.Append(CJavaCodeAttribute::monitorenter, fLineNumber);
    CJavaCodeAttribute::ExceptionInfo tempException;
    tempException.fStartPC = code.CreateBlockLabel();
    unsigned short blockStack;
    context.GetCompiler().PushStatementContext(this);
    error = fBlock->GenerateCode(code, context, blockStack);
    stackUsed = ::max(stackUsed, blockStack);
    context.GetCompiler().PopStatementContext();
    tempException.fEndPC = code.CreateBlockLabel();
    EmitMonitorExitCode(code);
    unsigned long gotoInstruction = code.size();
    code.Append(CJavaCodeAttribute::op_goto, fLineNumber);
    CJavaCodeAttribute* codeAttribute = context.GetMethod().GetCode();
    assert(codeAttribute != 0);
    tempException.fHandlerPC = code.CreateBlockLabel();
    tempException.fCatchType = 0;
    codeAttribute->AddExceptionHandler(tempException);
    EmitMonitorExitCode(code);
    code.Append(CJavaCodeAttribute::athrow, fLineNumber);
    code[gotoInstruction].fArguments.u4 = code.CreateBlockLabel();
  }
  return error;
}

//
//  Method name : EmitMonitorExitCode
//  Description : This method is used to append code necessary to clean up
//    the monitor created by this synchronized statement.
//
void
CSynchronized::EmitMonitorExitCode(CCodeSequence& code)
{
  code.Append(CJavaCodeAttribute::aload, fSynchronizeVariable, fLineNumber);
  code.Append(CJavaCodeAttribute::monitorexit, fLineNumber);
}

//============================== CLabelStatement ==============================

//
//  Method name : CLabelStatement
//  Description : Constructs a CLabelStatement for a labelled jump.
//
CLabelStatement::CLabelStatement(unicode_string* label,
				 CStatement* adoptStatement)
   : fLabel(label),
     fCaseExpression(0),
     fStatement(adoptStatement),
     fType(kLabel)
{
}

//
//  Method name : CLabelStatement
//  Description : Constructs a CLabelStatement for a 'default' statement
//
CLabelStatement::CLabelStatement(CStatement* adoptDefault)
  : fLabel(0),
    fCaseExpression(0),
    fStatement(adoptDefault),
    fType(kDefault)
{
}

//
//  Method name : CLabelStatement
//  Description : Constructs a CLabelStatement for a 'case' statement
//
CLabelStatement::CLabelStatement(CExpression* caseExpression,
				 CStatement* adoptCase)
  : fLabel(0),
    fCaseExpression(caseExpression),
    fStatement(adoptCase),
    fType(kCase)
{
}

//
//  Method name : ~CLabelStatement
//  Description : Destructor.
//
CLabelStatement::~CLabelStatement()
{
  delete fLabel;
  delete fCaseExpression;
  delete fStatement;
}

//
//  Method name : GetLabel
//  Description : Retrieves the label that is applied using this label
//    statement and returns it to the user.  If this is a case or default
//    label, 0 is returned.
//
const unicode_string*
CLabelStatement::GetLabel() const
{
  return fLabel;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CLabelStatement::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  CCompileError* error = 0;
  stackUsed = 0;
  if (fStatement != 0) {
    context.GetCompiler().PushStatementContext(this);
    error = fStatement->GenerateCode(code, context, stackUsed);
    context.GetCompiler().PopStatementContext();
  }
  if (!fNestedBreaks.empty()) {
    unsigned long endLabel = code.CreateBlockLabel();
    for (list<CNonlocalBranch>::iterator i = fNestedBreaks.begin();
	 !(i == fNestedBreaks.end()); ++i) {
      unsigned long address = (*i).GetAddress();
      code[address].fArguments.u4 = endLabel;
      context.Merge((*i).GetContext());
    }
  }
  return error;
}

//
//  Method name : RegisterBreakBranch
//  Description : This method tells the label construct that a break
//    statement was found in its body and gives this statement the location
//    of the 'goto' code in the instruction block.  The label is then
//    responsible for adjusting the target of the 'goto' to jump to the right
//    location.
//
void
CLabelStatement::RegisterBreakBranch(const CNonlocalBranch& instruction)
{
  fNestedBreaks.push_front(instruction);
}

//============================== CTryStatement ==============================

//
//  Method name : CTryStatement
//  Description : Constructs a CTryStatement
//
CTryStatement::CTryStatement(CCompoundStatement* TryBlock,
		     deque<CCatchClause*>* catchClauses,
		     CCompoundStatement* finally,
		     unsigned short finallyHandlerVariable,
		     unsigned short finallySubroutineVariable)
   : fTryBlock(TryBlock),
     fCatchList(catchClauses),
     fFinally(finally),
     fFinallyHandlerVariable(finallyHandlerVariable),
     fFinallySubroutineVariable(finallySubroutineVariable),
     fFinallySubroutineLocation(0)
{
  if (fCatchList == 0) {
    fCatchList = new deque<CCatchClause*>;
  }
}

//
//  Method name : ~CTryStatement
//  Description : Destructor.
//
CTryStatement::~CTryStatement()
{
  delete fTryBlock;
  delete fFinally;
  for (deque<CCatchClause*>::iterator i = fCatchList->begin();
       i != fCatchList->end(); ++i) {
    delete *i;
  }
  delete fCatchList;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CTryStatement::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  assert(fTryBlock != 0 && fCatchList != 0);
  stackUsed = 0;
  CCompileError* error = 0;
  CCompileContext inContext = context;
  unsigned long handlerInstruction;
  if (fFinally != 0) {
    // goto to skip over the 'finally' handler & subroutine
    unsigned long gotoInstruction = code.size();
    code.Append(CJavaCodeAttribute::op_goto, fLineNumber);
    // exception handler code to call 'finally'
    handlerInstruction = code.CreateBlockLabel();
    code.Append(CJavaCodeAttribute::astore,
		fFinallyHandlerVariable, fLineNumber);
    unsigned long jsrInstruction = code.size();
    code.Append(CJavaCodeAttribute::jsr, fLineNumber);
    code.Append(CJavaCodeAttribute::aload,
		fFinallyHandlerVariable, fLineNumber);
    code.Append(CJavaCodeAttribute::athrow, fLineNumber);
    // 'finally' subroutine code
    fFinallySubroutineLocation = code.CreateBlockLabel();
    code[jsrInstruction].fArguments.u4 = fFinallySubroutineLocation;
    context.GetCompiler().PushFinallyHandler(fFinallySubroutineLocation);
    code.Append(CJavaCodeAttribute::astore,
		fFinallySubroutineVariable, fLineNumber);
    error = fFinally->GenerateCode(code, context, stackUsed);
    stackUsed = ::max(stackUsed, (unsigned short)1);
    code.Append(CJavaCodeAttribute::ret,
		fFinallySubroutineVariable, fLineNumber);
    code[gotoInstruction].fArguments.u4 = code.CreateBlockLabel();
  }

  // stick in the part of the code enclosed by the try { ... } clause
  inContext.GetCompiler().PushStatementContext(this);
  unsigned long startTryBlock = code.CreateBlockLabel();
  if (error == 0) {
    for (deque<CCatchClause*>::iterator i = fCatchList->begin();
	 !(i == fCatchList->end()); ++i) {
      context.PushThrowable(inContext.GetCompiler().FixType((*i)->fCatchType));
    }
    unsigned short tryStack;
    error = fTryBlock->GenerateCode(code, context, tryStack);
    for (int i = 0; i < fCatchList->size(); ++i) {
      context.PopThrowable();
    }
    stackUsed = ::max(tryStack, stackUsed);
  }
  unsigned long endTryBlock = code.CreateBlockLabel();
  if (context.IsReachable()) {
    EmitFinallyCall(code);
  }

  // now stick in the code needed for each of the 'catch' clauses...
  CJavaCodeAttribute* codeAttribute = context.GetMethod().GetCode();
  assert(codeAttribute != 0);
  if (fCatchList->size() > 0) {
    stackUsed = ::max(stackUsed, (unsigned short)1);
    unsigned long gotoInstruction = code.size();
    code.Append(CJavaCodeAttribute::op_goto, fLineNumber);
    for (deque<CCatchClause*>::iterator i = fCatchList->begin();
	 error == 0 && !(i == fCatchList->end()); ++i) {
      assert((*i) != 0 && (*i)->fBlock != 0);
      assert((*i)->fCatchType.IsReference());
      unicode_string className;
      inContext.GetCompiler().FixType((*i)->fCatchType).GetBaseClassName(
								 className);
      unsigned short exceptionClassIndex =
	inContext.GetClass().AddClassConstant(className);
      CJavaCodeAttribute::ExceptionInfo tempException;
      tempException.fStartPC = startTryBlock;
      tempException.fEndPC = endTryBlock;
      tempException.fHandlerPC = code.CreateBlockLabel();
      tempException.fCatchType = exceptionClassIndex;
      codeAttribute->AddExceptionHandler(tempException);
      unsigned short variable = (*i)->fExceptionVariable;
      code.Append(CJavaCodeAttribute::astore, variable, fLineNumber);
      unsigned short catchStack;
      CCompileContext catchContext = inContext;
      catchContext.InitializeVariable((*i)->fExceptionVariable);
      error = (*i)->fBlock->GenerateCode(code, catchContext, catchStack);
      stackUsed = ::max(stackUsed, catchStack);
      if (catchContext.IsReachable()) {
	EmitFinallyCall(code);
	code.Append(CJavaCodeAttribute::op_goto, endTryBlock, fLineNumber);
      }
      context.Merge(catchContext);
    }
    code[gotoInstruction].fArguments.u4 = code.CreateBlockLabel();
  }
  context.GetCompiler().PopStatementContext();
  if (fFinally != 0) {
    context.GetCompiler().PopFinallyHandler();
    CJavaCodeAttribute::ExceptionInfo tempException;
    tempException.fStartPC = startTryBlock;
    tempException.fEndPC = code.CreateBlockLabel();
    tempException.fHandlerPC = handlerInstruction;
    tempException.fCatchType = 0;
    codeAttribute->AddExceptionHandler(tempException);
  }
  return error;
}

//
//  Method name : EmitFinallyCall
//  Description : This method is used to append a call to the 'finally' clause
//    for this 'try' statement onto the end of the provided string.  If
//    this try block doesn't contain a 'finally', then no code is emitted.
//
void
CTryStatement::EmitFinallyCall(CCodeSequence& code)
{
  if (fFinally != 0) {
    code.Append(CJavaCodeAttribute::jsr,
		fFinallySubroutineLocation, fLineNumber);
  }
}

//
//  Method name : CCatchClause
//  Description : Constructs a catch clause for a Try statement
//
CCatchClause::CCatchClause(unsigned short variableIndex,
			   const CJavaTypeSignature& catchType,
			   CCompoundStatement* block)
   : fExceptionVariable(variableIndex),
     fCatchType(catchType),
     fBlock(block)
{
}

//
//  Method name : ~CCatchClause
//  Description : Destructor.
//
CCatchClause::~CCatchClause()
{
  delete fBlock;
}


//================================== CSwitch ==================================

//
//  Method name : CSwitch
//  Description : Constructs a CSwitch statement from the expression that
//    forms the basis for the jump and the block of statements to jump into.
//
CSwitch::CSwitch(CExpression* adoptSwitch, CCompoundStatement* block)
  : fSwitchExpression(adoptSwitch),
    fBlock(block)
{
}

//
//  Method name : ~CSwitch
//  Description : Destructor.
//
CSwitch::~CSwitch()
{
  delete fSwitchExpression;
  delete fBlock;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CSwitch::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  assert(fSwitchExpression != 0 && fBlock != 0 && fBlock->fChildren != 0);
  CJavaTypeSignature type;
  CCompileError* error =
    CExpression::EvaluateType(fSwitchExpression, context, type);
  if (error == 0) {
    error = fSwitchExpression->GenerateCode(code, context,
					    stackUsed, false);
  }
  unsigned long switchBase = code.size();
  long highKey = INT_MIN, lowKey = INT_MAX;
  vector<unsigned long> keys;
  bool foundDefault = false;
  context.GetCompiler().PushStatementContext(this);
  for (StatementList::iterator i = fBlock->fChildren->begin();
       error == 0 && i != fBlock->fChildren->end(); ++i) {
    error = ScanStatementForLabels(*(*i), keys, foundDefault, highKey,
				   lowKey, context);
  }
  if (error == 0) {
    unsigned long switchLine = fSwitchExpression->GetLineNumber();
    long numKeys = highKey - lowKey + 1;
    unsigned long keyCount = keys.size();
    if (keyCount > 0 && numKeys <= keyCount * 2) {
      // use a tableswitch
      code.Append(CJavaCodeAttribute::tableswitch, numKeys, switchLine);
      code.Append(CJavaCodeAttribute::nop, 0, switchLine);
      code.Append(CJavaCodeAttribute::nop, lowKey, switchLine);
      code.Append(CJavaCodeAttribute::nop, highKey, switchLine);
      for (int i = 0; i < numKeys; ++i) {
	code.Append(CJavaCodeAttribute::nop, (unsigned long)-1, switchLine);
      }
      unsigned long defaultLocation = 0;
      bool defaultFound = false;
      CCompileContext currentContext = context;
      currentContext.SetUnreachable();
      for (StatementList::iterator i = fBlock->fChildren->begin();
	   error == 0 && i != fBlock->fChildren->end(); ++i) {
	CLabelStatement* statement = DYNAMIC_CAST(CLabelStatement, *i);
	if (statement != 0) {
	  switch (statement->fType) {
	  case CLabelStatement::kDefault:
	    defaultFound = true;
	  case CLabelStatement::kCase:
	    currentContext.Merge(context);
	    TableswitchLabel(*statement, code, switchBase + 4,
			     lowKey, defaultLocation);
	  }
	}
	unsigned short childStack;
	error = (*i)->GenerateCode(code, currentContext, childStack);
	if (childStack > stackUsed) {
	  stackUsed = childStack;
	}
      }
      if (!defaultFound) {
	defaultLocation = code.CreateBlockLabel();
	context.Merge(currentContext);
      } else {
	context = currentContext;
      }
      code[switchBase + 1].fArguments.u4 = defaultLocation;
      for (int i = 0; i < numKeys; ++i) {
	if (code[switchBase + i + 4].fArguments.u4 == (unsigned long)-1) {
	  code[switchBase + i + 4].fArguments.u4 = defaultLocation;
	}
      }
    } else {
      // use a lookupswitch
      code.Append(CJavaCodeAttribute::lookupswitch, numKeys, switchLine);
      code.Append(CJavaCodeAttribute::nop, 0, switchLine);
      code.Append(CJavaCodeAttribute::nop, keyCount, switchLine);
      for (int i = 0; i < keyCount * 2; ++i) {
	code.Append(CJavaCodeAttribute::nop, 0, switchLine);
      }
      unsigned long startKey = switchBase + 3;
      unsigned long endKey = startKey;
      unsigned long defaultLocation = 0;
      bool defaultFound = false;
      CCompileContext currentContext = context;
      currentContext.SetUnreachable();
      for (StatementList::iterator i = fBlock->fChildren->begin();
	   error == 0 && i != fBlock->fChildren->end(); ++i) {
	CLabelStatement* statement = DYNAMIC_CAST(CLabelStatement, *i);
	if (statement != 0) {
	  switch (statement->fType) {
	  case CLabelStatement::kDefault:
	    defaultFound = true;
	  case CLabelStatement::kCase:
	    currentContext.Merge(context);
	    LookupswitchLabel(*statement, code, startKey, endKey,
			      defaultLocation);
	  }
	}
	unsigned short childStack;
	error = (*i)->GenerateCode(code, currentContext, childStack);
	if (childStack > stackUsed) {
	  stackUsed = childStack;
	}
      }
      if (!defaultFound) {
	defaultLocation = code.CreateBlockLabel();
	context.Merge(currentContext);
      } else {
	context = currentContext;
      }
      code[switchBase + 1].fArguments.u4 = defaultLocation;
    }
  }
  context.GetCompiler().PopStatementContext();
  if (!fNestedBreaks.empty()) {
    unsigned long afterSwitch = code.CreateBlockLabel();
    for (list<CNonlocalBranch>::iterator i = fNestedBreaks.begin();
	 !(i == fNestedBreaks.end()); ++i) {
      unsigned long address = (*i).GetAddress();
      code[address].fArguments.u4 = afterSwitch;
      context.Merge((*i).GetContext());
    }
  }
  return error;
}

//
//  Method name : ScanStatementForLabels
//  Description : This is a little helper function used by CSwitch to go
//    through a statement to try to find the top-level 'case' and 'default'
//    labels which it needs to know about.  In the process, it figures out
//    the highest and lowest 'case' labels as well as a count of all case
//    labels found.  If this evaluation leads to an error, this method returns
//    a newly-allocated compile error.
//
CCompileError*
CSwitch::ScanStatementForLabels(CStatement& statement,
			   vector<unsigned long>& keys, bool& foundDefault,
			   long& highKey, long& lowKey,
			   CCompileContext& context)
{
  CCompileError* error = 0;
  CLabelStatement* label = DYNAMIC_CAST(CLabelStatement, &statement);
  if (label != 0) {
    switch (label->fType) {
    case CLabelStatement::kCase:
      {
	CJavaTypeSignature caseType;
	error = CExpression::EvaluateType(label->fCaseExpression,
					  context, caseType);
	if (error == 0) {
	  COrdinalLiteral* key =
	    DYNAMIC_CAST(COrdinalLiteral, label->fCaseExpression);
	  if (key == 0) {
	    error = new CCompileError("Invalid 'case' label.",
				      label->fCaseExpression->GetLineNumber());
	  } else {
	    long keyValue = key->GetInteger();
	    for (vector<unsigned long>::iterator i = keys.begin();
		i != keys.end(); ++i) {
	      if (*i == (unsigned long)keyValue) {
		error = new CCompileError("Duplicate 'case' label.",
				      label->fCaseExpression->GetLineNumber());
		break;
	      }
	    }
	    if (error == 0) {
	      keys.push_back((unsigned long)keyValue);
	      highKey = (keyValue > highKey) ? keyValue : highKey;
	      lowKey = (keyValue < lowKey) ? keyValue : lowKey;
	      error = ScanStatementForLabels(*label->fStatement, keys,
				     foundDefault, highKey, lowKey, context);
	    }
	  }
	}
      }
      break;
    case CLabelStatement::kDefault:
      if (foundDefault) {
	error = MakeError("Two 'default' tags found in body of 'switch'");
      } else {
	foundDefault = true;
	error = ScanStatementForLabels(*label->fStatement, keys,
				       foundDefault, highKey, lowKey, context);
      }
      break;
    }
  }
  return error;
}

//
//  Method name : TableswitchLabel
//  Description : This is used to process a label found at the top-level of the
//    statement block in a switch statement.  Each of the labels must be
//    registered in the switch lookup table, and this call makes sure that this
//    gets done even in cases where there are nested labels that need to be
//    recognized.      case 1: case 2: ...
//
void
CSwitch::TableswitchLabel(CLabelStatement& label, CCodeSequence& code,
			  unsigned long keyBase, long lowKey,
			  unsigned long& defaultLocation)
{
  switch (label.fType) {
  case CLabelStatement::kCase:
    {
      COrdinalLiteral* key =
	DYNAMIC_CAST(COrdinalLiteral, label.fCaseExpression);
      assert(key != 0);
      unsigned long startKey = keyBase + (((long)key->GetInteger()) - lowKey);
      code[startKey].fArguments.u4 = code.CreateBlockLabel();
    }
    break;
  case CLabelStatement::kDefault:
    defaultLocation = code.CreateBlockLabel();
  }
  CLabelStatement* childLabel =
    DYNAMIC_CAST(CLabelStatement, label.fStatement);
  if (childLabel != 0) {
    TableswitchLabel(*childLabel, code, keyBase, lowKey, defaultLocation);
  }
}

//
//  Method name : LookupswitchLabel
//  Description : This is used to process a label found at the top-level of the
//    statement block in a switch statement.  Each of the labels must be
//    registered in the switch lookup table, and this call makes sure that this
//    gets done even in cases where there are nested labels that need to be
//    recognized.      case 1: case 2: ...
//
void
CSwitch::LookupswitchLabel(CLabelStatement& label, CCodeSequence& code,
			   unsigned long keyBase, unsigned long& endKey,
			   unsigned long& defaultLocation)
{
  switch (label.fType) {
  case CLabelStatement::kCase:
    {
      COrdinalLiteral* key =
	DYNAMIC_CAST(COrdinalLiteral, label.fCaseExpression);
      assert(key != 0);
      // have to insert in ascending order as of JDK 1.1, apparently.
      int insertPoint = endKey;
      for (; insertPoint > keyBase; insertPoint -= 2) {
	if ((long)code[insertPoint - 2].fArguments.u4 <=
	    (long)key->GetInteger()) {
	  break;
	}
	code[insertPoint].fArguments.u4 = code[insertPoint - 2].fArguments.u4;
	code[insertPoint + 1].fArguments.u4 =
	  code[insertPoint - 1].fArguments.u4;
      }
      code[insertPoint].fArguments.u4 = key->GetInteger();
      code[insertPoint + 1].fArguments.u4 = code.CreateBlockLabel();
      endKey += 2;
    }
    break;
  case CLabelStatement::kDefault:
    defaultLocation = code.CreateBlockLabel();
  }
  CLabelStatement* childLabel =
    DYNAMIC_CAST(CLabelStatement, label.fStatement);
  if (childLabel != 0) {
    LookupswitchLabel(*childLabel, code, keyBase, endKey, defaultLocation);
  }
}

//
//  Method name : RegisterBreakBranch
//  Description : This method tells the switch construct that a break
//    statement was found in its body.  This gives the switch the location of
//    the 'goto' code in the instruction block.  The loop is then responsible
//    for adjusting the target of the 'goto' to jump to the right location.
//
void
CSwitch::RegisterBreakBranch(const CNonlocalBranch& instruction)
{
  fNestedBreaks.push_front(instruction);
}

//========================= CExplicitConstructorCall ==========================

//
//  Method name : CExplicitConstructorCall
//  Description : Constructs a CExplicitConstructorCall
//
CExplicitConstructorCall::CExplicitConstructorCall(Type type,
					   ExpressionList* adoptArguments)
  : fType(type)
{
  CExpression* target = (type == kThis) ?
    new CSpecialExpression(CSpecialExpression::kThis) :
    new CSpecialExpression(CSpecialExpression::kSuper);
  fExpression =
    new CMethodCall(target, CCompiler::kConstructorName, adoptArguments, true);
}

//
//  Method name : ~CExplicitConstructorCall
//  Description : Destructor.
//
CExplicitConstructorCall::~CExplicitConstructorCall()
{
  delete fExpression;
}

//
//  Method name : GenerateCode
//  Description : Appends the generated bytecode for this statement onto the
//    code parameter, using the other parameters to help get the
//    needed information.  In addition, the 'stackUsed' parameter is set to
//    the maximum expression stack depth incurred by this statement.
//    If this operation is successful, 0 is returned, otherwise a compile
//    error structure is created and returned to the caller to explain what
//    went wrong.
//
CCompileError*
CExplicitConstructorCall::HandleGenerateCode(CCodeSequence& code,
		 CCompileContext& context, unsigned short& stackUsed)
{
  assert(fExpression != 0);
  stackUsed = 0;
  CJavaTypeSignature type;
  CCompileError* error =
    CExpression::EvaluateType(fExpression, context, type);
  if (error == 0) {
    error =
      fExpression->GenerateCode(code, context, stackUsed, true);
  }
  return error;
}
