Skip to content

Commit a5b0893

Browse files
committed
Refactor ES2025 Iterator constructor implementation
Refactored the ES2025 Iterator global constructor for better maintainability and spec compliance. Changes: - Cleaned up NativeIteratorConstructor with better documentation - Fixed initialization to work properly with Symbol properties - Changed from lazy loading to direct initialization (required for Symbol properties) - Improved error messages using typeErrorById - Added proper JavaDoc comments Testing: - Iterator constructor works as expected - Symbol.toStringTag and Symbol.iterator properties properly set - Iterator() and new Iterator() correctly throw TypeError Related to #1696
1 parent fce6929 commit a5b0893

File tree

3 files changed

+1830
-3392
lines changed

3 files changed

+1830
-3392
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2+
*
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6+
7+
package org.mozilla.javascript;
8+
9+
/**
10+
* The global Iterator constructor object as defined in ES2025.
11+
*
12+
* <p>This implements the Iterator constructor which serves as the base for iterator helper methods.
13+
* The constructor itself is not directly callable or constructable per spec.
14+
*
15+
* <p>See: https://tc39.es/proposal-iterator-helpers/#sec-iterator-constructor
16+
*/
17+
public final class NativeIteratorConstructor extends BaseFunction {
18+
private static final long serialVersionUID = 1L;
19+
20+
private static final String ITERATOR_NAME = "Iterator";
21+
public static final String ITERATOR_PROTOTYPE_TAG = "IteratorPrototype";
22+
23+
private NativeIteratorConstructor() {
24+
// Private constructor for singleton pattern
25+
}
26+
27+
/**
28+
* Initialize the Iterator constructor in the given scope.
29+
*
30+
* @param scope the scope to initialize in
31+
* @param sealed whether to seal the objects
32+
*/
33+
public static void init(ScriptableObject scope, boolean sealed) {
34+
Context cx = Context.getContext();
35+
NativeIteratorConstructor constructor = new NativeIteratorConstructor();
36+
constructor.setParentScope(scope);
37+
constructor.setPrototype(getFunctionPrototype(scope));
38+
39+
// Create Iterator.prototype
40+
NativeObject prototype = new NativeObject();
41+
prototype.setParentScope(scope);
42+
prototype.setPrototype(getObjectPrototype(scope));
43+
44+
// Define Symbol.toStringTag on Iterator.prototype
45+
prototype.defineProperty(
46+
SymbolKey.TO_STRING_TAG,
47+
ITERATOR_NAME,
48+
ScriptableObject.DONTENUM | ScriptableObject.READONLY);
49+
50+
// Define Symbol.iterator on Iterator.prototype - returns this
51+
BaseFunction iteratorMethod =
52+
new BaseFunction() {
53+
@Override
54+
public Object call(
55+
Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
56+
// ES2025: %Iterator.prototype%[@@iterator] just returns this
57+
return thisObj;
58+
}
59+
60+
@Override
61+
public String getFunctionName() {
62+
return "[Symbol.iterator]";
63+
}
64+
};
65+
prototype.defineProperty(SymbolKey.ITERATOR, iteratorMethod, ScriptableObject.DONTENUM);
66+
67+
// Set up constructor properties per ES2025 spec
68+
int attrs =
69+
ScriptableObject.DONTENUM | ScriptableObject.READONLY | ScriptableObject.PERMANENT;
70+
constructor.defineProperty("prototype", prototype, attrs);
71+
constructor.defineProperty(
72+
"name", ITERATOR_NAME, ScriptableObject.DONTENUM | ScriptableObject.READONLY);
73+
constructor.defineProperty(
74+
"length",
75+
Integer.valueOf(0),
76+
ScriptableObject.DONTENUM | ScriptableObject.READONLY);
77+
78+
if (sealed) {
79+
constructor.sealObject();
80+
prototype.sealObject();
81+
}
82+
83+
// Define Iterator as a global property
84+
ScriptableObject.defineProperty(
85+
scope, ITERATOR_NAME, constructor, ScriptableObject.DONTENUM);
86+
87+
// Store prototype for later use by iterator helpers and ES6Iterator
88+
scope.associateValue(ITERATOR_PROTOTYPE_TAG, prototype);
89+
}
90+
91+
@Override
92+
public String getClassName() {
93+
return ITERATOR_NAME;
94+
}
95+
96+
@Override
97+
public String getFunctionName() {
98+
return ITERATOR_NAME;
99+
}
100+
101+
/**
102+
* Iterator() called as a function - always throws per ES2025 spec.
103+
*
104+
* @throws EcmaError TypeError as Iterator is not callable
105+
*/
106+
@Override
107+
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
108+
throw ScriptRuntime.typeErrorById("msg.not.ctor", ITERATOR_NAME);
109+
}
110+
111+
/**
112+
* new Iterator() - always throws per ES2025 spec.
113+
*
114+
* @throws EcmaError TypeError as Iterator is not constructable
115+
*/
116+
@Override
117+
public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
118+
throw ScriptRuntime.typeErrorById("msg.not.ctor", ITERATOR_NAME);
119+
}
120+
121+
/**
122+
* Get the Iterator prototype from the scope.
123+
*
124+
* @param scope the scope to look in
125+
* @return the Iterator.prototype object, or null if not initialized
126+
*/
127+
public static Scriptable getIteratorPrototype(Scriptable scope) {
128+
Scriptable top = ScriptableObject.getTopLevelScope(scope);
129+
Object proto = ScriptableObject.getTopScopeValue(top, ITERATOR_PROTOTYPE_TAG);
130+
return proto instanceof Scriptable ? (Scriptable) proto : null;
131+
}
132+
}

rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@ public static ScriptableObject initSafeStandardObjects(
256256
new LazilyLoadedCtor(scope, "BigInt", sealed, true, NativeBigInt::init);
257257
new LazilyLoadedCtor(scope, "Proxy", sealed, true, NativeProxy::init);
258258
new LazilyLoadedCtor(scope, "Reflect", sealed, true, NativeReflect::init);
259+
// ES2025 Iterator - must be initialized after Symbol for Symbol properties to work
260+
NativeIteratorConstructor.init(scope, sealed);
259261
}
260262

261263
if (scope instanceof TopLevel) {

0 commit comments

Comments
 (0)