// $Id: body.cpp,v 1.8 1999/03/09 14:37:15 shields Exp $ copyright notice #include "config.h" #include <assert.h> #include "semantic.h" #include "control.h" void Semantic::ProcessBlockStatements(AstBlock *block_body) { // // An empty block that is not a switch block can complete normally // iff it is reachable. A nonempty block that is not a switch // block can complete normally iff the last statement in it can // complete normally. // if (block_body -> NumStatements() == 0) block_body -> can_complete_normally = block_body -> is_reachable; else { // // The first statement in a nonempty block that is not a // switch block is reachable iff the block is reachable. // Every other statement S in a nonempty block that is not a // switch block is reachable iff the statement preceeding S // can complete normally. // AstStatement *statement = (AstStatement *) block_body -> Statement(0); statement -> is_reachable = block_body -> is_reachable; AstStatement *first_unreachable_statement = (AstStatement *) (statement -> is_reachable ? NULL : statement); ProcessStatement(statement); for (int i = 1; i < block_body -> NumStatements(); i++) { AstStatement *previous_statement = statement; statement = (AstStatement *) block_body -> Statement(i); statement -> is_reachable = previous_statement -> can_complete_normally; if (! statement -> is_reachable && (first_unreachable_statement == NULL)) first_unreachable_statement = statement; ProcessStatement(statement); } if (statement -> can_complete_normally) block_body -> can_complete_normally = true; // // If we have one or more unreachable statements that are contained in a // reachable block then issue message. (If the enclosing block is not reachable // the message will be issued later for the enclosing block.) // if (first_unreachable_statement && LocalBlockStack().TopBlock() -> is_reachable) { if (first_unreachable_statement == statement) { ReportSemError(SemanticError::UNREACHABLE_STATEMENT, statement -> LeftToken(), statement -> RightToken()); } else { ReportSemError(SemanticError::UNREACHABLE_STATEMENTS, first_unreachable_statement -> LeftToken(), statement -> RightToken()); } } // // If an enclosed block has a higher max_variable_index than the current block, // update max_variable_index in the current_block, accordingly. // BlockSymbol *block = block_body -> block_symbol; if (block -> max_variable_index < LocalBlockStack().TopMaxEnclosedVariableIndex()) block -> max_variable_index = LocalBlockStack().TopMaxEnclosedVariableIndex(); } return; } void Semantic::ProcessBlock(Ast *stmt) { AstBlock *block_body = (AstBlock *) stmt; AstBlock *enclosing_block = LocalBlockStack().TopBlock(); // // Guess that the number of elements will not exceed the number of statements + 3. The +3 takes into account // one label + one ForInit declaration and one extra something else. // int table_size = block_body -> NumStatements() + 3; BlockSymbol *block = LocalSymbolTable().Top() -> InsertBlockSymbol(table_size); // // enclosing_block is not present only when we are processing the block of a static initializer // block -> max_variable_index = (enclosing_block ? enclosing_block -> block_symbol -> max_variable_index : 1); LocalSymbolTable().Push(block -> Table()); block_body -> block_symbol = block; block_body -> nesting_level = LocalBlockStack().Size(); LocalBlockStack().Push(block_body); // // Note that in constructing the Ast, the parser encloses each // labeled statement in its own block. Therefore the declaration // of this label will not conflict with the declaration of another // label with the same name declared at the same nesting level. // // For example, the following sequence of statements is legal: // // l: a = b; // l: b = c; // if (block_body -> label_token_opt) { NameSymbol *name_symbol = (NameSymbol *) lex_stream -> NameSymbol(block_body -> label_token_opt); Symbol *symbol = LocalSymbolTable().FindLabelSymbol(name_symbol); if (symbol) { ReportSemError(SemanticError::DUPLICATE_LABEL, block_body -> label_token_opt, block_body -> label_token_opt, name_symbol -> Name()); } else { LabelSymbol *label = LocalSymbolTable().Top() -> InsertLabelSymbol(name_symbol); label -> block = block_body; label -> nesting_level = block_body -> nesting_level; } } ProcessBlockStatements(block_body); LocalBlockStack().Pop(); LocalSymbolTable().Pop(); // // Update the information for the block that immediately encloses the current block. // if (enclosing_block) { if (LocalBlockStack().TopMaxEnclosedVariableIndex() < block -> max_variable_index) LocalBlockStack().TopMaxEnclosedVariableIndex() = block -> max_variable_index; } block -> CompressSpace(); // space optimization return; } void Semantic::ProcessLocalVariableDeclarationStatement(Ast *stmt) { AstLocalVariableDeclarationStatement *local_decl = (AstLocalVariableDeclarationStatement *) stmt; AstArrayType *array_type = local_decl -> type -> ArrayTypeCast(); Ast *actual_type = (array_type ? array_type -> type : local_decl -> type); if ((! control.option.one_one) && local_decl -> NumLocalModifiers() > 0) { ReportSemError(SemanticError::ONE_ONE_FEATURE, local_decl -> LocalModifier(0) -> LeftToken(), local_decl -> LocalModifier(local_decl -> NumLocalModifiers() - 1) -> RightToken()); } AccessFlags access_flags = ProcessLocalModifiers(local_decl); AstPrimitiveType *primitive_type = actual_type -> PrimitiveTypeCast(); TypeSymbol *field_type = (primitive_type ? field_type = FindPrimitiveType(primitive_type) : MustFindType(actual_type)); for (int i = 0; i < local_decl -> NumVariableDeclarators(); i++) { AstVariableDeclarator *variable_declarator = local_decl -> VariableDeclarator(i); AstVariableDeclaratorId *name = variable_declarator -> variable_declarator_name; NameSymbol *name_symbol = (NameSymbol *) lex_stream -> NameSymbol(name -> identifier_token); // // TODO: Confirm that this new test is indeed the case. In 1.0, only the more restricted test below was necessary. // // if (LocalSymbolTable().FindVariableSymbol(name_symbol)) // { // ReportSemError(SemanticError::DUPLICATE_LOCAL_VARIABLE_DECLARATION, // name -> identifier_token, // name -> identifier_token, // name_symbol -> Name()); // } // SemanticEnvironment *where_found; Tuple<VariableSymbol *> variables_found(2); SearchForVariableInEnvironment(variables_found, where_found, state_stack.Top(), name_symbol, name -> identifier_token); VariableSymbol *symbol = (variables_found.Length() > 0 ? variables_found[0] : (VariableSymbol *) NULL); if (symbol && symbol -> IsLocal()) { ReportSemError(SemanticError::DUPLICATE_LOCAL_VARIABLE_DECLARATION, name -> identifier_token, name -> identifier_token, name_symbol -> Name()); } else { symbol = LocalSymbolTable().Top() -> InsertVariableSymbol(name_symbol); variable_declarator -> symbol = symbol; int num_dimensions = (array_type ? array_type -> NumBrackets() : 0) + name -> NumBrackets(); if (num_dimensions == 0) symbol -> SetType(field_type); else symbol -> SetType(field_type -> GetArrayType((Semantic *) this, num_dimensions)); symbol -> SetFlags(access_flags); symbol -> SetOwner(ThisMethod()); symbol -> declarator = variable_declarator; BlockSymbol *block = LocalBlockStack().TopBlock() -> block_symbol; symbol -> SetLocalVariableIndex(block -> max_variable_index++); // Assigning a local_variable_index to a variable // also marks it complete as a side-effect. if (symbol -> Type() == control.long_type || symbol -> Type() == control.double_type) block -> max_variable_index++; if (variable_declarator -> variable_initializer_opt) ProcessVariableInitializer(variable_declarator); } } // // A local variable declaration statement can complete normally // iff it is reachable. // local_decl -> can_complete_normally = local_decl -> is_reachable; return; } void Semantic::ProcessExpressionStatement(Ast *stmt) { AstExpressionStatement *expression_statement = (AstExpressionStatement *) stmt; ProcessExpression(expression_statement -> expression); // // An expression statement can complete normally iff it is reachable. // expression_statement -> can_complete_normally = expression_statement -> is_reachable; return; } void Semantic::ProcessSynchronizedStatement(Ast *stmt) { AstSynchronizedStatement *synchronized_statement = (AstSynchronizedStatement *) stmt; ProcessExpression(synchronized_statement -> expression); synchronized_statement -> block -> is_reachable = synchronized_statement -> is_reachable; if (synchronized_statement -> expression -> Type() -> Primitive()) { ReportSemError(SemanticError::TYPE_NOT_REFERENCE, synchronized_statement -> expression -> LeftToken(), synchronized_statement -> expression -> RightToken(), synchronized_statement -> expression -> Type() -> Name()); } AstBlock *enclosing_block = LocalBlockStack().TopBlock(), *block_body = synchronized_statement -> block; // // Guess that the number of elements will not exceed the number of statements + 3. // BlockSymbol *block = LocalSymbolTable().Top() -> InsertBlockSymbol(block_body -> NumStatements() + 3); // // "synchronized" blocks require two more local variable slots for synchronization, // plus one extra variable slot if the containing method returns a value, plus an // additional slot if the value returned is double or long. // block -> synchronized_variable_index = enclosing_block -> block_symbol -> max_variable_index; block -> max_variable_index = block -> synchronized_variable_index + 2; if (ThisMethod() -> Type() == control.double_type || ThisMethod() -> Type() == control.long_type) block -> max_variable_index += 2; else if (ThisMethod() -> Type() != control.void_type) block -> max_variable_index++; LocalSymbolTable().Push(block -> Table()); block_body -> block_symbol = block; block_body -> nesting_level = LocalBlockStack().Size(); LocalBlockStack().Push(block_body); ProcessBlockStatements(block_body); LocalBlockStack().Pop(); LocalSymbolTable().Pop(); if (LocalBlockStack().TopMaxEnclosedVariableIndex() < block -> max_variable_index) LocalBlockStack().TopMaxEnclosedVariableIndex() = block -> max_variable_index; // // If the synchronized_statement is enclosed in a loop and it contains a reachable continue statement // then it may have already been marked as "can complete normally"; // synchronized_statement -> can_complete_normally = synchronized_statement -> can_complete_normally || synchronized_statement -> block -> can_complete_normally; block -> CompressSpace(); // space optimization return; } void Semantic::ProcessIfStatement(Ast *stmt) { AstIfStatement *if_statement = (AstIfStatement *) stmt; ProcessExpression(if_statement -> expression); if (if_statement -> expression -> Type() != control.no_type && if_statement -> expression -> Type() != control.boolean_type) { ReportSemError(SemanticError::TYPE_NOT_BOOLEAN, if_statement -> expression -> LeftToken(), if_statement -> expression -> RightToken(), if_statement -> expression -> Type() -> Name()); } // // Recall that the parser ensures that the statements that appear in an if-statement // (both the true and false statement) are enclosed in a block. // if_statement -> true_statement -> is_reachable = if_statement -> is_reachable; ProcessBlock(if_statement -> true_statement); if (if_statement -> false_statement_opt) { if_statement -> false_statement_opt -> is_reachable = if_statement -> is_reachable; ProcessBlock(if_statement -> false_statement_opt); // // If the if_statement is enclosed in a loop and it contains a reachable continue statement // then it may have already been marked as "can complete normally"; // if_statement -> can_complete_normally = if_statement -> can_complete_normally || if_statement -> true_statement -> can_complete_normally || if_statement -> false_statement_opt -> can_complete_normally; } else if_statement -> can_complete_normally = if_statement -> is_reachable; return; } void Semantic::ProcessWhileStatement(Ast *stmt) { AstWhileStatement *while_statement = (AstWhileStatement *) stmt; // // Recall that each while statement is enclosed in a unique block by the parser // BreakableStatementStack().Push(LocalBlockStack().TopBlock()); ContinuableStatementStack().Push(LocalBlockStack().TopBlock()); AstStatement *enclosed_statement = while_statement -> statement; enclosed_statement -> is_reachable = while_statement -> is_reachable; ProcessExpression(while_statement -> expression); if (while_statement -> expression -> Type() == control.boolean_type) { if (while_statement -> expression -> IsConstant()) { IntLiteralValue *literal = (IntLiteralValue *) while_statement -> expression -> value; if (! literal -> value) { if (while_statement -> is_reachable) while_statement -> can_complete_normally = true; enclosed_statement -> is_reachable = false; } } else if (while_statement -> is_reachable) while_statement -> can_complete_normally = true; } else if (while_statement -> expression -> Type() != control.no_type) { ReportSemError(SemanticError::TYPE_NOT_BOOLEAN, while_statement -> expression -> LeftToken(), while_statement -> expression -> RightToken(), while_statement -> expression -> Type() -> Name()); } ProcessStatement(enclosed_statement); if ((! enclosed_statement -> is_reachable) && (while_statement -> is_reachable)) { AstBlock *block_body = enclosed_statement -> BlockCast(); if (! block_body || (block_body -> NumStatements() == 0)) { ReportSemError(SemanticError::UNREACHABLE_STATEMENT, enclosed_statement -> LeftToken(), enclosed_statement -> RightToken()); } } // // If the while statement contained a reachable break statement, // then the while statement can complete normally. It is marked // here only for completeness, as marking the enclosing block is // enough to propagate the proper information upward. // AstBlock *block_body = (AstBlock *) BreakableStatementStack().Top(); if (block_body -> can_complete_normally) while_statement -> can_complete_normally = true; BreakableStatementStack().Pop(); ContinuableStatementStack().Pop(); return; } void Semantic::ProcessForStatement(Ast *stmt) { AstForStatement *for_statement = (AstForStatement *) stmt; // // Note that in constructing the Ast, the parser encloses each // for-statement whose for-init-statements starts with a local // variable declaration in its own block. Therefore a redeclaration // of another local variable with the same name in a different loop // at the same nesting level will not cause any conflict. // // For example, the following sequence of statements is legal: // // for (int i = 0; i < 10; i++); // for (int i = 10; i < 20; i++); // for (int i = 0; i < for_statement -> NumForInitStatements(); i++) ProcessStatement(for_statement -> ForInitStatement(i)); // // Recall that each for statement is enclosed in a unique block by the parser // BreakableStatementStack().Push(LocalBlockStack().TopBlock()); ContinuableStatementStack().Push(LocalBlockStack().TopBlock()); // // Assume that if the for_statement is reachable then its // contained statement is also reachable. If it turns out that the // condition (end) expression is a constant FALSE expression we will // change the assumption... // AstStatement *enclosed_statement = for_statement -> statement; enclosed_statement -> is_reachable = for_statement -> is_reachable; if (for_statement -> end_expression_opt) { ProcessExpression(for_statement -> end_expression_opt); if (for_statement -> end_expression_opt -> Type() == control.boolean_type) { if (for_statement -> end_expression_opt -> IsConstant()) { IntLiteralValue *literal = (IntLiteralValue *) for_statement -> end_expression_opt -> value; if (! literal -> value) { if (for_statement -> is_reachable) for_statement -> can_complete_normally = true; enclosed_statement -> is_reachable = false; } } else if (for_statement -> is_reachable) for_statement -> can_complete_normally = true; } else if (for_statement -> end_expression_opt -> Type() != control.no_type) { ReportSemError(SemanticError::TYPE_NOT_BOOLEAN, for_statement -> end_expression_opt -> LeftToken(), for_statement -> end_expression_opt -> RightToken(), for_statement -> end_expression_opt -> Type() -> Name()); } } ProcessStatement(enclosed_statement); if ((! enclosed_statement -> is_reachable) && (for_statement -> is_reachable)) { AstBlock *block_body = enclosed_statement -> BlockCast(); if (! block_body || (block_body -> NumStatements() == 0)) { ReportSemError(SemanticError::UNREACHABLE_STATEMENT, enclosed_statement -> LeftToken(), enclosed_statement -> RightToken()); } } for (int j = 0; j < for_statement -> NumForUpdateStatements(); j++) ProcessExpressionStatement(for_statement -> ForUpdateStatement(j)); // // If the for statement contained a reachable break statement, // then the for statement can complete normally. It is marked // here only for completeness, as marking the enclosing block is // enough to propagate the proper information upward. // AstBlock *block_body = (AstBlock *) BreakableStatementStack().Top(); if (block_body -> can_complete_normally) for_statement -> can_complete_normally = true; BreakableStatementStack().Pop(); ContinuableStatementStack().Pop(); return; } void Semantic::ProcessSwitchStatement(Ast *stmt) { AstSwitchStatement *switch_statement = (AstSwitchStatement *) stmt; AstBlock *enclosing_block = LocalBlockStack().TopBlock(); // // We estimate a size for the switch symbol table based on the number of lines in it. // AstBlock *block_body = switch_statement -> switch_block; BlockSymbol *block = LocalSymbolTable().Top() -> InsertBlockSymbol(); block -> max_variable_index = enclosing_block -> block_symbol -> max_variable_index; LocalSymbolTable().Push(block -> Table()); block_body -> block_symbol = block; block_body -> nesting_level = LocalBlockStack().Size(); LocalBlockStack().Push(block_body); BreakableStatementStack().Push(block_body); ProcessExpression(switch_statement -> expression); TypeSymbol *type = switch_statement -> expression -> Type(); if (type != control.int_type && type != control.short_type && type != control.char_type && type != control.byte_type && type != control.no_type) { ReportSemError(SemanticError::TYPE_NOT_VALID_FOR_SWITCH, switch_statement -> expression -> LeftToken(), switch_statement -> expression -> RightToken(), type -> ContainingPackage() -> PackageName(), type -> ExternalName()); } switch_statement -> default_case.switch_block_statement = NULL; // // A switch block is reachable iff its switch statement is reachable. // block_body -> is_reachable = switch_statement -> is_reachable; for (int i = 0; i < block_body -> NumStatements(); i++) { AstSwitchBlockStatement *switch_block_statement = (AstSwitchBlockStatement *) block_body -> Statement(i); for (int j = 0; j < switch_block_statement -> NumSwitchLabels(); j++) { AstCaseLabel *case_label; if (case_label = switch_block_statement -> SwitchLabel(j) -> CaseLabelCast()) { ProcessExpression(case_label -> expression); if (CanAssignmentConvert(type, case_label -> expression)) { if (! case_label -> expression -> IsConstant()) { ReportSemError(SemanticError::EXPRESSION_NOT_CONSTANT, case_label -> expression -> LeftToken(), case_label -> expression -> RightToken()); case_label -> expression -> symbol = control.no_type; } else if (case_label -> expression -> Type() != control.no_type) { if (case_label -> expression -> Type() != type && type != control.no_type) case_label -> expression = ConvertToType(case_label -> expression, type); case_label -> map_index = switch_statement -> map.Length(); CaseElement *case_element = compilation_unit -> ast_pool -> GenCaseElement(); switch_statement -> map.Next() = case_element; case_element -> expression = case_label -> expression; case_element -> switch_block_statement = switch_block_statement; case_element -> index = case_label -> map_index; // use this index to keep sort stable ! } } else { ReportSemError(SemanticError::VALUE_NOT_REPRESENTABLE_IN_TYPE, case_label -> expression -> LeftToken(), case_label -> expression -> RightToken(), type -> Name()); } } else if (switch_statement -> default_case.switch_block_statement == NULL) switch_statement -> default_case.switch_block_statement = switch_block_statement; else { ReportSemError(SemanticError::MULTIPLE_DEFAULT_LABEL, ((AstDefaultLabel *) switch_block_statement -> SwitchLabel(j)) -> LeftToken(), ((AstDefaultLabel *) switch_block_statement -> SwitchLabel(j)) -> RightToken()); } } if (switch_block_statement -> NumStatements() > 0) { // // A statement in a switch block is reachable iff its // switch statement is reachable and at least one of the // following is true: // // . it bears a case or default label // . there is a statement preceeding it in the switch block and that // preceeding statement can compile normally. // AstStatement *statement = (AstStatement *) switch_block_statement -> Statement(0); statement -> is_reachable = switch_statement -> is_reachable; AstStatement *first_unreachable_statement = (AstStatement *) (statement -> is_reachable ? NULL : statement); ProcessStatement(statement); for (int j = 1; j < switch_block_statement -> NumStatements(); j++) { AstStatement *previous_statement = statement; statement = (AstStatement *) switch_block_statement -> Statement(j); if (switch_statement -> is_reachable) { statement -> is_reachable = previous_statement -> can_complete_normally; if ((! statement -> is_reachable) && (first_unreachable_statement == NULL)) first_unreachable_statement = statement; } ProcessStatement(statement); } if (first_unreachable_statement) { if (first_unreachable_statement == statement) { ReportSemError(SemanticError::UNREACHABLE_STATEMENT, statement -> LeftToken(), statement -> RightToken()); } else { ReportSemError(SemanticError::UNREACHABLE_STATEMENTS, first_unreachable_statement -> LeftToken(), statement -> RightToken()); } } } } // // A switch statement can complete normally iff at least one of the // following is true: // // . there is a reachable break statement that exits the switch // statement. (See ProcessBreakStatement) // . the switch block is empty or contains only switch labels // // // // TODO: This statement seems to be erroneous. The proper statement // // as implemented here is: // // // // . the switch block is empty or contains only case labels // // // . there is at least one switch label after the last switch block // statement group. // . the last statement in the switch block can complete normally // if (block_body -> can_complete_normally) switch_statement -> can_complete_normally = true; else if (switch_statement -> default_case.switch_block_statement == NULL) switch_statement -> can_complete_normally = true; else { int last_index = block_body -> NumStatements() - 1; AstSwitchBlockStatement *last_switch_block_statement = (AstSwitchBlockStatement *) block_body -> Statement(last_index); // // Last switch block statement contains only switch labels. // if (last_switch_block_statement -> NumStatements() == 0) switch_statement -> can_complete_normally = true; else { // // Last switch block statement contains only switch statements // AstStatement *last_statement = (AstStatement *) last_switch_block_statement -> Statement(last_switch_block_statement -> NumStatements() - 1); if (last_statement -> can_complete_normally) switch_statement -> can_complete_normally = true; } } switch_statement -> SortCases(); for (int k = 1; k < switch_statement -> map.Length(); k++) { if (switch_statement -> map[k] -> Value() == switch_statement -> map[k - 1] -> Value()) { wchar_t info[12], *str = &info[11]; int n = switch_statement -> map[k] -> Value(); bool negative = (n < 0); if (negative) n = -n; *str = U_NULL; do { *--str = (U_0 + n % 10); n /= 10; } while (n != 0); if (negative) *--str = '-'; ReportSemError(SemanticError::DUPLICATE_CASE_VALUE, switch_statement -> map[k] -> expression -> LeftToken(), switch_statement -> map[k] -> expression -> RightToken(), str); } } // // If an enclosed block has a higher max_variable_index than the current block, // update max_variable_index in the current_block, accordingly. // Also, update the information for the block that immediately encloses the current block. // if (block -> max_variable_index < LocalBlockStack().TopMaxEnclosedVariableIndex()) block -> max_variable_index = LocalBlockStack().TopMaxEnclosedVariableIndex(); BreakableStatementStack().Pop(); LocalBlockStack().Pop(); LocalSymbolTable().Pop(); if (enclosing_block) { if (LocalBlockStack().TopMaxEnclosedVariableIndex() < block -> max_variable_index) LocalBlockStack().TopMaxEnclosedVariableIndex() = block -> max_variable_index; } block -> CompressSpace(); // space optimization return; } void Semantic::ProcessDoStatement(Ast *stmt) { AstDoStatement *do_statement = (AstDoStatement *) stmt; // // Recall that each Do statement is enclosed in a unique block by the parser // BreakableStatementStack().Push(LocalBlockStack().TopBlock()); ContinuableStatementStack().Push(LocalBlockStack().TopBlock()); AstStatement *enclosed_statement = do_statement -> statement; enclosed_statement -> is_reachable = do_statement -> is_reachable; ProcessStatement(enclosed_statement); ProcessExpression(do_statement -> expression); IntLiteralValue *literal = NULL; if (do_statement -> expression -> Type() == control.boolean_type) { if (do_statement -> expression -> IsConstant()) literal = (IntLiteralValue *) do_statement -> expression -> value; } else if (do_statement -> expression -> Type() != control.no_type) { ReportSemError(SemanticError::TYPE_NOT_BOOLEAN, do_statement -> expression -> LeftToken(), do_statement -> expression -> RightToken(), do_statement -> expression -> Type() -> Name()); } // // A do statement can complete normally, iff at least one of the following is true: // 1. The contained statement can complete normally and the condition expression // is not a constant expression with the value true // 2. There is a reachable break statement that exits the do statement // (This condition is true is the block that immediately encloses this do statement // can complete normally. See ProcessBreakStatement) // AstBlock *block_body = (AstBlock *) BreakableStatementStack().Top(); do_statement -> can_complete_normally = (enclosed_statement -> can_complete_normally && ((! literal) || literal -> value == 0)) || block_body -> can_complete_normally; BreakableStatementStack().Pop(); ContinuableStatementStack().Pop(); return; } void Semantic::ProcessBreakStatement(Ast *stmt) { AstBreakStatement *break_statement = (AstBreakStatement *) stmt; // // Recall that it is possible to break out of any labeled statement even if it is not a // do, for, while or switch statement. // if (break_statement -> identifier_token_opt) { NameSymbol *name_symbol = (NameSymbol *) lex_stream -> NameSymbol(break_statement -> identifier_token_opt); LabelSymbol *label_symbol = LocalSymbolTable().FindLabelSymbol(name_symbol); if (label_symbol) { break_statement -> nesting_level = label_symbol -> nesting_level; AstBlock *block_body = label_symbol -> block; // // A labeled statement can complete normally if there is a // reachable break statement that exits the labeled statement. // if (block_body && break_statement -> is_reachable) block_body -> can_complete_normally = true; } else { AstBlock *block_body = (AstBlock *) LocalBlockStack().TopBlock(); break_statement -> nesting_level = block_body -> nesting_level; ReportSemError(SemanticError::UNDECLARED_LABEL, break_statement -> identifier_token_opt, break_statement -> identifier_token_opt, lex_stream -> Name(break_statement -> identifier_token_opt)); } } else { AstBlock *block_body = (AstBlock *) (BreakableStatementStack().Size() > 0 ? BreakableStatementStack().Top() : LocalBlockStack().TopBlock()); break_statement -> nesting_level = block_body -> nesting_level; if (BreakableStatementStack().Size() > 0) { if (break_statement -> is_reachable) block_body -> can_complete_normally = true; } else ReportSemError(SemanticError::MISPLACED_BREAK_STATEMENT, break_statement -> LeftToken(), break_statement -> RightToken()); } return; } void Semantic::ProcessContinueStatement(Ast *stmt) { AstContinueStatement *continue_statement = (AstContinueStatement *) stmt; // // The loop statement that is to be continued. // Ast *loop_statement = NULL; if (ContinuableStatementStack().Size() <= 0) { ReportSemError(SemanticError::MISPLACED_CONTINUE_STATEMENT, continue_statement -> LeftToken(), continue_statement -> RightToken()); } else if (continue_statement -> identifier_token_opt) { NameSymbol *name_symbol = (NameSymbol *) lex_stream -> NameSymbol(continue_statement -> identifier_token_opt); LabelSymbol *label_symbol = LocalSymbolTable().FindLabelSymbol(name_symbol); if (label_symbol) { continue_statement -> nesting_level = label_symbol -> nesting_level; assert(label_symbol -> block -> NumStatements() > 0); loop_statement = label_symbol -> block -> Statement(0); } else { AstBlock *block_body = (AstBlock *) LocalBlockStack().TopBlock(); continue_statement -> nesting_level = block_body -> nesting_level; ReportSemError(SemanticError::UNDECLARED_LABEL, continue_statement -> identifier_token_opt, continue_statement -> identifier_token_opt, lex_stream -> Name(continue_statement -> identifier_token_opt)); } } else { AstBlock *block_body = (AstBlock *) ContinuableStatementStack().Top(); loop_statement = block_body -> Statement(0); continue_statement -> nesting_level = block_body -> nesting_level; } // // If this is a valid continue statement, it is associated with a loop statement. // Since the loop can be continued, its enclosed statement "can complete normally". // if (loop_statement) { AstDoStatement *do_statement = loop_statement -> DoStatementCast(); AstForStatement *for_statement = loop_statement -> ForStatementCast(); AstWhileStatement *while_statement = loop_statement -> WhileStatementCast(); AstStatement *enclosed_statement = (do_statement ? do_statement -> statement : (for_statement ? for_statement -> statement : (while_statement ? while_statement -> statement : NULL))); if (enclosed_statement) enclosed_statement -> can_complete_normally = true; else { assert(continue_statement -> identifier_token_opt); ReportSemError(SemanticError::INVALID_CONTINUE_TARGET, continue_statement -> LeftToken(), continue_statement -> RightToken(), lex_stream -> Name(continue_statement -> identifier_token_opt)); } } return; } void Semantic::ProcessReturnStatement(Ast *stmt) { AstReturnStatement *return_statement = (AstReturnStatement *) stmt; MethodSymbol *this_method = ThisMethod(); if (this_method -> name_symbol == control.clinit_name_symbol || this_method -> name_symbol == control.block_init_name_symbol) { ReportSemError(SemanticError::RETURN_STATEMENT_IN_INITIALIZER, return_statement -> LeftToken(), return_statement -> RightToken()); } else if (return_statement -> expression_opt) { ProcessExpression(return_statement -> expression_opt); if (this_method -> Type() == control.void_type) { ReportSemError(SemanticError::MISPLACED_RETURN_WITH_EXPRESSION, return_statement -> LeftToken(), return_statement -> RightToken()); } else if (return_statement -> expression_opt -> Type() != control.no_type) { if (! CanAssignmentConvert(this_method -> Type(), return_statement -> expression_opt)) { ReportSemError(SemanticError::MISMATCHED_RETURN_AND_METHOD_TYPE, return_statement -> expression_opt -> LeftToken(), return_statement -> expression_opt -> RightToken(), return_statement -> expression_opt -> Type() -> ContainingPackage() -> PackageName(), return_statement -> expression_opt -> Type() -> ExternalName(), this_method -> Type() -> ContainingPackage() -> PackageName(), this_method -> Type() -> ExternalName()); } } } else if (this_method -> Type() != control.void_type) { ReportSemError(SemanticError::MISPLACED_RETURN_WITH_NO_EXPRESSION, return_statement -> LeftToken(), return_statement -> RightToken()); } return; } bool Semantic::CatchableException(TypeSymbol *exception) { // // An unchecked exception or an error is ok !! // if (! CheckedException(exception) || exception == control.no_type) return true; // // Firstly, check the stack of try statements to see if the exception in question is catchable. // for (int i = TryStatementStack().Size() - 1; i >= 0; i--) { AstTryStatement *try_statement = (AstTryStatement *) TryStatementStack()[i]; // // If a try statement contains a finally clause that can complete abruptly // then any exception that can reach it is assumed to be catchable. // See Spec 11.3. // if (try_statement -> finally_clause_opt && (! try_statement -> finally_clause_opt -> block -> can_complete_normally)) return true; // // Check each catch clause in turn. // for (int k = 0; k < try_statement -> NumCatchClauses(); k++) { AstCatchClause *clause = try_statement -> CatchClause(k); VariableSymbol *symbol = clause -> parameter_symbol; if (CanAssignmentConvertReference(symbol -> Type(), exception)) return true; } } // // If we are processing the initialization expression of a field, // ThisMethod() is not defined. // MethodSymbol *this_method = ThisMethod(); if (this_method) { for (int l = this_method -> NumThrows() - 1; l >= 0; l--) { if (CanAssignmentConvertReference(this_method -> Throws(l), exception)) return true; } if (this_method -> NumInitializerConstructors() > 0) { int j; for (j = this_method -> NumInitializerConstructors() - 1; j >= 0; j--) { MethodSymbol *method = this_method -> InitializerConstructor(j); int k; for (k = method -> NumThrows() - 1; k >= 0; k--) { if (CanAssignmentConvertReference(method -> Throws(k), exception)) break; } if (k < 0) // no hit was found in method. return false; } if (j < 0) // all the relevant constructors can catch the exception return true; } } return false; } void Semantic::ProcessThrowStatement(Ast *stmt) { AstThrowStatement *throw_statement = (AstThrowStatement *) stmt; ProcessExpression(throw_statement -> expression); TypeSymbol *type = throw_statement -> expression -> Type(); if (type != control.no_type && (! CanAssignmentConvertReference(control.Throwable(), type))) { ReportSemError(SemanticError::EXPRESSION_NOT_THROWABLE, throw_statement -> LeftToken(), throw_statement -> RightToken()); } SymbolSet *exception_set = TryExceptionTableStack().Top(); if (exception_set) exception_set -> AddElement(type); if (! CatchableException(type)) { MethodSymbol *this_method = ThisMethod(); MethodSymbol *method = (this_method && this_method -> Identity() != control.clinit_name_symbol && this_method -> Identity() != control.block_init_name_symbol ? this_method : (MethodSymbol *) NULL); if (TryStatementStack().Size() > 0) ReportSemError(SemanticError::BAD_THROWABLE_EXPRESSION_IN_TRY, throw_statement -> LeftToken(), throw_statement -> RightToken(), type -> ContainingPackage() -> PackageName(), type -> ExternalName(), (method ? method -> Header() : StringConstant::US_EMPTY)); else if (method) ReportSemError(SemanticError::BAD_THROWABLE_EXPRESSION_IN_METHOD, throw_statement -> LeftToken(), throw_statement -> RightToken(), type -> ContainingPackage() -> PackageName(), type -> ExternalName(), method -> Header()); else ReportSemError(SemanticError::BAD_THROWABLE_EXPRESSION, throw_statement -> LeftToken(), throw_statement -> RightToken(), type -> ContainingPackage() -> PackageName(), type -> ExternalName()); } return; } void Semantic::ProcessTryStatement(Ast *stmt) { AstTryStatement *try_statement = (AstTryStatement *) stmt; // // A try_statement containing a finally clause requires some extra local // variables in its immediately enclosing block. If it is enclosed in a method // that returns void then 2 extra elements are needed. If the method // returns a long or a double value, two additional elements are needed. // Otherwise, one additional element is needed. // If this try_statement is the first try_statement with a finally clause // that was encountered in the immediately enclosing block, we allocate // two extra slots for the special local variables. // AstBlock *enclosing_block = LocalBlockStack().TopBlock(); if (try_statement -> finally_clause_opt) { BlockSymbol *block = enclosing_block -> block_symbol; if (block -> try_variable_index == 0) // first try_statement encountered in enclosing block? { block -> try_variable_index = block -> max_variable_index; block -> max_variable_index += 2; if (ThisMethod() -> Type() != control.void_type) { if (control.IsDoubleWordType(ThisMethod() -> Type())) block -> max_variable_index += 2; else block -> max_variable_index += 1; } } // // A finally block is processed in the environment of its immediate enclosing block. // (as opposed to the environment of its associated try block). // // Note that the finally block must be processed prior to the other // blocks in the try statement, because the computation of whether or not // an exception is catchable in a try statement depends on the termination // status of the associated finally block. See CatchableException function. // AstBlock *block_body = try_statement -> finally_clause_opt -> block; block_body -> is_reachable = try_statement -> is_reachable; ProcessBlock(block_body); } // // Note that the catch clauses are processed first - prior to processing // the main block - so that we can have their parameters available when we // are processing the main block, in case that block contains a throw // statement. See ProcessThrowStatement for more information. // // Also, recall that the body of the catch blocks must not be // processed within the environment of the associated try whose // exceptions they are supposed to catch but within the immediate enclosing // block (which may itself be a try block). // for (int i = 0; i < try_statement -> NumCatchClauses(); i++) { AstCatchClause *clause = try_statement -> CatchClause(i); AstFormalParameter *parameter = clause -> formal_parameter; TypeSymbol *parm_type; if (parameter -> type -> PrimitiveTypeCast()) { ReportSemError(SemanticError::CATCH_PRIMITIVE_TYPE, parameter -> LeftToken(), parameter -> RightToken()); parm_type = control.Error(); } else if (parameter -> type -> ArrayTypeCast()) { ReportSemError(SemanticError::CATCH_ARRAY_TYPE, parameter -> LeftToken(), parameter -> RightToken()); parm_type = control.Error(); } else parm_type = MustFindType(parameter -> type); if (! parm_type -> IsSubclass(control.Throwable())) { ReportSemError(SemanticError::TYPE_NOT_THROWABLE, parameter -> LeftToken(), parameter -> RightToken(), parm_type -> ContainingPackage() -> PackageName(), parm_type -> ExternalName()); } AstVariableDeclaratorId *name = parameter -> variable_declarator_name; NameSymbol *name_symbol = (NameSymbol *) lex_stream -> NameSymbol(name -> identifier_token); if (LocalSymbolTable().FindVariableSymbol(name_symbol)) { ReportSemError(SemanticError::DUPLICATE_LOCAL_VARIABLE_DECLARATION, name -> identifier_token, name -> identifier_token, name_symbol -> Name()); } AstBlock *block_body = clause -> block; // // Guess that the number of elements in the table will not exceed the number of statements + the clause parameter. // BlockSymbol *block = LocalSymbolTable().Top() -> InsertBlockSymbol(block_body -> NumStatements() + 1); block -> max_variable_index = enclosing_block -> block_symbol -> max_variable_index; LocalSymbolTable().Push(block -> Table()); if ((! control.option.one_one) && parameter -> NumParameterModifiers() > 0) { ReportSemError(SemanticError::ONE_ONE_FEATURE, parameter -> ParameterModifier(0) -> LeftToken(), parameter -> ParameterModifier(0) -> RightToken()); } AccessFlags access_flags = ProcessFormalModifiers(parameter); VariableSymbol *symbol = LocalSymbolTable().Top() -> InsertVariableSymbol(name_symbol); symbol -> SetFlags(access_flags); symbol -> SetType(parm_type); symbol -> SetOwner(ThisMethod()); symbol -> SetLocalVariableIndex(block -> max_variable_index++); symbol -> declarator = parameter -> variable_declarator_name; clause -> parameter_symbol = symbol; // // Note that for the purpose of semantic checking we assume that // the body of the catch block is reachable. Whether or not the catch // statement can be executed at all is checked later. // block_body -> is_reachable = true; block_body -> block_symbol = block; block_body -> nesting_level = LocalBlockStack().Size(); LocalBlockStack().Push(block_body); ProcessBlockStatements(block_body); LocalBlockStack().Pop(); LocalSymbolTable().Pop(); // // Update the information for the block that immediately encloses the current block. // if (LocalBlockStack().TopMaxEnclosedVariableIndex() < block -> max_variable_index) LocalBlockStack().TopMaxEnclosedVariableIndex() = block -> max_variable_index; // // If a catch clause block can complete normally, we assume // that the try statement can complete normally. This may // prove to be false later if we find out that the finally // clause cannot complete normally... // if (block_body -> can_complete_normally) try_statement -> can_complete_normally = true; block -> CompressSpace(); // space optimization } // // // TryStatementStack().Push(try_statement); SymbolSet *exception_set = new SymbolSet; TryExceptionTableStack().Push(exception_set); try_statement -> block -> is_reachable = try_statement -> is_reachable; ProcessBlock(try_statement -> block); if (try_statement -> block -> can_complete_normally) try_statement -> can_complete_normally = true; // // A catch block is reachable iff both of the following are true: // // . Some expression or throw statement in the try block is reachable // and can throw an exception that is assignable to the parameter // of the catch clause C. // // . There is no earlier catch block A in the try statement such that the // type of C's parameter is the same as or a subclass of the type of A's // parameter. // for (int l = 0; l < try_statement -> NumCatchClauses(); l++) { AstCatchClause *clause = try_statement -> CatchClause(l); VariableSymbol *symbol = clause -> parameter_symbol; if (CheckedException(symbol -> Type())) { TypeSymbol *exception; for (exception = (TypeSymbol *) exception_set -> FirstElement(); exception; exception = (TypeSymbol *) exception_set -> NextElement()) { if (CanAssignmentConvertReference(symbol -> Type(), exception) || CanAssignmentConvertReference(exception, symbol -> Type())) break; } AstCatchClause *previous_clause; int k; for (k = 0; k < l; k++) { previous_clause = try_statement -> CatchClause(k); if (symbol -> Type() -> IsSubclass(previous_clause -> parameter_symbol -> Type())) break; } if (! exception) // no assignable exception was found { ReportSemError(SemanticError::UNREACHABLE_CATCH_CLAUSE, clause -> formal_parameter -> LeftToken(), clause -> formal_parameter -> RightToken(), symbol -> Type() -> ContainingPackage() -> PackageName(), symbol -> Type() -> ExternalName()); } else if (k < l) { FileLocation loc(lex_stream, previous_clause -> formal_parameter -> variable_declarator_name -> identifier_token); ReportSemError(SemanticError::BLOCKED_CATCH_CLAUSE, clause -> formal_parameter -> LeftToken(), clause -> formal_parameter -> RightToken(), symbol -> Type() -> ContainingPackage() -> PackageName(), symbol -> Type() -> ExternalName(), loc.location); } else clause -> block -> is_reachable = true; } } TryStatementStack().Pop(); TryExceptionTableStack().Pop(); if (TryExceptionTableStack().Top()) TryExceptionTableStack().Top() -> Union(*exception_set); delete exception_set; // // A try statement cannot complete normally if it contains a finally // clause that cannot complete normally. // if (try_statement -> finally_clause_opt && (! try_statement -> finally_clause_opt -> block -> can_complete_normally)) try_statement -> can_complete_normally = false; return; } void Semantic::ProcessEmptyStatement(Ast *stmt) { AstEmptyStatement *empty_statement = (AstEmptyStatement *) stmt; // // An empty statement can complete normally iff it is reachable. // empty_statement -> can_complete_normally = empty_statement -> is_reachable; return; } TypeSymbol *Semantic::GetLocalType(AstClassDeclaration *class_declaration) { NameSymbol *name_symbol = (NameSymbol *) lex_stream -> NameSymbol(class_declaration -> identifier_token); TypeSymbol *type = LocalSymbolTable().Top() -> InsertNestedTypeSymbol(name_symbol); TypeSymbol *outermost_type = ThisType() -> outermost_type; if (! outermost_type -> local) outermost_type -> local = new SymbolSet; int num = outermost_type -> local -> NameCount(name_symbol) + 1; wchar_t str[11], *p = &str[10]; *p = U_NULL; do { p--; *p = U_0 + (num % 10); num /= 10; } while (num > 0); int length = wcslen(p) + outermost_type -> NameLength() + 1 + name_symbol -> NameLength() + 1; // +1 for $,... +1 for $ wchar_t *external_name = new wchar_t[length + 1]; // +1 for '\0'; wcscpy(external_name, outermost_type -> Name()); wcscat(external_name, StringConstant::US__DS_); wcscat(external_name, p); wcscat(external_name, StringConstant::US__DS_); wcscat(external_name, name_symbol -> Name()); type -> SetACC_PRIVATE(); type -> outermost_type = outermost_type; type -> SetExternalIdentity(control.FindOrInsertName(external_name, length)); outermost_type -> local -> AddElement(type); delete [] external_name; return type; } void Semantic::ProcessClassDeclaration(Ast *stmt) { AstClassDeclaration *class_declaration = (AstClassDeclaration *) stmt; AstClassBody *class_body = class_declaration -> class_body; class_declaration -> MarkLocal(); // identify class as "statement" and assert that it is "reachable" and "can_complete_normally" if (! control.option.one_one) { ReportSemError(SemanticError::ONE_ONE_FEATURE, class_declaration -> LeftToken(), class_declaration -> RightToken()); } CheckNestedTypeDuplication(state_stack.Top(), class_declaration -> identifier_token); NameSymbol *name_symbol = (NameSymbol *) lex_stream -> NameSymbol(class_declaration -> identifier_token); TypeSymbol *inner_type = GetLocalType(class_declaration); inner_type -> outermost_type = ThisType() -> outermost_type; inner_type -> supertypes_closure = new SymbolSet; inner_type -> subtypes_closure = new SymbolSet; inner_type -> subtypes = new SymbolSet; inner_type -> semantic_environment = new SemanticEnvironment((Semantic *) this, inner_type, state_stack.Top()); inner_type -> declaration = class_declaration; inner_type -> file_symbol = source_file_symbol; inner_type -> SetFlags(ProcessLocalClassModifiers(class_declaration)); inner_type -> SetOwner(ThisMethod()); // // Add 3 extra elements for padding. May need a default constructor and other support elements. // inner_type -> SetSymbolTable(class_body -> NumClassBodyDeclarations() + 3); inner_type -> SetLocation(); inner_type -> SetSignature(control); if (StaticRegion()) inner_type -> SetACC_STATIC(); else inner_type -> InsertThis(0); class_declaration -> semantic_environment = inner_type -> semantic_environment; // save for processing bodies later. CheckClassMembers(inner_type, class_body); ProcessTypeHeaders(class_declaration); ProcessMembers(class_declaration -> semantic_environment, class_body); CompleteSymbolTable(class_declaration -> semantic_environment, class_declaration -> identifier_token, class_body); ProcessExecutableBodies(class_declaration -> semantic_environment, class_body); UpdateLocalConstructors(inner_type); return; } void Semantic::ProcessThisCall(AstThisCall *this_call) { TypeSymbol *this_type = ThisType(), *containing_type = this_type -> ContainingType(); ExplicitConstructorInvocation() = this_call; // signal that we are about to process an explicit constructor invocation if (this_call -> base_opt) { if (! control.option.one_one) { ReportSemError(SemanticError::ONE_ONE_FEATURE, this_call -> base_opt -> LeftToken(), this_call -> dot_token_opt); } ProcessExpression(this_call -> base_opt); TypeSymbol *expr_type = this_call -> base_opt -> Type(); if (expr_type != control.no_type) { if (! containing_type) { ReportSemError(SemanticError::TYPE_NOT_INNER_CLASS, this_call -> base_opt -> LeftToken(), this_call -> base_opt -> RightToken(), this_type -> ContainingPackage() -> PackageName(), this_type -> ExternalName(), expr_type -> ContainingPackage() -> PackageName(), expr_type -> ExternalName()); this_call -> base_opt -> symbol = control.no_type; } // // 1.2 change. In 1.1, we used to allow access to any subclass of type. Now, there must // be a perfect match. // // else if (! expr_type -> IsSubclass(containing_type)) // else if (expr_type != containing_type) { ReportSemError(SemanticError::INVALID_ENCLOSING_INSTANCE, this_call -> base_opt -> LeftToken(), this_call -> base_opt -> RightToken(), this_type -> ContainingPackage() -> PackageName(), this_type -> ExternalName(), containing_type -> ContainingPackage() -> PackageName(), containing_type -> ExternalName(), expr_type -> ContainingPackage() -> PackageName(), expr_type -> ExternalName()); this_call -> base_opt -> symbol = control.no_type; } } } else // (! super_call -> base_opt) { if (this_type -> IsInner()) this_call -> base_opt = CreateAccessToType(this_call, containing_type); } bool no_bad_argument = true; for (int i = 0; i < this_call -> NumArguments(); i++) { AstExpression *expr = (AstExpression *) this_call -> Argument(i); ProcessExpressionOrStringConstant(expr); no_bad_argument = no_bad_argument && (expr -> Type() != control.no_type); } if (no_bad_argument) { MethodSymbol *constructor = FindConstructor(this_type, this_call, this_call -> LeftToken(), this_call -> RightToken()); if (constructor) { this_call -> symbol = constructor; for (int i = 0; i < this_call -> NumArguments(); i++) { AstExpression *expr = this_call -> Argument(i); if (expr -> Type() != constructor -> FormalParameter(i) -> Type()) this_call -> Argument(i) = ConvertToType(expr, constructor -> FormalParameter(i) -> Type()); } for (int k = constructor -> NumThrows() - 1; k >= 0; k--) { TypeSymbol *exception = constructor -> Throws(k); if (! CatchableException(exception)) { ReportSemError(SemanticError::CONSTRUCTOR_DOES_NOT_THROW_THIS_EXCEPTION, this_call -> this_token, this_call -> this_token, exception -> ContainingPackage() -> PackageName(), exception -> ExternalName()); } } if (this_type -> IsLocal()) // a local type may use enclosed local variables? this_type -> AddLocalConstructorCallEnvironment(GetEnvironment(this_call)); // // Note that there is no need to do access-checking as we are allowed, // within the body of a class, to invoke any other constructor or member // (private or otherwise) in that class. // } } ExplicitConstructorInvocation() = NULL; // signal that we are no longer processing an explicit constructor invocation this_call -> can_complete_normally = this_call -> is_reachable; return; } void Semantic::ProcessSuperCall(AstSuperCall *super_call) { TypeSymbol *this_type = ThisType(); ExplicitConstructorInvocation() = super_call; // signal that we are about to process an explicit constructor invocation TypeSymbol *super_type = this_type -> super; if (! super_type) // this is only possible if we are compiling an illegal Object.java source file. super_type = control.Object(); if (super_call -> base_opt) { if (! control.option.one_one) { ReportSemError(SemanticError::ONE_ONE_FEATURE, super_call -> base_opt -> LeftToken(), super_call -> dot_token_opt); } ProcessExpression(super_call -> base_opt); TypeSymbol *expr_type = super_call -> base_opt -> Type(); if (expr_type != control.no_type) { TypeSymbol *containing_type = super_type -> ContainingType(); if (! containing_type) { ReportSemError(SemanticError::SUPER_TYPE_NOT_INNER_CLASS, super_call -> base_opt -> LeftToken(), super_call -> base_opt -> RightToken(), super_type -> ContainingPackage() -> PackageName(), super_type -> ExternalName(), this_type -> ContainingPackage() -> PackageName(), this_type -> ExternalName(), expr_type -> ContainingPackage() -> PackageName(), expr_type -> ExternalName()); super_call -> base_opt -> symbol = control.no_type; } // // 1.2 change. In 1.1, we used to allow access to any subclass of type. Now, there must // be a perfect match. // // else if (! expr_type -> IsSubclass(containing_type)) // else if (expr_type != containing_type) { ReportSemError(SemanticError::INVALID_ENCLOSING_INSTANCE, super_call -> base_opt -> LeftToken(), super_call -> base_opt -> RightToken(), this_type -> ContainingPackage() -> PackageName(), this_type -> ExternalName(), containing_type -> ContainingPackage() -> PackageName(), containing_type -> ExternalName(), expr_type -> ContainingPackage() -> PackageName(), expr_type -> ExternalName()); super_call -> base_opt -> symbol = control.no_type; } } } else // (! super_call -> base_opt) { if (super_type && super_type -> IsInner()) super_call -> base_opt = CreateAccessToType(super_call, super_type -> ContainingType()); } bool no_bad_argument = true; for (int i = 0; i < super_call -> NumArguments(); i++) { AstExpression *expr = (AstExpression *) super_call -> Argument(i); ProcessExpressionOrStringConstant(expr); no_bad_argument = no_bad_argument && (expr -> Type() != control.no_type); } if (this_type == control.Object()) { super_call -> symbol = NULL; ReportSemError(SemanticError::MISPLACED_SUPER_EXPRESSION, super_call -> super_token, super_call -> super_token); } else if (no_bad_argument) { MethodSymbol *constructor = FindConstructor(super_type, super_call, super_call -> LeftToken(), super_call -> RightToken()); if (constructor) { // // No need to do a full access-check. Do the minimal stuff here ! // if (constructor -> ACC_PRIVATE()) { if (this_type -> outermost_type == super_type -> outermost_type) { // // TODO: Awaiting language clarification. // ReportSemError(SemanticError::PRIVATE_ENCLOSED_CONSTRUCTOR, super_call -> LeftToken(), super_call -> RightToken(), constructor -> Header()); constructor = TypeSymbol::GetReadAccessMethod(constructor); } else ReportSemError(SemanticError::PRIVATE_CONSTRUCTOR_NOT_ACCESSIBLE, super_call -> LeftToken(), super_call -> RightToken(), constructor -> Header(), super_type -> ContainingPackage() -> PackageName(), super_type -> ExternalName()); } else if (! (constructor -> ACC_PUBLIC() || constructor -> ACC_PROTECTED())) { if (! (this_type -> outermost_type == super_type -> outermost_type || super_type -> ContainingPackage() == this_package)) ReportSemError(SemanticError::DEFAULT_CONSTRUCTOR_NOT_ACCESSIBLE, super_call -> super_token, super_call -> super_token, constructor -> Header(), super_type -> ContainingPackage() -> PackageName(), super_type -> ExternalName()); } super_call -> symbol = constructor; if (super_call -> base_opt && (super_call -> base_opt -> Type() != control.no_type) && (super_call -> base_opt -> Type() != super_type -> ContainingType())) { assert(CanMethodInvocationConvert(super_type -> ContainingType(), super_call -> base_opt -> Type())); super_call -> base_opt = ConvertToType(super_call -> base_opt, super_type -> ContainingType()); } for (int i = 0; i < super_call -> NumArguments(); i++) { AstExpression *expr = super_call -> Argument(i); if (expr -> Type() != constructor -> FormalParameter(i) -> Type()) super_call -> Argument(i) = ConvertToType(expr, constructor -> FormalParameter(i) -> Type()); } for (int k = constructor -> NumThrows((Semantic *) this, super_call -> super_token) - 1; k >= 0; k--) { TypeSymbol *exception = constructor -> Throws(k); if (! CatchableException(exception)) { ReportSemError(SemanticError::CONSTRUCTOR_DOES_NOT_THROW_SUPER_EXCEPTION, super_call -> LeftToken(), super_call -> RightToken(), this_type -> Name(), exception -> ContainingPackage() -> PackageName(), exception -> ExternalName(), constructor -> containing_type -> ContainingPackage() -> PackageName(), constructor -> containing_type -> ExternalName()); } } if (super_type -> IsLocal()) // a local type may use enclosed local variables? { if (super_type -> LocalClassProcessingCompleted()) { for (int i = 1; i < super_type -> NumConstructorParameters(); i++) { VariableSymbol *local = super_type -> ConstructorParameter(i) -> accessed_local; AstSimpleName *simple_name = compilation_unit -> ast_pool -> GenSimpleName(super_call -> super_token); this_type -> FindOrInsertLocalShadow(local); assert(ThisMethod() -> LocalConstructor() && (! ThisMethod() -> IsGeneratedLocalConstructor())); BlockSymbol *block_symbol = ThisMethod() -> LocalConstructor() -> block_symbol; simple_name -> symbol = block_symbol -> FindVariableSymbol(local -> Identity()); assert(simple_name -> symbol); // for the time being, we will now process sources with the error below... // // This is possible if the source contains a method that erroneously calls a super class. // if (! simple_name -> symbol) simple_name -> symbol = control.no_type; super_call -> AddLocalArgument(simple_name); } assert(constructor -> LocalConstructor() && (! constructor -> IsGeneratedLocalConstructor())); super_call -> symbol = constructor -> LocalConstructor(); } else // are we currently within the body of the type in question ? { super_type -> AddLocalConstructorCallEnvironment(GetEnvironment(super_call)); } } } } ExplicitConstructorInvocation() = NULL; // signal that we are no longer processing an explicit constructor invocation super_call -> can_complete_normally = super_call -> is_reachable; return; } void Semantic::CheckThrow(AstExpression *throw_expression) { TypeSymbol *throw_type = throw_expression -> symbol -> TypeCast(); assert(throw_type); if (throw_type -> ACC_INTERFACE()) { ReportSemError(SemanticError::NOT_A_CLASS, throw_expression -> LeftToken(), throw_expression -> RightToken(), throw_type -> ContainingPackage() -> PackageName(), throw_type -> ExternalName()); } else if (! throw_type -> IsSubclass(control.Throwable())) { ReportSemError(SemanticError::TYPE_NOT_THROWABLE, throw_expression -> LeftToken(), throw_expression -> RightToken(), throw_type -> ContainingPackage() -> PackageName(), throw_type -> ExternalName()); } return; } void Semantic::ProcessMethodBody(AstMethodDeclaration *method_declaration) { MethodSymbol *this_method = ThisMethod(); for (int k = 0; k < method_declaration -> NumThrows(); k++) CheckThrow(method_declaration -> Throw(k)); AstMethodDeclarator *method_declarator = method_declaration -> method_declarator; if (! method_declaration -> method_body -> EmptyStatementCast()) { // // The block that is the body of a method is reachable // AstBlock *block_body; // // The body of a method must be a regular block. If instead, it // is a constructor block, mark the compilation unit as a bad // compilation so that the parser can properly diagnose this // problem later. // AstConstructorBlock *constructor_block = method_declaration -> method_body -> ConstructorBlockCast(); if (constructor_block) { compilation_unit -> kind = Ast::BAD_COMPILATION; // invalidate the compilation unit constructor_block -> is_reachable = true; block_body = constructor_block -> block; // // If the parser recognizes the body of a method as a ConstructorBlock // then it must have an explicit_constructor_invocation. // AstThisCall *this_call = constructor_block -> explicit_constructor_invocation_opt -> ThisCallCast(); if (this_call) { this_call -> is_reachable = true; // // Do not process the explicit constructor invocation as this could // cause problems with assertions (e.g. for inner classes) that will // turn out not to be true. // // ProcessThisCall(this_call); // block_body -> is_reachable = this_call -> can_complete_normally; } else { AstSuperCall *super_call = (AstSuperCall *) constructor_block -> explicit_constructor_invocation_opt; super_call -> is_reachable = true; // // Do not process the explicit constructor invocation as this could // cause problems with assertions (e.g. for inner classes) that will // turn out not to be true. // // ProcessSuperCall(super_call); // block_body -> is_reachable = super_call -> can_complete_normally; } } else { block_body = (AstBlock *) method_declaration -> method_body; block_body -> is_reachable = true; } block_body -> block_symbol = this_method -> block_symbol; block_body -> nesting_level = LocalBlockStack().Size(); LocalBlockStack().Push(block_body); ProcessBlockStatements(block_body); LocalBlockStack().Pop(); if (block_body -> can_complete_normally) { if (this_method -> Type() == control.void_type) { AstReturnStatement *return_statement = compilation_unit -> ast_pool -> GenReturnStatement(); return_statement -> return_token = block_body -> right_brace_token; return_statement -> expression_opt = NULL; return_statement -> semicolon_token = block_body -> right_brace_token; return_statement -> is_reachable = true; block_body -> can_complete_normally = false; block_body -> AddStatement(return_statement); } else { ReportSemError(SemanticError::TYPED_METHOD_WITH_NO_RETURN, method_declaration -> type -> LeftToken(), method_declaration -> method_declarator -> identifier_token, this_method -> Header(), this_method -> Type() -> Name()); } } if (this_method -> ACC_ABSTRACT() || this_method -> ACC_NATIVE()) { ReportSemError(SemanticError::ABSTRACT_METHOD_WITH_BODY, method_declaration -> LeftToken(), method_declaration -> RightToken(), this_method -> Header()); } } else if (! (this_method -> ACC_ABSTRACT() || this_method -> ACC_NATIVE())) { ReportSemError(SemanticError::NON_ABSTRACT_METHOD_WITHOUT_BODY, method_declaration -> LeftToken(), method_declaration -> RightToken(), this_method -> Header()); } this_method -> block_symbol -> CompressSpace(); // space optimization return; } void Semantic::ProcessConstructorBody(AstConstructorDeclaration *constructor_declaration, bool body_reachable) { TypeSymbol *this_type = ThisType(); MethodSymbol *this_method = ThisMethod(); for (int k = 0; k < constructor_declaration -> NumThrows(); k++) CheckThrow(constructor_declaration -> Throw(k)); AstMethodDeclarator *constructor_declarator = constructor_declaration -> constructor_declarator; // // The block that is the body of a constructor is reachable // AstConstructorBlock *constructor_block = constructor_declaration -> constructor_body; constructor_block -> is_reachable = true; AstBlock *block_body = constructor_block -> block; AstThisCall *this_call = NULL; AstSuperCall *super_call = NULL; TypeSymbol *super_type = this_type -> super; if (constructor_block -> explicit_constructor_invocation_opt) { this_call = constructor_block -> explicit_constructor_invocation_opt -> ThisCallCast(); super_call = constructor_block -> explicit_constructor_invocation_opt -> SuperCallCast(); } else if (super_type) { LexStream::TokenIndex loc = block_body -> LeftToken(); super_call = compilation_unit -> ast_pool -> GenSuperCall(); super_call -> base_opt = NULL; super_call -> dot_token_opt = loc; super_call -> super_token = loc; super_call -> left_parenthesis_token = loc; super_call -> right_parenthesis_token = loc; super_call -> semicolon_token = loc; constructor_block -> explicit_constructor_invocation_opt = super_call; if (super_type -> IsInner() && (! this_type -> CanAccess(super_type -> ContainingType()))) { ReportSemError(SemanticError::ENCLOSING_INSTANCE_NOT_ACCESSIBLE, constructor_declaration -> constructor_declarator -> LeftToken(), constructor_declaration -> constructor_declarator -> RightToken(), super_type -> ContainingType() -> ContainingPackage() -> PackageName(), super_type -> ContainingType() -> ExternalName()); } } // // If the constructor starts with an explicit_constructor_invocation, either // one specified by the user or generated, we process it and set up the proper // local environment, if appropriate... // if (constructor_block -> explicit_constructor_invocation_opt) { // // If we are processing a local constructor, set up the generated environment... // if (this_type -> IsLocal()) { LocalSymbolTable().Pop(); assert(this_method -> LocalConstructor() && (! this_method -> IsGeneratedLocalConstructor())); LocalSymbolTable().Push(this_method -> LocalConstructor() -> block_symbol -> Table()); } if (this_call) { this_call -> is_reachable = true; ProcessThisCall(this_call); } else { assert(super_call); super_call -> is_reachable = true; ProcessSuperCall(super_call); } // // If we are processing a local constructor, restore its original environment... // if (this_type -> IsLocal()) { LocalSymbolTable().Pop(); LocalSymbolTable().Push(this_method -> block_symbol -> Table()); } } if (! (body_reachable || (constructor_block -> explicit_constructor_invocation_opt && constructor_block -> explicit_constructor_invocation_opt -> ThisCallCast()))) { ReportSemError(SemanticError::UNREACHABLE_CONSTRUCTOR_BODY, constructor_declaration -> LeftToken(), constructor_declaration -> RightToken()); } // // Guess that the number of elements will not exceed the number of statements. // int table_size = block_body -> NumStatements(); BlockSymbol *block = LocalSymbolTable().Top() -> InsertBlockSymbol(table_size); // // enclosing_block is not present only when we are processing the block of a static initializer // block -> max_variable_index = this_method -> block_symbol -> max_variable_index; LocalSymbolTable().Push(block -> Table()); block_body -> is_reachable = true; block_body -> block_symbol = block; block_body -> nesting_level = LocalBlockStack().Size(); LocalBlockStack().Push(block_body); ProcessBlockStatements(block_body); if (block_body -> can_complete_normally) { AstReturnStatement *return_statement = compilation_unit -> ast_pool -> GenReturnStatement(); return_statement -> return_token = block_body -> right_brace_token; return_statement -> expression_opt = NULL; return_statement -> semicolon_token = block_body -> right_brace_token; return_statement -> is_reachable = true; block_body -> can_complete_normally = false; block_body -> AddStatement(return_statement); } constructor_block -> can_complete_normally = block_body -> can_complete_normally; LocalBlockStack().Pop(); LocalSymbolTable().Pop(); block -> CompressSpace(); // space optimization return; } void Semantic::ProcessExecutableBodies(SemanticEnvironment *environment, AstClassBody *class_body) { state_stack.Push(environment); TypeSymbol *this_type = ThisType(); assert(this_type -> HeaderProcessed()); assert(this_type -> ConstructorMembersProcessed()); assert(this_type -> MethodMembersProcessed()); assert(this_type -> FieldMembersProcessed()); ThisVariable() = NULL; // All variable declarations have already been processed // // Compute the set of instance final variables declared by the user in this type // as well as the set of instance final variables that have not yet been initialized. // Tuple<VariableSymbol *> finals(this_type -> NumVariableSymbols()), unassigned_finals(this_type -> NumVariableSymbols()); for (int k = 0; k < this_type -> NumVariableSymbols(); k++) { VariableSymbol *variable_symbol = this_type -> VariableSym(k); if (variable_symbol -> ACC_FINAL() && variable_symbol -> declarator) { finals.Next() = variable_symbol; if (! variable_symbol -> IsDefinitelyAssigned()) unassigned_finals.Next() = variable_symbol; } } AstBlock *last_block_body = (class_body -> NumBlocks() > 0 ? class_body -> Block(class_body -> NumBlocks() - 1) : (AstBlock *) NULL); if (class_body -> NumConstructors() == 0) { // // Issue an error for each unassigned final. // for (int k = 0; k < unassigned_finals.Length(); k++) { ReportSemError(SemanticError::UNINITIALIZED_FINAL_VARIABLE, unassigned_finals[k] -> declarator -> LeftToken(), unassigned_finals[k] -> declarator -> RightToken()); } // // Process the body of the default constructor, if there is one. // (An anonymous class does not yet have a default constructor at this point.) // AstConstructorDeclaration *constructor_decl = class_body -> default_constructor; if (constructor_decl) { ThisMethod() = constructor_decl -> constructor_symbol; LocalSymbolTable().Push(ThisMethod() -> block_symbol -> Table()); LocalBlockStack().max_size = 0; ProcessConstructorBody(constructor_decl, ((! last_block_body) || last_block_body -> can_complete_normally)); LocalSymbolTable().Pop(); ThisMethod() -> max_block_depth = LocalBlockStack().max_size; } } else { for (int i = 0; i < class_body -> NumConstructors(); i++) { AstConstructorDeclaration *constructor_decl = class_body -> Constructor(i); ThisMethod() = constructor_decl -> constructor_symbol; MethodSymbol *this_method = ThisMethod(); if (this_method) { for (int l = 0; l < this_method -> NumFormalParameters(); l++) { VariableSymbol *parm = this_method -> FormalParameter(l); AstVariableDeclaratorId *name = (AstVariableDeclaratorId *) parm -> declarator; SemanticEnvironment *where_found; Tuple<VariableSymbol *> variables_found(2); SearchForVariableInEnvironment(variables_found, where_found, state_stack.Top(), parm -> Identity(), name -> identifier_token); VariableSymbol *symbol = (variables_found.Length() > 0 ? variables_found[0] : (VariableSymbol *) NULL); if (symbol && symbol -> IsLocal()) { ReportSemError(SemanticError::DUPLICATE_LOCAL_VARIABLE_DECLARATION, name -> identifier_token, name -> identifier_token, parm -> Name()); } } AstConstructorBlock *constructor_block = constructor_decl -> constructor_body; if (constructor_block -> explicit_constructor_invocation_opt && constructor_block -> explicit_constructor_invocation_opt -> ThisCallCast()) { for (int j = 0; j < unassigned_finals.Length(); j++) unassigned_finals[j] -> MarkDefinitelyAssigned(); } else { for (int j = 0; j < unassigned_finals.Length(); j++) unassigned_finals[j] -> MarkNotDefinitelyAssigned(); } LocalSymbolTable().Push(this_method -> block_symbol -> Table()); LocalBlockStack().max_size = 0; int start_num_errors = NumErrors(); ProcessConstructorBody(constructor_decl, ((! last_block_body) || last_block_body -> can_complete_normally)); LocalSymbolTable().Pop(); this_method -> max_block_depth = LocalBlockStack().max_size; if (NumErrors() == start_num_errors) DefiniteConstructorBody(constructor_decl, finals); for (int k = 0; k < unassigned_finals.Length(); k++) { VariableSymbol *variable_symbol = unassigned_finals[k]; if (! variable_symbol -> IsDefinitelyAssigned()) { ReportSemError(SemanticError::UNINITIALIZED_FINAL_VARIABLE_IN_CONSTRUCTOR, constructor_decl -> LeftToken(), constructor_decl -> RightToken(), variable_symbol -> Name()); } } } } for (int l = 0; l < this_type -> NumPrivateAccessConstructors(); l++) { ThisMethod() = this_type -> PrivateAccessConstructor(l); MethodSymbol *this_method = ThisMethod(); AstConstructorDeclaration *constructor_decl = (AstConstructorDeclaration *) this_method -> method_or_constructor_declaration; LocalSymbolTable().Push(this_method -> block_symbol -> Table()); LocalBlockStack().max_size = 0; ProcessConstructorBody(constructor_decl, true); LocalSymbolTable().Pop(); this_method -> max_block_depth = LocalBlockStack().max_size; } ConstructorCycleChecker cycle_checker(class_body); } for (int j = 0; j < class_body -> NumMethods(); j++) { AstMethodDeclaration *method_decl = class_body -> Method(j); ThisMethod() = method_decl -> method_symbol; MethodSymbol *this_method = ThisMethod(); if (this_method) { // // TODO: Confirm that this new test is indeed necessary. In 1.0, a more restricted test was used... // for (int i = 0; i < this_method -> NumFormalParameters(); i++) { VariableSymbol *parm = this_method -> FormalParameter(i); AstVariableDeclaratorId *name = (AstVariableDeclaratorId *) parm -> declarator; SemanticEnvironment *where_found; Tuple<VariableSymbol *> variables_found(2); SearchForVariableInEnvironment(variables_found, where_found, state_stack.Top(), parm -> Identity(), name -> identifier_token); VariableSymbol *symbol = (variables_found.Length() > 0 ? variables_found[0] : (VariableSymbol *) NULL); if (symbol && symbol -> IsLocal()) { ReportSemError(SemanticError::DUPLICATE_LOCAL_VARIABLE_DECLARATION, name -> identifier_token, name -> identifier_token, parm -> Name()); } } LocalSymbolTable().Push(this_method -> block_symbol -> Table()); LocalBlockStack().max_size = 0; int start_num_errors = NumErrors(); ProcessMethodBody(method_decl); LocalSymbolTable().Pop(); this_method -> max_block_depth = LocalBlockStack().max_size; if (NumErrors() == start_num_errors) DefiniteMethodBody(method_decl, finals); } } // // Mark all instance variables and constructor parameters final. // for (int i = 0; i < this_type -> NumConstructorParameters(); i++) this_type -> ConstructorParameter(i) -> SetACC_FINAL(); for (int l = 0; l < this_type -> NumEnclosingInstances(); l++) this_type -> EnclosingInstance(l) -> SetACC_FINAL(); // // We are done with all the methods, indicate that there is no method // being currently compiled in this environment. // ThisMethod() = NULL; // // Recursively process all inner types // for (int m = 0; m < class_body -> NumNestedClasses(); m++) { AstClassDeclaration *class_declaration = class_body -> NestedClass(m); if (class_declaration -> semantic_environment) ProcessExecutableBodies(class_declaration -> semantic_environment, class_declaration -> class_body); } for (int n = 0; n < class_body -> NumNestedInterfaces(); n++) { if (class_body -> NestedInterface(n) -> semantic_environment) ProcessExecutableBodies(class_body -> NestedInterface(n)); } state_stack.Pop(); return; } void Semantic::ProcessExecutableBodies(AstInterfaceDeclaration *interface_declaration) { state_stack.Push(interface_declaration -> semantic_environment); TypeSymbol *this_type = ThisType(); assert(this_type -> HeaderProcessed()); assert(this_type -> MethodMembersProcessed()); assert(this_type -> FieldMembersProcessed()); for (int k = 0; k < this_type -> NumVariableSymbols(); k++) { VariableSymbol *variable_symbol = this_type -> VariableSym(k); if (! variable_symbol -> IsDefinitelyAssigned()) { ReportSemError(SemanticError::UNINITIALIZED_FINAL_VARIABLE, variable_symbol -> declarator -> LeftToken(), variable_symbol -> declarator -> RightToken()); } } // // Recursively process all inner types // for (int m = 0; m < interface_declaration -> NumNestedClasses(); m++) { AstClassDeclaration *class_declaration = interface_declaration -> NestedClass(m); if (class_declaration -> semantic_environment) ProcessExecutableBodies(class_declaration -> semantic_environment, class_declaration -> class_body); } for (int n = 0; n < interface_declaration -> NumNestedInterfaces(); n++) { if (interface_declaration -> NestedInterface(n) -> semantic_environment) ProcessExecutableBodies(interface_declaration -> NestedInterface(n)); } state_stack.Pop(); return; }