This is an automated email from the ASF dual-hosted git repository.
Cole-Greer pushed a commit to branch 3.7-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/3.7-dev by this push:
new d05ddc04a1 TINKERPOP-3283: loops() quietly overflows (#3688)
d05ddc04a1 is described below
commit d05ddc04a1bfcf902094fb9862bf8b707ee6d674
Author: Iryna-Kahamlyk <[email protected]>
AuthorDate: Mon Oct 5 13:40:13 2026 -0700
TINKERPOP-3283: loops() quietly overflows (#3688)
Fixed `loops()` silently overflowing its internal counter and wrapping to a
negative count; the counter now uses `int` instead of `short` globally and
throws an `IllegalStateException` if Integer.MAX_VALUE iterations is ever
exceeded.
---
CHANGELOG.asciidoc | 1 +
docs/src/dev/provider/gremlin-semantics.asciidoc | 4 ++
docs/src/reference/the-traversal.asciidoc | 3 +
.../traverser/B_LP_NL_O_P_S_SE_SL_Traverser.java | 2 +-
.../traverser/B_LP_NL_O_S_SE_SL_Traverser.java | 2 +-
.../traverser/B_NL_O_S_SE_SL_Traverser.java | 2 +-
.../traversal/traverser/B_O_S_SE_SL_Traverser.java | 4 +-
.../traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java | 2 +-
.../traverser/LP_NL_O_OB_S_SE_SL_Traverser.java | 2 +-
.../traverser/NL_O_OB_S_SE_SL_Traverser.java | 2 +-
.../traverser/O_OB_S_SE_SL_Traverser.java | 4 +-
.../traversal/traverser/util/LabelledCounter.java | 10 +--
.../traverser/LoopCounterOverflowTest.java | 74 ++++++++++++++++++++++
.../traverser/util/LabelledCounterTest.java | 46 ++++++++++++++
.../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 1 +
gremlin-go/driver/cucumber/gremlin.go | 1 +
.../gremlin-javascript/test/cucumber/gremlin.js | 1 +
.../src/main/python/tests/feature/gremlin.py | 1 +
.../gremlin/test/features/branch/Repeat.feature | 12 ++++
19 files changed, 162 insertions(+), 12 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index b4e56b9526..41168ce7d7 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -25,6 +25,7 @@
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
[[release-3-7-8]]
=== TinkerPop 3.7.8 (Release Date: NOT OFFICIALLY RELEASED YET)
+* Fixed `loops()` silently overflowing its internal counter and wrapping to a
negative count; the counter now uses `int` instead of `short` and throws an
`IllegalStateException` if Integer.MAX_VALUE iterations is ever exceeded.
* Fixed `gremlin-python` GraphBinary serialization of `int` values outside the
Java `int` range, which raised a `struct.error` rather than writing a `Long`.
[[release-3-7-7]]
diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc
b/docs/src/dev/provider/gremlin-semantics.asciidoc
index 215a069d9c..0ce7e138b0 100644
--- a/docs/src/dev/provider/gremlin-semantics.asciidoc
+++ b/docs/src/dev/provider/gremlin-semantics.asciidoc
@@ -1466,6 +1466,8 @@ predicates are evaluated before the first iteration (pre)
or after each iteratio
after the body finishes), not before. Therefore, `loops()` reflects the number
of completed iterations.
- `loops()` without arguments returns the counter for the closest
(innermost) `repeat()`. `loops("name")` returns the
counter for the named loop.
+ - The loop counter is bounded to `Integer.MAX_VALUE` (2,147,483,647)
iterations. Exceeding this bound raises an error
+(see Exceptions).
- Re-queuing for the next iteration:
- After each iteration, if `until` is not satisfied at the post-check, the
traverser is sent back into the loop body
for another iteration. If it is satisfied, the traverser exits the loop and
proceeds downstream.
@@ -1485,6 +1487,8 @@ message containing: `The repeat()-traversal was not
defined`.
* Using `emit()`, `until()`, or `times()` without a matching `repeat()` will
raise an `IllegalStateException` at runtime
when the step is initialized during iteration with the message containing:
`The repeat()-traversal was not defined`.
+* Incrementing a loop counter past `Integer.MAX_VALUE` (2,147,483,647) will
raise an `IllegalStateException` with a
+ message containing: `has exceeded the maximum supported value`.
See:
link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/RepeatStep.java[source],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#repeat-step[reference],
diff --git a/docs/src/reference/the-traversal.asciidoc
b/docs/src/reference/the-traversal.asciidoc
index 2749f76483..8b84fca91d 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -2521,6 +2521,9 @@ The `loops()`-step (*map*) extracts the number of times
the `Traverser` has gone
g.V().emit(__.has("name",
"marko").or().loops().is(2)).repeat(__.out()).values("name")
----
+WARNING: The loop counter is limited to 2,147,483,647 iterations. A `repeat()`
construct that loops
+beyond that limit throws an `IllegalStateException`.
+
*Additional References*
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#loops()++[`loops()`],
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_P_S_SE_SL_Traverser.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_P_S_SE_SL_Traverser.java
index d8e24d2b3d..5f2e4d59f1 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_P_S_SE_SL_Traverser.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_P_S_SE_SL_Traverser.java
@@ -60,7 +60,7 @@ public class B_LP_NL_O_P_S_SE_SL_Traverser<T> extends
B_LP_O_P_S_SE_SL_Traverser
@Override
public void initialiseLoops(final String stepLabel, final String loopName)
{
if (this.nestedLoops.empty() ||
!this.nestedLoops.peek().hasLabel(stepLabel)) {
- final LabelledCounter lc = new LabelledCounter(stepLabel, (short)
0);
+ final LabelledCounter lc = new LabelledCounter(stepLabel, 0);
this.nestedLoops.push(lc);
if (loopName != null)
this.loopNames.put(loopName, lc);
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_S_SE_SL_Traverser.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_S_SE_SL_Traverser.java
index 07c2b84598..0e3e62a077 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_S_SE_SL_Traverser.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_S_SE_SL_Traverser.java
@@ -60,7 +60,7 @@ public class B_LP_NL_O_S_SE_SL_Traverser<T> extends
B_LP_O_S_SE_SL_Traverser<T>
@Override
public void initialiseLoops(final String stepLabel, final String loopName)
{
if (this.nestedLoops.empty() ||
!this.nestedLoops.peek().hasLabel(stepLabel)) {
- final LabelledCounter lc = new LabelledCounter(stepLabel, (short)
0);
+ final LabelledCounter lc = new LabelledCounter(stepLabel, 0);
this.nestedLoops.push(lc);
if (loopName != null)
this.loopNames.put(loopName, lc);
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_NL_O_S_SE_SL_Traverser.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_NL_O_S_SE_SL_Traverser.java
index 4268526199..c37b568daa 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_NL_O_S_SE_SL_Traverser.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_NL_O_S_SE_SL_Traverser.java
@@ -59,7 +59,7 @@ public class B_NL_O_S_SE_SL_Traverser<T> extends
B_O_S_SE_SL_Traverser<T> {
@Override
public void initialiseLoops(final String stepLabel, final String loopName)
{
if (this.nestedLoops.empty() ||
!this.nestedLoops.peek().hasLabel(stepLabel)) {
- final LabelledCounter lc = new LabelledCounter(stepLabel, (short)
0);
+ final LabelledCounter lc = new LabelledCounter(stepLabel, 0);
this.nestedLoops.push(lc);
if (loopName != null)
this.loopNames.put(loopName, lc);
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_O_S_SE_SL_Traverser.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_O_S_SE_SL_Traverser.java
index 2f78668a1c..10ec80ef7a 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_O_S_SE_SL_Traverser.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_O_S_SE_SL_Traverser.java
@@ -32,7 +32,7 @@ import java.util.Set;
public class B_O_S_SE_SL_Traverser<T> extends B_O_Traverser<T> {
protected Object sack = null;
- protected short loops = 0; // an optimization hack to use a short
internally to save bits :)
+ protected int loops = 0;
protected String loopName = null;
protected transient TraversalSideEffects sideEffects;
@@ -85,6 +85,8 @@ public class B_O_S_SE_SL_Traverser<T> extends
B_O_Traverser<T> {
@Override
public void incrLoops() {
+ if (this.loops == Integer.MAX_VALUE)
+ throw new IllegalStateException("Loop counter has exceeded the
maximum supported value of " + Integer.MAX_VALUE);
this.loops++;
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java
index aae7689083..7013cc3e83 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java
@@ -61,7 +61,7 @@ public class LP_NL_O_OB_P_S_SE_SL_Traverser<T> extends
LP_O_OB_P_S_SE_SL_Travers
@Override
public void initialiseLoops(final String stepLabel, final String loopName)
{
if (this.nestedLoops.empty() ||
!this.nestedLoops.peek().hasLabel(stepLabel)) {
- final LabelledCounter lc = new LabelledCounter(stepLabel, (short)
0);
+ final LabelledCounter lc = new LabelledCounter(stepLabel, 0);
this.nestedLoops.push(lc);
if (loopName != null)
this.loopNames.put(loopName, lc);
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_S_SE_SL_Traverser.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_S_SE_SL_Traverser.java
index b34493133e..e93840bb2b 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_S_SE_SL_Traverser.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_S_SE_SL_Traverser.java
@@ -66,7 +66,7 @@ public class LP_NL_O_OB_S_SE_SL_Traverser<T> extends
LP_O_OB_S_SE_SL_Traverser<T
@Override
public void initialiseLoops(final String stepLabel, final String loopName)
{
if (this.nestedLoops.empty() ||
!this.nestedLoops.peek().hasLabel(stepLabel)) {
- final LabelledCounter lc = new LabelledCounter(stepLabel, (short)
0);
+ final LabelledCounter lc = new LabelledCounter(stepLabel, 0);
this.nestedLoops.push(lc);
if (loopName != null)
this.loopNames.put(loopName, lc);
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_O_OB_S_SE_SL_Traverser.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_O_OB_S_SE_SL_Traverser.java
index 2fc4240a35..9564c81a57 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_O_OB_S_SE_SL_Traverser.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_O_OB_S_SE_SL_Traverser.java
@@ -61,7 +61,7 @@ public class NL_O_OB_S_SE_SL_Traverser<T> extends
O_OB_S_SE_SL_Traverser<T> {
@Override
public void initialiseLoops(final String stepLabel, final String loopName)
{
if (this.nestedLoops.empty() ||
!this.nestedLoops.peek().hasLabel(stepLabel)) {
- final LabelledCounter lc = new LabelledCounter(stepLabel, (short)
0);
+ final LabelledCounter lc = new LabelledCounter(stepLabel, 0);
this.nestedLoops.push(lc);
if (loopName != null)
this.loopNames.put(loopName, lc);
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/O_OB_S_SE_SL_Traverser.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/O_OB_S_SE_SL_Traverser.java
index c748a91d2b..a6ff843d55 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/O_OB_S_SE_SL_Traverser.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/O_OB_S_SE_SL_Traverser.java
@@ -33,7 +33,7 @@ import java.util.Set;
public class O_OB_S_SE_SL_Traverser<T> extends O_Traverser<T> {
protected Object sack = null;
- protected short loops = 0; // an optimization hack to use a short
internally to save bits :)
+ protected int loops = 0;
protected String loopName = null;
protected transient TraversalSideEffects sideEffects;
protected String future = HALT;
@@ -98,6 +98,8 @@ public class O_OB_S_SE_SL_Traverser<T> extends O_Traverser<T>
{
@Override
public void incrLoops() {
+ if (this.loops == Integer.MAX_VALUE)
+ throw new IllegalStateException("Loop counter has exceeded the
maximum supported value of " + Integer.MAX_VALUE);
this.loops++;
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/LabelledCounter.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/LabelledCounter.java
index b1a1d9779d..0b7b524de7 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/LabelledCounter.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/LabelledCounter.java
@@ -27,13 +27,13 @@ import java.io.Serializable;
public class LabelledCounter implements Serializable, Cloneable {
private final String label;
- private short count = 0;
+ private int count = 0;
protected LabelledCounter() {
label = "";
}
- public LabelledCounter(final String label, final short initialCount) {
+ public LabelledCounter(final String label, final int initialCount) {
if (label == null) {
throw new NullPointerException("Label is null");
}
@@ -50,6 +50,8 @@ public class LabelledCounter implements Serializable,
Cloneable {
}
public void increment() {
+ if (this.count == Integer.MAX_VALUE)
+ throw new IllegalStateException("Loop counter for label '" +
this.label + "' has exceeded the maximum supported value of " +
Integer.MAX_VALUE);
this.count++;
}
@@ -60,7 +62,7 @@ public class LabelledCounter implements Serializable,
Cloneable {
@Override
public String toString(){
- return "Step Label: " + this.label + " Counter: " +
Short.toString(this.count);
+ return "Step Label: " + this.label + " Counter: " +
Integer.toString(this.count);
}
@Override
@@ -77,7 +79,7 @@ public class LabelledCounter implements Serializable,
Cloneable {
@Override
public int hashCode() {
int result = label != null ? label.hashCode() : 0;
- result = 31 * result + (int) count;
+ result = 31 * result + count;
return result;
}
}
diff --git
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LoopCounterOverflowTest.java
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LoopCounterOverflowTest.java
new file mode 100644
index 0000000000..9df7a4943e
--- /dev/null
+++
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LoopCounterOverflowTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.traverser;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import
org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class LoopCounterOverflowTest {
+
+ // Overflow is seeded directly at Integer.MAX_VALUE rather than reached by
looping incrLoops()
+ // billions of times, which would be impractical for a unit test.
+
+ @Test
+ public void shouldThrowOnSingleLoopOverflow() {
+ final GraphTraversalSource g = EmptyGraph.instance().traversal();
+ final Traversal.Admin traversal =
g.withBulk(false).V().repeat(__.out()).times(10).asAdmin();
+ traversal.applyStrategies();
+ final Traverser.Admin<?> traverser =
traversal.getTraverserGenerator().generate(new Object(),
traversal.getStartStep(), 1L);
+ assertEquals(O_OB_S_SE_SL_Traverser.class, traverser.getClass());
+
+ ((O_OB_S_SE_SL_Traverser<?>) traverser).loops = Integer.MAX_VALUE;
+ assertEquals(Integer.MAX_VALUE, traverser.loops());
+
+ try {
+ traverser.incrLoops();
+ fail("Should have thrown an IllegalStateException when the loop
counter overflows an int");
+ } catch (IllegalStateException ise) {
+ assertEquals(Integer.MAX_VALUE, traverser.loops());
+ }
+ }
+
+ @Test
+ public void shouldThrowOnNestedLoopOverflow() {
+ final GraphTraversalSource g = EmptyGraph.instance().traversal();
+ final Traversal.Admin traversal =
g.withBulk(false).V().repeat(__.repeat(__.out())).times(10).asAdmin();
+ traversal.applyStrategies();
+ final Traverser.Admin<?> traverser =
traversal.getTraverserGenerator().generate(new Object(),
traversal.getStartStep(), 1L);
+ assertEquals(NL_O_OB_S_SE_SL_Traverser.class, traverser.getClass());
+
+ ((NL_O_OB_S_SE_SL_Traverser<?>) traverser).nestedLoops.push(new
LabelledCounter("repeat", Integer.MAX_VALUE));
+ assertEquals(Integer.MAX_VALUE, traverser.loops());
+
+ try {
+ traverser.incrLoops();
+ fail("Should have thrown an IllegalStateException when the loop
counter overflows an int");
+ } catch (IllegalStateException ise) {
+ assertEquals(Integer.MAX_VALUE, traverser.loops());
+ }
+ }
+}
diff --git
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/LabelledCounterTest.java
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/LabelledCounterTest.java
new file mode 100644
index 0000000000..d567b07bef
--- /dev/null
+++
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/LabelledCounterTest.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.traverser.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class LabelledCounterTest {
+
+ @Test
+ public void shouldIncrementCount() {
+ final LabelledCounter lc = new LabelledCounter("a", 0);
+ lc.increment();
+ lc.increment();
+ assertEquals(2, lc.count());
+ }
+
+ @Test
+ public void shouldThrowOnOverflow() {
+ final LabelledCounter lc = new LabelledCounter("a", Integer.MAX_VALUE);
+ try {
+ lc.increment();
+ fail("Should have thrown an IllegalStateException when the loop
counter overflows an int");
+ } catch (IllegalStateException ise) {
+ assertEquals(Integer.MAX_VALUE, lc.count());
+ }
+ }
+}
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index fe51031bab..6a85f9ebb7 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -119,6 +119,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_emit", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Emit()}},
{"g_V_untilXidentityX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.V().Until(__.Identity())}},
{"g_V_timesX5X", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Times(5)}},
+ {"g_VX1X_repeatXloopsX_timesX100000X", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V(p["vid1"]).Repeat(__.Loops()).Times(100000)}},
{"g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX1X_name", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Has("person","name","marko").Repeat(__.Out("created")).Times(1).Values<object>("name")}},
{"g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX0X_name", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Has("person","name","marko").Repeat(__.Out("created")).Times(0).Values<object>("name")}},
{"g_V_haxXperson_name_markoX_timesX1X_repeatXoutXcreatedXX_name", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Has("person","name","marko").Times(1).Repeat(__.Out("created")).Values<object>("name")}},
diff --git a/gremlin-go/driver/cucumber/gremlin.go
b/gremlin-go/driver/cucumber/gremlin.go
index d1de80cda4..321b7fc946 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -90,6 +90,7 @@ var translationMap = map[string][]func(g
*gremlingo.GraphTraversalSource, p map[
"g_V_emit": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Emit()}},
"g_V_untilXidentityX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.V().Until(gremlingo.T__.Identity())}},
"g_V_timesX5X": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Times(5)}},
+ "g_VX1X_repeatXloopsX_timesX100000X": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V(p["vid1"]).Repeat(gremlingo.T__.Loops()).Times(100000)}},
"g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX1X_name": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.V().Has("person", "name",
"marko").Repeat(gremlingo.T__.Out("created")).Times(1).Values("name")}},
"g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX0X_name": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.V().Has("person", "name",
"marko").Repeat(gremlingo.T__.Out("created")).Times(0).Values("name")}},
"g_V_haxXperson_name_markoX_timesX1X_repeatXoutXcreatedXX_name": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.V().Has("person", "name",
"marko").Times(1).Repeat(gremlingo.T__.Out("created")).Values("name")}},
diff --git
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index a83296fcd6..4749161753 100644
---
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -110,6 +110,7 @@ const gremlins = {
g_V_emit: [function({g}) { return g.V().emit() }],
g_V_untilXidentityX: [function({g}) { return g.V().until(__.identity())
}],
g_V_timesX5X: [function({g}) { return g.V().times(5) }],
+ g_VX1X_repeatXloopsX_timesX100000X: [function({g, vid1}) { return
g.V(vid1).repeat(__.loops()).times(100000) }],
g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX1X_name:
[function({g}) { return
g.V().has("person","name","marko").repeat(__.out("created")).times(1).values("name")
}],
g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX0X_name:
[function({g}) { return
g.V().has("person","name","marko").repeat(__.out("created")).times(0).values("name")
}],
g_V_haxXperson_name_markoX_timesX1X_repeatXoutXcreatedXX_name:
[function({g}) { return
g.V().has("person","name","marko").times(1).repeat(__.out("created")).values("name")
}],
diff --git a/gremlin-python/src/main/python/tests/feature/gremlin.py
b/gremlin-python/src/main/python/tests/feature/gremlin.py
index 3580de30dd..530b3915b7 100644
--- a/gremlin-python/src/main/python/tests/feature/gremlin.py
+++ b/gremlin-python/src/main/python/tests/feature/gremlin.py
@@ -92,6 +92,7 @@ world.gremlins = {
'g_V_emit': [(lambda g:g.V().emit())],
'g_V_untilXidentityX': [(lambda g:g.V().until(__.identity()))],
'g_V_timesX5X': [(lambda g:g.V().times(5))],
+ 'g_VX1X_repeatXloopsX_timesX100000X': [(lambda g,
vid1=None:g.V(vid1).repeat(__.loops()).times(100000))],
'g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX1X_name': [(lambda
g:g.V().has('person','name','marko').repeat(__.out('created')).times(1).name)],
'g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX0X_name': [(lambda
g:g.V().has('person','name','marko').repeat(__.out('created')).times(0).name)],
'g_V_haxXperson_name_markoX_timesX1X_repeatXoutXcreatedXX_name': [(lambda
g:g.V().has('person','name','marko').times(1).repeat(__.out('created')).name)],
diff --git
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Repeat.feature
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Repeat.feature
index 5c3d3e5e68..3e635f0fcf 100644
---
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Repeat.feature
+++
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Repeat.feature
@@ -399,6 +399,18 @@ Feature: Step - repeat()
When iterated to list
Then the traversal will raise an error with message containing text of
"The repeat()-traversal was not defined"
+ Scenario: g_VX1X_repeatXloopsX_timesX100000X
+ Given the modern graph
+ And using the parameter vid1 defined as "v[marko].id"
+ And the traversal of
+ """
+ g.V(vid1).repeat(__.loops()).times(100000)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[99999].i |
+
Scenario: g_V_haxXperson_name_markoX_repeatXoutXcreatedXX_timesX1X_name
Given the modern graph
And the traversal of