// ================================================================= // Performs finalization on this node. // // TODO: Move this into semant, we only have it in here because // if we do a checkAccess in semant we will get a null // reference exception if we are still assinging this // nodes parents (in the case of implicit boxing) // // ================================================================= CASTNode* CNewExpressionASTNode::Finalize(CSemanter* semanter) { // Grab arguments. std::vector<CDataType*> argument_datatypes; for (auto iter = ArgumentExpressions.begin(); iter != ArgumentExpressions.end(); iter++) { CExpressionBaseASTNode* node = dynamic_cast<CExpressionBaseASTNode*>(*iter); argument_datatypes.push_back(node->ExpressionResultType); } // Create new object. if (dynamic_cast<CArrayDataType*>(DataType) == NULL) { // Check class is valid. CClassASTNode* classNode = DataType->GetClass(semanter); // Check we can find a constructor. CClassMemberASTNode* node = classNode->FindClassMethod(semanter, classNode->Identifier, argument_datatypes, false); if (node == NULL) { semanter->GetContext()->FatalError(CStringHelper::FormatString("Could not find suitable constructor to instantiate class '%s'.", DataType->ToString().c_str()), Token); } // Now to do the actual finalization - checking if access is valid! node->CheckAccess(semanter, this); } return this; }
// ================================================================= // Performs semantic analysis on this node. // ================================================================= CASTNode* CMethodCallExpressionASTNode::Semant(CSemanter* semanter) { SEMANT_TRACE("CMethodCallExpressionASTNode"); // Only semant once. if (Semanted == true) { return this; } Semanted = true; // Get expression representations. CExpressionBaseASTNode* left_hand_expr = dynamic_cast<CExpressionBaseASTNode*>(LeftValue); CExpressionBaseASTNode* right_hand_expr = dynamic_cast<CExpressionBaseASTNode*>(RightValue); // Semant left hand node. LeftValue = ReplaceChild(LeftValue, LeftValue->Semant(semanter)); // Make sure we can access class. CClassASTNode* accessClass = left_hand_expr->ExpressionResultType->GetClass(semanter); if (accessClass == NULL) { semanter->GetContext()->FatalError(CStringHelper::FormatString("Invalid use of scoping operator."), Token); } // Check we can access this class from here. accessClass->CheckAccess(semanter, this); // NOTE: Do not r-value semant identifier, we want to process that ourselves. CIdentifierExpressionASTNode* identNode = dynamic_cast<CIdentifierExpressionASTNode*>(RightValue); // Semant arguments. std::vector<CDataType*> argument_types; std::string argument_types_string; for (std::vector<CASTNode*>::iterator iter = ArgumentExpressions.begin(); iter < ArgumentExpressions.end(); iter++) { CExpressionBaseASTNode* node = dynamic_cast<CExpressionBaseASTNode*>((*iter)->Semant(semanter)); argument_types.push_back(node->ExpressionResultType); if (iter != ArgumentExpressions.begin()) { argument_types_string += ", "; } argument_types_string += node->ExpressionResultType->ToString(); (*iter) = node; } // Make sure the identifier represents a valid field. CClassMemberASTNode* declaration = accessClass->FindClassMethod(semanter, identNode->Token.Literal, argument_types, false, NULL, this); if (declaration == NULL) { semanter->GetContext()->FatalError(CStringHelper::FormatString("Undefined method '%s(%s)' in class '%s'.", identNode->Token.Literal.c_str(), argument_types_string.c_str(), accessClass->ToString().c_str()), Token); } // UPDATE: Abstract method calling is fine. Remember we won't be able to instantiate classes that do not override all abstract methods. // if (declaration->IsAbstract == true) // { // semanter->GetContext()->FatalError(CStringHelper::FormatString("Cannot call method '%s(%s)' in class '%s', method is abstract.", identNode->Token.Literal.c_str(), argument_types_string.c_str(), accessClass->ToString().c_str()), Token); // } ResolvedDeclaration = declaration; // Check we can access this field from here. declaration->CheckAccess(semanter, this); // HACK: This is really hackish and needs fixing! if (dynamic_cast<CThisExpressionASTNode*>(LeftValue) != NULL && declaration->IsStatic == true) { LeftValue = ReplaceChild(LeftValue, new CClassRefExpressionASTNode(NULL, Token)); LeftValue->Token.Literal = declaration->FindClassScope(semanter)->Identifier; LeftValue->Semant(semanter); left_hand_expr = dynamic_cast<CExpressionBaseASTNode*>(LeftValue); } // Add default arguments if we do not have enough args to call. if (declaration->Arguments.size() > ArgumentExpressions.size()) { for (unsigned int i = ArgumentExpressions.size(); i < declaration->Arguments.size() ; i++) { CASTNode* expr = declaration->Arguments.at(i)->AssignmentExpression->Clone(semanter); AddChild(expr); ArgumentExpressions.push_back(expr); expr->Semant(semanter); } } // Cast all arguments to correct data types. int index = 0; for (std::vector<CASTNode*>::iterator iter = ArgumentExpressions.begin(); iter != ArgumentExpressions.end(); iter++) { CDataType* dataType = declaration->Arguments.at(index++)->Type; CExpressionBaseASTNode* subnode = dynamic_cast<CExpressionBaseASTNode*>(*iter); subnode = dynamic_cast<CExpressionBaseASTNode*>(ReplaceChild(subnode, subnode->CastTo(semanter, dataType, Token))); (*iter) = subnode; } // If we are a class reference, we can only access static fields. bool isClassReference = (dynamic_cast<CClassReferenceDataType*>(left_hand_expr->ExpressionResultType) != NULL); if (isClassReference == true) { if (declaration->IsStatic == false) { semanter->GetContext()->FatalError(CStringHelper::FormatString("Cannot access instance method '%s' through class reference '%s'.", declaration->Identifier.c_str(), accessClass->ToString().c_str()), Token); } } // If this is a constructor we are calling, make sure we are in a constructors scope, or its illegal! else { CClassMemberASTNode* methodScope = FindClassMethodScope(semanter); if (methodScope == NULL || methodScope->IsConstructor == false) { if (declaration->IsConstructor == true) { semanter->GetContext()->FatalError("Calling constructors manually is only valid inside another constructors scope.", Token); } } } // Resulting type is always our right hand type. ExpressionResultType = declaration->ReturnType; return this; }
// ================================================================= // Check for duplicate identifier. // ================================================================= CClassMemberASTNode* CClassASTNode::FindClassMethod(CSemanter* semanter, std::string identifier, std::vector<CDataType*> arguments, bool explicit_arguments, CASTNode* ignoreNode, CASTNode* referenceNode) { // Make sure this class is semanted. if (!Semanted) { Semant(semanter); } // Find all possible methods with the name. std::vector<CClassMemberASTNode*> nodes; CClassASTNode* scope = this; while (scope != NULL) { if (scope->Body != NULL) { for (auto iter = scope->Body->Children.begin(); iter != scope->Body->Children.end(); iter++) { CClassMemberASTNode* member = dynamic_cast<CClassMemberASTNode*>(*iter); if (member != NULL && member->MemberType == MemberType::Method && member->Identifier == identifier && member != ignoreNode && arguments.size() <= member->Arguments.size()) { // Has one of the other members overridcen this method already? bool alreadyExists = false; for (auto iter2 = nodes.begin(); iter2 != nodes.end(); iter2++) { CClassMemberASTNode* member2 = *iter2; if (member->Identifier == member2->Identifier && member->Arguments.size() == member2->Arguments.size() && member->IsVirtual == true && member2->IsOverride == true) { bool argsSame = true; for (unsigned int i = 0; i < member->Arguments.size(); i++) { CVariableStatementASTNode* arg = member->Arguments.at(i); CVariableStatementASTNode* arg2 = member2->Arguments.at(i); if (!arg->Type->IsEqualTo(semanter, arg2->Type)) { argsSame = false; break; } } if (argsSame == true) { alreadyExists = true; break; } } } if (alreadyExists == false) { member->Semant(semanter); nodes.push_back(member); } } } } scope = scope->SuperClass; } // Try and find amatch! CClassMemberASTNode* match = NULL; bool isExactMatch = false; std::string errorMessage = ""; // Look for valid nodes. for (auto iter = nodes.begin(); iter != nodes.end(); iter++) { CClassMemberASTNode* member = *iter; bool exact = true; bool possible = true; for (unsigned int i = 0; i < member->Arguments.size(); i++) { CVariableStatementASTNode* arg = member->Arguments.at(i); if (arguments.size() > member->Arguments.size()) { continue; } if (i < arguments.size()) { if (arguments.at(i)->IsEqualTo(semanter, arg->Type)) { continue; } exact = false; if (!explicit_arguments && CCastExpressionASTNode::IsValidCast(semanter, arguments.at(i), arg->Type, false))// arguments.at(i)->CanCastTo(semanter, arg->Type)) { continue; } } else if (arg->AssignmentExpression != NULL) { exact = false; if (!explicit_arguments) { continue; } } possible = false; break; } if (!possible) { continue; } if (exact == true) { if (isExactMatch == true) { semanter->GetContext()->FatalError(CStringHelper::FormatString("Found ambiguous reference to method of class '%s'. Reference could mean either '%s' or '%s'.", Identifier.c_str(), match->ToString().c_str(), member->ToString().c_str()), referenceNode == NULL ? Token : referenceNode->Token); } else { errorMessage = ""; match = member; isExactMatch = true; } } else { if (!isExactMatch) { if (match != NULL) { errorMessage = CStringHelper::FormatString("Found ambiguous reference to method of class '%s'. Reference could mean either '%s' or '%s'.", Identifier.c_str(), match->ToString().c_str(), member->ToString().c_str()); } else { match = member; } } } } // Return? if (!isExactMatch) { if (errorMessage != "") { semanter->GetContext()->FatalError(errorMessage, referenceNode == NULL ? Token : referenceNode->Token); } if (explicit_arguments == true) { return NULL; } } // No match available? :S if (match == NULL) { return NULL; } // Return matched class. return match; }