<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE bugzilla SYSTEM "https://bugs.webkit.org/page.cgi?id=bugzilla.dtd">

<bugzilla version="5.0.4.1"
          urlbase="https://bugs.webkit.org/"
          
          maintainer="admin@webkit.org"
>

    <bug>
          <bug_id>295660</bug_id>
          
          <creation_ts>2025-07-09 14:23:25 -0700</creation_ts>
          <short_desc>Instantiating an extended class inside overwritten Array.prototype[Symbol.iterator] throws RangeError</short_desc>
          <delta_ts>2026-05-12 22:08:08 -0700</delta_ts>
          <reporter_accessible>1</reporter_accessible>
          <cclist_accessible>1</cclist_accessible>
          <classification_id>1</classification_id>
          <classification>Unclassified</classification>
          <product>WebKit</product>
          <component>JavaScriptCore</component>
          <version>WebKit Nightly Build</version>
          <rep_platform>Unspecified</rep_platform>
          <op_sys>Unspecified</op_sys>
          <bug_status>RESOLVED</bug_status>
          <resolution>FIXED</resolution>
          
          
          <bug_file_loc></bug_file_loc>
          <status_whiteboard></status_whiteboard>
          <keywords>InRadar</keywords>
          <priority>P2</priority>
          <bug_severity>Normal</bug_severity>
          <target_milestone>---</target_milestone>
          
          
          <everconfirmed>1</everconfirmed>
          <reporter name="Meghan Denny">meghan</reporter>
          <assigned_to name="Nobody">webkit-unassigned</assigned_to>
          <cc>me</cc>
    
    <cc>syg</cc>
    
    <cc>webkit-bug-importer</cc>
          

      

      

      

          <comment_sort_order>oldest_to_newest</comment_sort_order>  
          <long_desc isprivate="0" >
    <commentid>2128816</commentid>
    <comment_count>0</comment_count>
    <who name="Meghan Denny">meghan</who>
    <bug_when>2025-07-09 14:23:25 -0700</bug_when>
    <thetext>Code to reproduce

```js
Array.prototype[Symbol.iterator] = function () {
  class A {}
  class B extends A {}
  new B();
};
for (const _ of []) {
}
```

Expected Behavior

```
❯ ~/WebKit/WebKitBuild/Debug/bin/jsc test.js
Exception: TypeError: Iterator result interface is not an object.
global code@test.js:6:14
```

Actual Behavior

```
❯ ~/WebKit/WebKitBuild/Debug/bin/jsc test.js
Exception: RangeError: Maximum call stack size exceeded.
B@
@test.js:4:8
B@
@test.js:4:8
B@
@test.js:4:8
B@
@test.js:4:8
...
```

Observed in

jsc shell @master
Safari Version 18.5 (20621.2.5.11.8)</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2130647</commentid>
    <comment_count>1</comment_count>
    <who name="Radar WebKit Bug Importer">webkit-bug-importer</who>
    <bug_when>2025-07-16 14:24:14 -0700</bug_when>
    <thetext>&lt;rdar://problem/155999441&gt;</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2203065</commentid>
    <comment_count>2</comment_count>
    <who name="Itsuru Nomaki">me</who>
    <bug_when>2026-04-22 00:00:52 -0700</bug_when>
    <thetext># About

This bug appears to be caused by behavior that is inconsitsent with the spec note in the runtime semantics for class definition evaluation:

&gt; NOTE: This branch behaves similarly to constructor(...args) { super(...args); }. The most notable distinction is that while the aforementioned ECMAScript source text observably calls the @@iterator method on %Array.prototype%, this function does not.
https://tc39.es/proposal-class-brand-check/#sec-runtime-semantics-classdefinitionevaluation

In this testcase, overriding `Array.prototype[Symbol.iterator]` causes re-entrancy through the implicit derived constructor path, eventually leading to unbounded recursion.

However, JSC currently synthesizes `(function (...args) { super(...args); })` for default derived constructors internally (at `builtins/BuiltinExecutables.cpp:L60 / defaultConstructorSourceCode()`), and compiles it through the normal rest/spread-based path rather than using a dedicated non-observable forwarding path.

