Skip to content

Commit a71e060

Browse files
committed
Fix limit of 0 should result in 0 iterations
1 parent d5beea0 commit a71e060

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
lines changed

GDJS/GDJS/Extensions/Builtin/CommonInstructionsExtension.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
991991
codeGenerator, parentContext, "number",
992992
gd::Expression(event.GetLimit()));
993993
outputCode += forEachLimitVar + " = " + limitCode + ";\n";
994-
outputCode += "if (" + forEachLimitVar + " > 0 && " +
994+
outputCode += "if (" + forEachLimitVar + " >= 0 && " +
995995
forEachSortedList + ".length > " + forEachLimitVar +
996996
") " + forEachSortedList + ".length = " +
997997
forEachLimitVar + ";\n";

GDevelop.js/__tests__/GDJSForEachCodeGenerationIntegrationTests.js

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,9 +1003,9 @@ describe('libGD.js - GDJS ForEach Code Generation integration tests', function (
10031003
);
10041004
});
10051005

1006-
it('limit of 0 does not truncate when orderBy is set', function () {
1007-
// The codegen checks "if (limit > 0 && list.length > limit)",
1008-
// so a limit of 0 should NOT truncate the list.
1006+
it('limit of 0 results in zero iterations when orderBy is set', function () {
1007+
// A limit of 0 means "iterate 0 objects" — no iterations should happen.
1008+
// Only an empty/unspecified limit means "no limit".
10091009
const serializerElement = gd.Serializer.fromJSObject([
10101010
{
10111011
type: 'BuiltinCommonInstructions::ForEach',
@@ -1051,7 +1051,58 @@ describe('libGD.js - GDJS ForEach Code Generation integration tests', function (
10511051

10521052
runCompiledEvents(gdjs, runtimeScene, [objectLists]);
10531053

1054-
// Limit 0 → no truncation, all objects iterated in sorted order
1054+
// Limit 0 → zero iterations, trace stays empty
1055+
expect(runtimeScene.getVariables().get('Trace').getAsString()).toBe('');
1056+
});
1057+
1058+
it('negative limit does not truncate when orderBy is set', function () {
1059+
// A negative limit is treated as "no limit" (all objects iterated).
1060+
const serializerElement = gd.Serializer.fromJSObject([
1061+
{
1062+
type: 'BuiltinCommonInstructions::ForEach',
1063+
object: 'MyObject',
1064+
orderBy: 'MyObject.Variable(Score)',
1065+
order: 'asc',
1066+
limit: '-1',
1067+
conditions: [],
1068+
actions: [
1069+
{
1070+
type: { value: 'ModVarSceneTxt' },
1071+
parameters: [
1072+
'Trace',
1073+
'+',
1074+
'ToString(MyObject.Variable(Score)) + ";"',
1075+
],
1076+
},
1077+
],
1078+
events: [],
1079+
},
1080+
]);
1081+
1082+
const runCompiledEvents = generateCompiledEventsFromSerializedEvents(
1083+
gd,
1084+
serializerElement,
1085+
{
1086+
parameterTypes: { MyObject: 'object' },
1087+
logCode: false,
1088+
}
1089+
);
1090+
1091+
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
1092+
const objectLists = new gdjs.Hashtable();
1093+
const myObjects = [];
1094+
objectLists.put('MyObject', myObjects);
1095+
[50, 10, 30, 20].forEach(v => {
1096+
const o = runtimeScene.createObject('MyObject');
1097+
o.getVariables().get('Score').setNumber(v);
1098+
myObjects.push(o);
1099+
});
1100+
1101+
runtimeScene.getVariables().get('Trace').setString('');
1102+
1103+
runCompiledEvents(gdjs, runtimeScene, [objectLists]);
1104+
1105+
// Negative limit → no truncation, all objects iterated in sorted order
10551106
expect(runtimeScene.getVariables().get('Trace').getAsString()).toBe(
10561107
'10;20;30;50;'
10571108
);

0 commit comments

Comments
 (0)