Compare commits

...

1 Commits

Author SHA1 Message Date
Joe Savona
aa7193720b [compiler] Name anonymous functions from inlined useCallbacks
@eps1lon flagged this case. Inlined useCallback has an extra LoadLocal indirection which caused us not to add a name. While I was there I added some extra checks to make sure we don't generate names for a given node twice (just in case).
2025-09-24 08:56:51 -07:00
3 changed files with 63 additions and 36 deletions

View File

@@ -19,7 +19,7 @@ export function nameAnonymousFunctions(fn: HIRFunction): void {
const parentName = fn.id; const parentName = fn.id;
const functions = nameAnonymousFunctionsImpl(fn); const functions = nameAnonymousFunctionsImpl(fn);
function visit(node: Node, prefix: string): void { function visit(node: Node, prefix: string): void {
if (node.generatedName != null) { if (node.generatedName != null && node.fn.nameHint == null) {
/** /**
* Note that we don't generate a name for functions that already had one, * Note that we don't generate a name for functions that already had one,
* so we'll only add the prefix to anonymous functions regardless of * so we'll only add the prefix to anonymous functions regardless of
@@ -70,6 +70,10 @@ function nameAnonymousFunctionsImpl(fn: HIRFunction): Array<Node> {
if (name != null && name.kind === 'named') { if (name != null && name.kind === 'named') {
names.set(lvalue.identifier.id, name.value); names.set(lvalue.identifier.id, name.value);
} }
const func = functions.get(value.place.identifier.id);
if (func != null) {
functions.set(lvalue.identifier.id, func);
}
break; break;
} }
case 'PropertyLoad': { case 'PropertyLoad': {
@@ -106,6 +110,7 @@ function nameAnonymousFunctionsImpl(fn: HIRFunction): Array<Node> {
const variableName = value.lvalue.place.identifier.name; const variableName = value.lvalue.place.identifier.name;
if ( if (
node != null && node != null &&
node.generatedName == null &&
variableName != null && variableName != null &&
variableName.kind === 'named' variableName.kind === 'named'
) { ) {
@@ -137,7 +142,7 @@ function nameAnonymousFunctionsImpl(fn: HIRFunction): Array<Node> {
continue; continue;
} }
const node = functions.get(arg.identifier.id); const node = functions.get(arg.identifier.id);
if (node != null) { if (node != null && node.generatedName == null) {
const generatedName = const generatedName =
fnArgCount > 1 ? `${calleeName}(arg${i})` : `${calleeName}()`; fnArgCount > 1 ? `${calleeName}(arg${i})` : `${calleeName}()`;
node.generatedName = generatedName; node.generatedName = generatedName;
@@ -152,7 +157,7 @@ function nameAnonymousFunctionsImpl(fn: HIRFunction): Array<Node> {
continue; continue;
} }
const node = functions.get(attr.place.identifier.id); const node = functions.get(attr.place.identifier.id);
if (node != null) { if (node != null && node.generatedName == null) {
const elementName = const elementName =
value.tag.kind === 'BuiltinTag' value.tag.kind === 'BuiltinTag'
? value.tag.name ? value.tag.name

View File

@@ -4,15 +4,19 @@
```javascript ```javascript
// @enableNameAnonymousFunctions // @enableNameAnonymousFunctions
import {useEffect} from 'react'; import {useCallback, useEffect} from 'react';
import {identity, Stringify, useIdentity} from 'shared-runtime'; import {identity, Stringify, useIdentity} from 'shared-runtime';
import * as SharedRuntime from 'shared-runtime'; import * as SharedRuntime from 'shared-runtime';
function Component(props) { function Component(props) {
function named() { function named() {
const inner = () => props.named; const inner = () => props.named;
return inner(); const innerIdentity = identity(() => props.named);
return inner(innerIdentity());
} }
const callback = useCallback(() => {
return 'ok';
}, []);
const namedVariable = function () { const namedVariable = function () {
return props.namedVariable; return props.namedVariable;
}; };
@@ -30,6 +34,7 @@ function Component(props) {
return ( return (
<> <>
{named()} {named()}
{callback()}
{namedVariable()} {namedVariable()}
{methodCall()} {methodCall()}
{call()} {call()}
@@ -63,7 +68,7 @@ export const TODO_FIXTURE_ENTRYPOINT = {
```javascript ```javascript
import { c as _c } from "react/compiler-runtime"; // @enableNameAnonymousFunctions import { c as _c } from "react/compiler-runtime"; // @enableNameAnonymousFunctions
import { useEffect } from "react"; import { useCallback, useEffect } from "react";
import { identity, Stringify, useIdentity } from "shared-runtime"; import { identity, Stringify, useIdentity } from "shared-runtime";
import * as SharedRuntime from "shared-runtime"; import * as SharedRuntime from "shared-runtime";
@@ -75,7 +80,12 @@ function Component(props) {
const inner = { "Component[named > inner]": () => props.named }[ const inner = { "Component[named > inner]": () => props.named }[
"Component[named > inner]" "Component[named > inner]"
]; ];
return inner(); const innerIdentity = identity(
{ "Component[named > identity()]": () => props.named }[
"Component[named > identity()]"
],
);
return inner(innerIdentity());
}; };
$[0] = props.named; $[0] = props.named;
$[1] = t0; $[1] = t0;
@@ -83,6 +93,8 @@ function Component(props) {
t0 = $[1]; t0 = $[1];
} }
const named = t0; const named = t0;
const callback = _ComponentCallback;
let t1; let t1;
if ($[2] !== props.namedVariable) { if ($[2] !== props.namedVariable) {
t1 = { t1 = {
@@ -197,57 +209,62 @@ function Component(props) {
} else { } else {
t9 = $[18]; t9 = $[18];
} }
let t10; const t10 = callback();
let t11;
if ($[19] !== namedVariable) { if ($[19] !== namedVariable) {
t10 = namedVariable(); t11 = namedVariable();
$[19] = namedVariable; $[19] = namedVariable;
$[20] = t10; $[20] = t11;
} else { } else {
t10 = $[20]; t11 = $[20];
}
const t11 = methodCall();
const t12 = call();
let t13;
if ($[21] !== hookArgument) {
t13 = hookArgument();
$[21] = hookArgument;
$[22] = t13;
} else {
t13 = $[22];
} }
const t12 = methodCall();
const t13 = call();
let t14; let t14;
if ($[21] !== hookArgument) {
t14 = hookArgument();
$[21] = hookArgument;
$[22] = t14;
} else {
t14 = $[22];
}
let t15;
if ( if (
$[23] !== builtinElementAttr || $[23] !== builtinElementAttr ||
$[24] !== namedElementAttr || $[24] !== namedElementAttr ||
$[25] !== t10 || $[25] !== t11 ||
$[26] !== t11 || $[26] !== t12 ||
$[27] !== t12 || $[27] !== t13 ||
$[28] !== t13 || $[28] !== t14 ||
$[29] !== t9 $[29] !== t9
) { ) {
t14 = ( t15 = (
<> <>
{t9} {t9}
{t10} {t10}
{t11} {t11}
{t12} {t12}
{t13}
{builtinElementAttr} {builtinElementAttr}
{namedElementAttr} {namedElementAttr}
{t13} {t14}
</> </>
); );
$[23] = builtinElementAttr; $[23] = builtinElementAttr;
$[24] = namedElementAttr; $[24] = namedElementAttr;
$[25] = t10; $[25] = t11;
$[26] = t11; $[26] = t12;
$[27] = t12; $[27] = t13;
$[28] = t13; $[28] = t14;
$[29] = t9; $[29] = t9;
$[30] = t14; $[30] = t15;
} else { } else {
t14 = $[30]; t15 = $[30];
} }
return t14; return t15;
}
function _ComponentCallback() {
return "ok";
} }
export const TODO_FIXTURE_ENTRYPOINT = { export const TODO_FIXTURE_ENTRYPOINT = {

View File

@@ -1,14 +1,18 @@
// @enableNameAnonymousFunctions // @enableNameAnonymousFunctions
import {useEffect} from 'react'; import {useCallback, useEffect} from 'react';
import {identity, Stringify, useIdentity} from 'shared-runtime'; import {identity, Stringify, useIdentity} from 'shared-runtime';
import * as SharedRuntime from 'shared-runtime'; import * as SharedRuntime from 'shared-runtime';
function Component(props) { function Component(props) {
function named() { function named() {
const inner = () => props.named; const inner = () => props.named;
return inner(); const innerIdentity = identity(() => props.named);
return inner(innerIdentity());
} }
const callback = useCallback(() => {
return 'ok';
}, []);
const namedVariable = function () { const namedVariable = function () {
return props.namedVariable; return props.namedVariable;
}; };
@@ -26,6 +30,7 @@ function Component(props) {
return ( return (
<> <>
{named()} {named()}
{callback()}
{namedVariable()} {namedVariable()}
{methodCall()} {methodCall()}
{call()} {call()}