Fix memory corruption/crashes when events in extensions had errors (#7050)

This commit is contained in:
Florian Rival
2024-10-13 22:44:36 +02:00
committed by GitHub
parent ab97258832
commit 31dac9cc93
3 changed files with 123 additions and 68 deletions

View File

@@ -14,10 +14,10 @@
namespace gd {
/**
* \brief
* \brief
*/
class GD_CORE_API ProjectDiagnostic {
public:
public:
enum ErrorType {
UndeclaredVariable,
MissingBehavior,
@@ -25,12 +25,17 @@ public:
MismatchedObjectType,
};
ProjectDiagnostic(ErrorType type_, const gd::String &message_,
ProjectDiagnostic(ErrorType type_,
const gd::String &message_,
const gd::String &actualValue_,
const gd::String &expectedValue_, const gd::String &objectName_ = "")
: type(type_), message(message_), actualValue(actualValue_), expectedValue(expectedValue_),
objectName(objectName_){};
virtual ~ProjectDiagnostic(){};
const gd::String &expectedValue_,
const gd::String &objectName_ = "")
: type(type_),
message(message_),
actualValue(actualValue_),
expectedValue(expectedValue_),
objectName(objectName_) {};
virtual ~ProjectDiagnostic() {};
ErrorType GetType() const { return type; };
const gd::String &GetMessage() const { return message; }
@@ -38,7 +43,7 @@ public:
const gd::String &GetActualValue() const { return actualValue; }
const gd::String &GetExpectedValue() const { return expectedValue; }
private:
private:
ErrorType type;
gd::String message;
gd::String objectName;
@@ -47,12 +52,12 @@ private:
};
/**
* \brief
* \brief
*/
class GD_CORE_API DiagnosticReport {
public:
DiagnosticReport(){};
virtual ~DiagnosticReport(){};
public:
DiagnosticReport() {};
virtual ~DiagnosticReport() {};
void Add(const gd::ProjectDiagnostic &projectDiagnostic) {
projectDiagnostics.push_back(
@@ -67,32 +72,39 @@ public:
const gd::String &GetSceneName() const { return sceneName; }
void SetSceneName(const gd::String &sceneName_) {
sceneName = sceneName_;
void SetSceneName(const gd::String &sceneName_) { sceneName = sceneName_; }
void LogAllDiagnostics() {
for (auto &diagnostic : projectDiagnostics) {
std::cout << diagnostic->GetMessage()
<< "(object: " << diagnostic->GetObjectName()
<< ", actual value: " << diagnostic->GetActualValue()
<< ", expected value: " << diagnostic->GetExpectedValue() << ")"
<< std::endl;
}
}
private:
private:
std::vector<std::unique_ptr<gd::ProjectDiagnostic>> projectDiagnostics;
gd::String sceneName;
};
/**
* \brief
* \brief
*/
class GD_CORE_API WholeProjectDiagnosticReport {
public:
WholeProjectDiagnosticReport(){};
virtual ~WholeProjectDiagnosticReport(){};
public:
WholeProjectDiagnosticReport() {};
virtual ~WholeProjectDiagnosticReport() {};
const DiagnosticReport &Get(std::size_t index) const {
return *diagnosticReports[index].get();
};
void Clear() {
diagnosticReports.clear();
};
void Clear() { diagnosticReports.clear(); };
DiagnosticReport& AddNewDiagnosticReportForScene(const gd::String &sceneName) {
DiagnosticReport &AddNewDiagnosticReportForScene(
const gd::String &sceneName) {
auto diagnosticReport = gd::make_unique<gd::DiagnosticReport>();
diagnosticReport->SetSceneName(sceneName);
diagnosticReports.push_back(std::move(diagnosticReport));
@@ -102,7 +114,7 @@ public:
std::size_t Count() const { return diagnosticReports.size(); };
bool HasAnyIssue() {
for (auto& diagnosticReport : diagnosticReports) {
for (auto &diagnosticReport : diagnosticReports) {
if (diagnosticReport->Count() > 0) {
return true;
}
@@ -110,8 +122,8 @@ public:
return false;
}
private:
private:
std::vector<std::unique_ptr<gd::DiagnosticReport>> diagnosticReports;
};
} // namespace gd
} // namespace gd

View File

@@ -345,14 +345,14 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
objectInParameter, "");
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
return "/* Unknown object - skipped. */";
} else if (!expectedObjectType.empty() &&
actualObjectType != expectedObjectType) {
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
actualObjectType, expectedObjectType, objectInParameter);
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
return "/* Mismatched object type - skipped. */";
}
}
@@ -509,7 +509,7 @@ void EventsCodeGenerator::CheckBehaviorParameters(
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::MissingBehavior, "",
actualBehaviorType, expectedBehaviorType, lastObjectName);
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
}
}
});
@@ -567,14 +567,14 @@ gd::String EventsCodeGenerator::GenerateActionCode(
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::UnknownObject, "",
objectInParameter, "");
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
return "/* Unknown object - skipped. */";
} else if (!expectedObjectType.empty() &&
actualObjectType != expectedObjectType) {
gd::ProjectDiagnostic projectDiagnostic(
gd::ProjectDiagnostic::ErrorType::MismatchedObjectType, "",
actualObjectType, expectedObjectType, objectInParameter);
diagnosticReport->Add(projectDiagnostic);
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
return "/* Mismatched object type - skipped. */";
}
}

