Skip to content

Commit 67b27f0

Browse files
[RSC-PATCH] Walk parsed JSON instead of using reviver for parsing RSC payload
Rebuild react-server-dom-webpack from React fork rsc-patches/v19.0.3 with the reviveModel optimization (backport of facebook/react#35776). Replaces JSON.parse reviver callback with plain JSON.parse() followed by a recursive reviveModel() walk in pure JS, yielding ~75% speedup in RSC chunk deserialization by eliminating C++→JS boundary crossings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent aba1d26 commit 67b27f0

9 files changed

Lines changed: 451 additions & 316 deletions

src/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,7 @@
10381038
chunk.value = null;
10391039
chunk.reason = null;
10401040
try {
1041-
var value = JSON.parse(resolvedModel, chunk._response._fromJSON),
1041+
var value = parseModel(chunk._response, resolvedModel),
10421042
resolveListeners = chunk.value;
10431043
null !== resolveListeners &&
10441044
((chunk.value = null),
@@ -1470,6 +1470,7 @@
14701470
get: function () {
14711471
return "This object has been omitted by React in the console log to avoid sending too much data from the server. Try logging smaller or more specific objects.";
14721472
},
1473+
set: function () {},
14731474
enumerable: !0,
14741475
configurable: !1
14751476
}),
@@ -1510,7 +1511,6 @@
15101511
this._nonce = nonce;
15111512
this._chunks = chunks;
15121513
this._stringDecoder = new TextDecoder();
1513-
this._fromJSON = null;
15141514
this._rowLength = this._rowTag = this._rowID = this._rowState = 0;
15151515
this._buffer = [];
15161516
this._tempRefs = temporaryReferences;
@@ -1525,7 +1525,6 @@
15251525
this._replayConsole = replayConsole;
15261526
this._rootEnvironmentName =
15271527
void 0 === environmentName ? "Server" : environmentName;
1528-
this._fromJSON = createFromJSONCallback(this);
15291528
}
15301529
function resolveModel(response, id, model) {
15311530
var chunks = response._chunks,
@@ -1554,7 +1553,7 @@
15541553
function resolveModule(response, id, model) {
15551554
var chunks = response._chunks,
15561555
chunk = chunks.get(id);
1557-
model = JSON.parse(model, response._fromJSON);
1556+
model = parseModel(response, model);
15581557
var clientReference = resolveClientReference(
15591558
response._bundlerConfig,
15601559
model
@@ -1801,7 +1800,7 @@
18011800
return response;
18021801
}
18031802
function resolveHint(response, code, model) {
1804-
response = JSON.parse(model, response._fromJSON);
1803+
response = parseModel(response, model);
18051804
model = ReactDOMSharedInternals.d;
18061805
switch (code) {
18071806
case "D":
@@ -1959,7 +1958,7 @@
19591958
}
19601959
function resolveConsoleEntry(response, value) {
19611960
if (response._replayConsole) {
1962-
var payload = JSON.parse(value, response._fromJSON);
1961+
var payload = parseModel(response, value);
19631962
value = payload[0];
19641963
var stackTrace = payload[1],
19651964
owner = payload[2],
@@ -2120,67 +2119,90 @@
21202119
resolveModel(response, id, row);
21212120
}
21222121
}
2123-
function createFromJSONCallback(response) {
2124-
return function (key, value) {
2125-
if ("string" === typeof value)
2126-
return parseModelString(response, this, key, value);
2127-
if ("object" === typeof value && null !== value) {
2122+
function parseModel(response, json) {
2123+
json = JSON.parse(json);
2124+
return reviveModel(response, json, { "": json }, "");
2125+
}
2126+
function reviveModel(response, value, parentObject, key) {
2127+
if ("string" === typeof value)
2128+
return "$" === value[0]
2129+
? parseModelString(response, parentObject, key, value)
2130+
: value;
2131+
if ("object" !== typeof value || null === value) return value;
2132+
if (isArrayImpl(value)) {
2133+
for (var i = 0; i < value.length; i++)
2134+
value[i] = reviveModel(response, value[i], value, "" + i);
2135+
if (value[0] === REACT_ELEMENT_TYPE) {
21282136
if (value[0] === REACT_ELEMENT_TYPE)
2129-
if (
2130-
((key = value[4]),
2131-
(value = {
2137+
b: {
2138+
i = value[4];
2139+
value = {
21322140
$$typeof: REACT_ELEMENT_TYPE,
21332141
type: value[1],
21342142
key: value[2],
21352143
props: value[3],
2136-
_owner: null === key ? response._debugRootOwner : key
2137-
}),
2144+
_owner: null === i ? response._debugRootOwner : i
2145+
};
21382146
Object.defineProperty(value, "ref", {
21392147
enumerable: !1,
21402148
get: nullRefGetter
2141-
}),
2142-
(value._store = {}),
2149+
});
2150+
value._store = {};
21432151
Object.defineProperty(value._store, "validated", {
21442152
configurable: !1,
21452153
enumerable: !1,
21462154
writable: !0,
21472155
value: 1
2148-
}),
2156+
});
21492157
Object.defineProperty(value, "_debugInfo", {
21502158
configurable: !1,
21512159
enumerable: !1,
21522160
writable: !0,
21532161
value: null
2154-
}),
2155-
null !== initializingHandler)
2156-
) {
2157-
var handler = initializingHandler;
2158-
initializingHandler = handler.parent;
2159-
handler.errored
2160-
? ((key = new ReactPromise(
2162+
});
2163+
if (null !== initializingHandler) {
2164+
i = initializingHandler;
2165+
initializingHandler = i.parent;
2166+
if (i.errored) {
2167+
response = new ReactPromise(
21612168
"rejected",
21622169
null,
2163-
handler.value,
2170+
i.value,
21642171
response
2165-
)),
2166-
(value = {
2172+
);
2173+
value = {
21672174
name: getComponentNameFromType(value.type) || "",
21682175
owner: value._owner
2169-
}),
2170-
(key._debugInfo = [value]),
2171-
(value = createLazyChunkWrapper(key)))
2172-
: 0 < handler.deps &&
2173-
((key = new ReactPromise("blocked", null, null, response)),
2174-
(handler.value = value),
2175-
(handler.chunk = key),
2176-
(value = Object.freeze.bind(Object, value.props)),
2177-
key.then(value, value),
2178-
(value = createLazyChunkWrapper(key)));
2179-
} else Object.freeze(value.props);
2180-
return value;
2176+
};
2177+
response._debugInfo = [value];
2178+
response = createLazyChunkWrapper(response);
2179+
break b;
2180+
}
2181+
if (0 < i.deps) {
2182+
response = new ReactPromise("blocked", null, null, response);
2183+
i.value = value;
2184+
i.chunk = response;
2185+
value = Object.freeze.bind(Object, value.props);
2186+
response.then(value, value);
2187+
response = createLazyChunkWrapper(response);
2188+
break b;
2189+
}
2190+
} else Object.freeze(value.props);
2191+
response = value;
2192+
}
2193+
else response = value;
2194+
return response;
21812195
}
21822196
return value;
2183-
};
2197+
}
2198+
for (i in value)
2199+
"__proto__" === i
2200+
? delete value[i]
2201+
: ((parentObject = reviveModel(response, value[i], value, i)),
2202+
void 0 !== parentObject
2203+
? (value[i] = parentObject)
2204+
: delete value[i]);
2205+
return value;
21842206
}
21852207
function createResponseFromOptions(options) {
21862208
return new ResponseInstance(

src/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.production.js

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ function initializeModelChunk(chunk) {
665665
chunk.value = null;
666666
chunk.reason = null;
667667
try {
668-
var value = JSON.parse(resolvedModel, chunk._response._fromJSON),
668+
var value = parseModel(chunk._response, resolvedModel),
669669
resolveListeners = chunk.value;
670670
null !== resolveListeners &&
671671
((chunk.value = null),
@@ -1033,11 +1033,9 @@ function ResponseInstance(
10331033
this._nonce = nonce;
10341034
this._chunks = chunks;
10351035
this._stringDecoder = new TextDecoder();
1036-
this._fromJSON = null;
10371036
this._rowLength = this._rowTag = this._rowID = this._rowState = 0;
10381037
this._buffer = [];
10391038
this._tempRefs = temporaryReferences;
1040-
this._fromJSON = createFromJSONCallback(this);
10411039
}
10421040
function resolveBuffer(response, id, buffer) {
10431041
var chunks = response._chunks,
@@ -1049,7 +1047,7 @@ function resolveBuffer(response, id, buffer) {
10491047
function resolveModule(response, id, model) {
10501048
var chunks = response._chunks,
10511049
chunk = chunks.get(id);
1052-
model = JSON.parse(model, response._fromJSON);
1050+
model = parseModel(response, model);
10531051
var clientReference = resolveClientReference(response._bundlerConfig, model);
10541052
if ((model = preloadModule(clientReference))) {
10551053
if (chunk) {
@@ -1357,7 +1355,7 @@ function processFullBinaryRow(response, id, tag, buffer, chunk) {
13571355
case 72:
13581356
id = buffer[0];
13591357
buffer = buffer.slice(1);
1360-
response = JSON.parse(buffer, response._fromJSON);
1358+
response = parseModel(response, buffer);
13611359
buffer = ReactDOMSharedInternals.d;
13621360
switch (id) {
13631361
case "D":
@@ -1447,45 +1445,58 @@ function processFullBinaryRow(response, id, tag, buffer, chunk) {
14471445
);
14481446
}
14491447
}
1450-
function createFromJSONCallback(response) {
1451-
return function (key, value) {
1452-
if ("string" === typeof value)
1453-
return parseModelString(response, this, key, value);
1454-
if ("object" === typeof value && null !== value) {
1455-
if (value[0] === REACT_ELEMENT_TYPE) {
1456-
if (
1457-
((key = {
1448+
function parseModel(response, json) {
1449+
json = JSON.parse(json);
1450+
return reviveModel(response, json, { "": json }, "");
1451+
}
1452+
function reviveModel(response, value, parentObject, key) {
1453+
if ("string" === typeof value)
1454+
return "$" === value[0]
1455+
? parseModelString(response, parentObject, key, value)
1456+
: value;
1457+
if ("object" !== typeof value || null === value) return value;
1458+
if (isArrayImpl(value)) {
1459+
for (var i = 0; i < value.length; i++)
1460+
value[i] = reviveModel(response, value[i], value, "" + i);
1461+
if (value[0] === REACT_ELEMENT_TYPE) {
1462+
if (value[0] === REACT_ELEMENT_TYPE)
1463+
b: {
1464+
value = {
14581465
$$typeof: REACT_ELEMENT_TYPE,
14591466
type: value[1],
14601467
key: value[2],
14611468
ref: null,
14621469
props: value[3]
1463-
}),
1464-
null !== initializingHandler)
1465-
)
1466-
if (
1467-
((value = initializingHandler),
1468-
(initializingHandler = value.parent),
1469-
value.errored)
1470-
)
1471-
(key = new ReactPromise("rejected", null, value.value, response)),
1472-
(key = createLazyChunkWrapper(key));
1473-
else if (0 < value.deps) {
1474-
var blockedChunk = new ReactPromise(
1475-
"blocked",
1476-
null,
1477-
null,
1478-
response
1479-
);
1480-
value.value = key;
1481-
value.chunk = blockedChunk;
1482-
key = createLazyChunkWrapper(blockedChunk);
1470+
};
1471+
if (null !== initializingHandler) {
1472+
i = initializingHandler;
1473+
initializingHandler = i.parent;
1474+
if (i.errored) {
1475+
response = new ReactPromise("rejected", null, i.value, response);
1476+
response = createLazyChunkWrapper(response);
1477+
break b;
1478+
}
1479+
if (0 < i.deps) {
1480+
response = new ReactPromise("blocked", null, null, response);
1481+
i.value = value;
1482+
i.chunk = response;
1483+
response = createLazyChunkWrapper(response);
1484+
break b;
1485+
}
14831486
}
1484-
} else key = value;
1485-
return key;
1487+
response = value;
1488+
}
1489+
else response = value;
1490+
return response;
14861491
}
14871492
return value;
1488-
};
1493+
}
1494+
for (i in value)
1495+
"__proto__" === i
1496+
? delete value[i]
1497+
: ((parentObject = reviveModel(response, value[i], value, i)),
1498+
void 0 !== parentObject ? (value[i] = parentObject) : delete value[i]);
1499+
return value;
14891500
}
14901501
function createResponseFromOptions(options) {
14911502
return new ResponseInstance(

0 commit comments

Comments
 (0)