As a result, the synthesized constructor materializes rest arguments and performs spread before `super_construct_varargs`, which makes the operation observably consult `Array.prototype[Symbol.iterator]`.

The following dumped bytecode shows the relevant part of the synthesized default derived constructor:

Environment:

* Revision: `ef14489fb3cc`
* Machine: MacBook Air (M2, 2022), 16 GB RAM

```text
bb#1
Predecessors: [ ]
[   0] enter              
[   1] mov                dst:loc5, src:callee
[   4] mov                dst:loc6, src:this
[   7] mov                dst:this, src:&lt;JSValue()&gt;(const0)
[  10] mov                dst:loc7, src:&lt;JSValue()&gt;(const0)
[  13] create_rest        dst:loc7, numParametersToSkip:0
[  16] get_prototype_of   dst:loc8, value:callee, valueProfile:1
[  20] mov                dst:loc11, src:loc7
[  23] spread             dst:loc11, argument:loc11
[  26] mov                dst:loc12, src:loc6
[  29] super_construct_varargs dst:loc8, callee:loc8, thisValue:loc12, arguments:loc11, firstFree:loc13, firstVarArg:0, valueProfile:2
[  38] is_empty           dst:loc13, operand:this
[  41] jtrue              condition:loc13, targetLabel:6(-&gt;47)
Successors: [ #3 #2 ]
```

# Fixing Plans

There seem to be two possible directions for fixing this issue.

### 1. Stop synthesizing the source text for default derived constructors

One possible fix is to stop synthesizing source text such as `(function (...args) { super(...args); })` for default derived constructors, introduce an explicit `isDefaultDerivedConstructor` flag in `UnlinkedFunctionExecutable`, and add a dedicated bytecode-generation path in `BytecodeGenerator` for this case.

This direction appears more principled, because it avoids modeling the implicit derived constructor as ordinary rest/spread-based source code in the first place. In particular, it would make it possible to implement a non-observable forwarding path that does not go through `create_rest` / `spread`.

However, this approach seems to have a relatively large implementation surface. It would affect the executable/function metadata layer in addition to bytecode generation. Also, at least from my current investigation, it is not yet clear how the actual arguments should be forwarded to `super` without using rest/spread while still keeping the implementation self-contained and consistent with the existing machinery.

### 2. Restrict the fix to `emitBytecode`

Another possible fix is to keep the current synthesized source text, but special-case the default derived constructor path during bytecode generation, for example around `FunctionCallValueNode::emitBytecode`, so that the synthesized `super(...args)` does not get compiled through the normal observable rest/spread path.

This direction appears smaller in scope, since it would localize the fix to bytecode generation and avoid broader changes to constructor synthesis or executable metadata.

The downside is that the synthesized source code itself would remain in the program, even though it would no longer accurately describe the effective execution path. That may be confusing for future maintenance. In addition, I do not yet have a clear way to obtain the actual argument count at runtime from this path without introducing further machinery, so even this seemingly smaller approach still has an unresolved implementation issue.

This is my first time investigating and proposing a change in JSC, so my understanding of some parts of the codebase may still be incomplete. I would greatly appreciate any feedback, corrections, or guidance on the analysis and the proposed directions above.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2204938</commentid>
    <comment_count>3</comment_count>
    <who name="Itsuru Nomaki">me</who>
    <bug_when>2026-04-27 08:15:11 -0700</bug_when>
    <thetext>Pull Request: https://github.com/WebKit/WebKit/pull/63689</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2210387</commentid>
    <comment_count>4</comment_count>
    <who name="EWS">ews-feeder</who>
    <bug_when>2026-05-12 22:08:06 -0700</bug_when>
    <thetext>Committed 313130@main (cf863e88b16a): &lt;https://commits.webkit.org/313130@main&gt;

Reviewed commits have been landed. Closing PR #63689 and removing active labels.</thetext>
  </long_desc>
      
      

    </bug>

</bugzilla>