View File

@@ -21,13 +21,13 @@
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/EventsFunction.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertiesContainer.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDJS/Events/CodeGeneration/BehaviorCodeGenerator.h"
#include "GDJS/Events/CodeGeneration/EventsCodeGenerator.h"
#include "GDJS/Extensions/JsPlatform.h"
@@ -130,19 +130,27 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
std::set<gd::String>& includeFiles,
bool compilationForRuntime) {
gd::ObjectsContainer parameterObjectsAndGroups;
auto projectScopedContainers =
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsFunctionsExtension, eventsFunction, parameterObjectsAndGroups);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project,
eventsFunctionsExtension,
eventsFunction,
parameterObjectsAndGroups);
EventsCodeGenerator codeGenerator(projectScopedContainers);
codeGenerator.SetCodeNamespace(codeNamespace);
codeGenerator.SetGenerateCodeForRuntime(compilationForRuntime);
gd::DiagnosticReport diagnosticReport;
codeGenerator.SetDiagnosticReport(&diagnosticReport);
gd::String output = GenerateEventsListCompleteFunctionCode(
codeGenerator,
codeGenerator.GetCodeNamespaceAccessor() + "func",
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
eventsFunction.GetParametersForEvents(eventsFunctionsExtension), 0, true),
eventsFunction.GetParametersForEvents(eventsFunctionsExtension),
0,
true),
codeGenerator.GenerateFreeEventsFunctionContext(
eventsFunctionsExtension,
eventsFunction,
@@ -151,6 +159,15 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
"",
codeGenerator.GenerateEventsFunctionReturn(eventsFunction));
// TODO: the editor should pass the diagnostic report and display it to the
// user. For now, display it in the console.
if (diagnosticReport.Count() > 0) {
std::cout << "Diagnostics when generating events function code ("
<< eventsFunctionsExtension.GetName() << ", "
<< eventsFunction.GetName() << "):" << std::endl;
diagnosticReport.LogAllDiagnostics();
}
includeFiles.insert(codeGenerator.GetIncludeFiles().begin(),
codeGenerator.GetIncludeFiles().end());
return output;
@@ -167,17 +184,22 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
const gd::String& preludeCode,
std::set<gd::String>& includeFiles,
bool compilationForRuntime) {
gd::ObjectsContainer parameterObjectsContainers;
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForBehaviorEventsFunction(
project, eventsFunctionsExtension, eventsBasedBehavior,
eventsFunction, parameterObjectsContainers);
project,
eventsFunctionsExtension,
eventsBasedBehavior,
eventsFunction,
parameterObjectsContainers);
EventsCodeGenerator codeGenerator(projectScopedContainers);
codeGenerator.SetCodeNamespace(codeNamespace);
codeGenerator.SetGenerateCodeForRuntime(compilationForRuntime);
gd::DiagnosticReport diagnosticReport;
codeGenerator.SetDiagnosticReport(&diagnosticReport);
// Generate the code setting up the context of the function.
gd::String fullPreludeCode =
preludeCode + "\n" + "var that = this;\n" +
@@ -216,6 +238,15 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
"",
codeGenerator.GenerateEventsFunctionReturn(eventsFunction));
// TODO: the editor should pass the diagnostic report and display it to the
// user. For now, display it in the console.
if (diagnosticReport.Count() > 0) {
std::cout << "Diagnostics when generating behavior code ("
<< eventsBasedBehavior.GetName() << ", "
<< eventsFunction.GetName() << "):" << std::endl;
diagnosticReport.LogAllDiagnostics();
}
includeFiles.insert(codeGenerator.GetIncludeFiles().begin(),
codeGenerator.GetIncludeFiles().end());
return output;
@@ -233,17 +264,22 @@ gd::String EventsCodeGenerator::GenerateObjectEventsFunctionCode(
const gd::String& endingCode,
std::set<gd::String>& includeFiles,
bool compilationForRuntime) {
gd::ObjectsContainer parameterObjectsContainers;
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForObjectEventsFunction(
project, eventsFunctionsExtension, eventsBasedObject,
eventsFunction, parameterObjectsContainers);
project,
eventsFunctionsExtension,
eventsBasedObject,
eventsFunction,
parameterObjectsContainers);
EventsCodeGenerator codeGenerator(projectScopedContainers);
codeGenerator.SetCodeNamespace(codeNamespace);
codeGenerator.SetGenerateCodeForRuntime(compilationForRuntime);
gd::DiagnosticReport diagnosticReport;
codeGenerator.SetDiagnosticReport(&diagnosticReport);
// Generate the code setting up the context of the function.
gd::String fullPreludeCode =
preludeCode + "\n" + "var that = this;\n" +
@@ -258,8 +294,7 @@ gd::String EventsCodeGenerator::GenerateObjectEventsFunctionCode(
"var Object = Hashtable.newFrom({Object: thisObjectList});\n";
// Add child-objects
for (auto &childObject :
eventsBasedObject.GetObjects().GetObjects()) {
for (auto& childObject : eventsBasedObject.GetObjects().GetObjects()) {
// child-object are never picked because they are not parameters.
const auto& childName = ManObjListName(childObject->GetName());
fullPreludeCode += "var this" + childName +
@@ -293,6 +328,15 @@ gd::String EventsCodeGenerator::GenerateObjectEventsFunctionCode(
endingCode,
codeGenerator.GenerateEventsFunctionReturn(eventsFunction));
// TODO: the editor should pass the diagnostic report and display it to the
// user. For now, display it in the console.
if (diagnosticReport.Count() > 0) {
std::cout << "Diagnostics when generating object code ("
<< eventsBasedObject.GetName() << ", " << eventsFunction.GetName()
<< "):" << std::endl;
diagnosticReport.LogAllDiagnostics();
}
includeFiles.insert(codeGenerator.GetIncludeFiles().begin(),
codeGenerator.GetIncludeFiles().end());
return output;
@@ -424,8 +468,7 @@ gd::String EventsCodeGenerator::GenerateObjectEventsFunctionContext(
ConvertToStringExplicit(thisObjectName) + ": thisObjectList\n";
// Add child-objects
for (auto &childObject :
eventsBasedObject.GetObjects().GetObjects()) {
for (auto& childObject : eventsBasedObject.GetObjects().GetObjects()) {
const auto& childName = ManObjListName(childObject->GetName());
// child-object are never picked because they are not parameters.
objectsGettersMap += ", " +
@@ -458,8 +501,8 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
const gd::String& thisObjectName,
const gd::String& thisBehaviorName) {
const auto& extensionName = eventsFunctionsExtension.GetName();
const auto &parameters = eventsFunction.GetParametersForEvents(
eventsFunctionsContainer);
const auto& parameters =
eventsFunction.GetParametersForEvents(eventsFunctionsContainer);
// When running in the context of a function generated from events, we
// need some indirection to deal with objects, behaviors and parameters in
@@ -514,8 +557,9 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
}
}
const gd::String async =
eventsFunction.IsAsync() ? " task: new gdjs.ManuallyResolvableTask(),\n" : "";
const gd::String async = eventsFunction.IsAsync()
? " task: new gdjs.ManuallyResolvableTask(),\n"
: "";
return gd::String("var eventsFunctionContext = {\n") +
// The async task, if there is one
@@ -875,8 +919,9 @@ gd::String EventsCodeGenerator::GenerateObjectAction(
if (instrInfos.codeExtraInformation.type == "number" ||
instrInfos.codeExtraInformation.type == "string" ||
// Boolean actions declared with addExpressionAndConditionAndAction uses
// MutatorAndOrAccessor even though they don't declare an operator parameter.
// Boolean operators are only used with SetMutators or SetCustomCodeGenerator.
// MutatorAndOrAccessor even though they don't declare an operator
// parameter. Boolean operators are only used with SetMutators or
// SetCustomCodeGenerator.
(instrInfos.codeExtraInformation.type == "boolean" &&
instrInfos.codeExtraInformation.accessType ==
gd::InstructionMetadata::ExtraInformation::AccessType::Mutators)) {
@@ -942,8 +987,9 @@ gd::String EventsCodeGenerator::GenerateBehaviorAction(
if (instrInfos.codeExtraInformation.type == "number" ||
instrInfos.codeExtraInformation.type == "string" ||
// Boolean actions declared with addExpressionAndConditionAndAction uses
// MutatorAndOrAccessor even though they don't declare an operator parameter.
// Boolean operators are only used with SetMutators or SetCustomCodeGenerator.
// MutatorAndOrAccessor even though they don't declare an operator
// parameter. Boolean operators are only used with SetMutators or
// SetCustomCodeGenerator.
(instrInfos.codeExtraInformation.type == "boolean" &&
instrInfos.codeExtraInformation.accessType ==
gd::InstructionMetadata::ExtraInformation::AccessType::Mutators)) {
@@ -1311,7 +1357,7 @@ gd::String EventsCodeGenerator::GenerateGetVariable(
if (scope == ANY_VARIABLE) {
const auto variablesContainersList =
GetProjectScopedContainers().GetVariablesContainersList();
const auto &variablesContainer =
const auto& variablesContainer =
variablesContainersList.GetVariablesContainerFromVariableName(
variableName);
const auto sourceType = variablesContainer.GetSourceType();
@@ -1327,12 +1373,13 @@ gd::String EventsCodeGenerator::GenerateGetVariable(
variablesContainersList.GetLocalVariablesContainerPosition(
variablesContainer);
output = GenerateLocalVariablesStackAccessor() + "[" +
gd::String::From(localVariablesIndex) + "]";
gd::String::From(localVariablesIndex) + "]";
} else if (sourceType ==
gd::VariablesContainer::SourceType::ExtensionGlobal) {
gd::VariablesContainer::SourceType::ExtensionGlobal) {
variables = &variablesContainer;
output = "eventsFunctionContext.globalVariablesForExtension";
} else if (sourceType == gd::VariablesContainer::SourceType::ExtensionScene) {
} else if (sourceType ==
gd::VariablesContainer::SourceType::ExtensionScene) {
variables = &variablesContainer;
output = "eventsFunctionContext.sceneVariablesForExtension";
}
@@ -1371,17 +1418,13 @@ gd::String EventsCodeGenerator::GenerateGetVariable(
if (HasProjectAndLayout()) {
if (GetLayout().GetObjects().HasObjectNamed(
objectName)) // We check first layout's objects' list.
variables = &GetLayout()
.GetObjects()
.GetObject(objectName)
.GetVariables();
objectName)) // We check first layout's objects' list.
variables =
&GetLayout().GetObjects().GetObject(objectName).GetVariables();
else if (GetProject().GetObjects().HasObjectNamed(
objectName)) // Then the global objects list.
variables = &GetProject()
.GetObjects()
.GetObject(objectName)
.GetVariables();
objectName)) // Then the global objects list.
variables =
&GetProject().GetObjects().GetObject(objectName).GetVariables();
}
}