Skip to content

Commit

Permalink
[compiler] Support method-call version of macro functions
Browse files Browse the repository at this point in the history
Adds fixtures for `macro.namespace(...)` style invocations which we use internally in some cases instead of just `macro(...)`. I tried every example i could think of that could possibly break it (including basing one off of another fixture where we hit an invariant related due to a temporary being emitted for a method call), and they all worked. I just had to fix an existing bug where we early return in some cases instead of continuing, which is a holdover from when this pass was originally written as a ReactiveFunction visitor.

ghstack-source-id: c01f45b3ef6f42b6d1f1ff0508aea258000e0fce
Pull Request resolved: #29899
  • Loading branch information
josephsavona committed Jun 17, 2024
1 parent 92219ff commit dbc5f36
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ function* runWithEnvironment(
memoizeFbtOperandsInSameScope(hir);
yield log({
kind: "hir",
name: "MemoizeFbtOperandsInSameScope",
name: "MemoizeFbtAndMacroOperandsInSameScope",
value: hir,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
import { eachReactiveValueOperand } from "./visitors";

/**
* This pass supports the
* This pass supports the `fbt` translation system (https://facebook.github.io/fbt/)
* as well as similar user-configurable macro-like APIs where it's important that
* the name of the function not be changed, and it's literal arguments not be turned
Expand Down Expand Up @@ -75,7 +74,7 @@ function visit(
for (const instruction of block.instructions) {
const { lvalue, value } = instruction;
if (lvalue === null) {
return;
continue;
}
if (
value.kind === "Primitive" &&
Expand All @@ -96,7 +95,7 @@ function visit(
} else if (isFbtCallExpression(fbtValues, value)) {
const fbtScope = lvalue.identifier.scope;
if (fbtScope === null) {
return;
continue;
}

/*
Expand All @@ -122,7 +121,7 @@ function visit(
) {
const fbtScope = lvalue.identifier.scope;
if (fbtScope === null) {
return;
continue;
}

/*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@

## Input

```javascript
// @compilationMode(infer) @enableAssumeHooksFollowRulesOfReact:false @customMacros(cx)
import { identity } from "shared-runtime";

const DARK = "dark";

function Component() {
const theme = useTheme();
return (
<div
className={cx.foo({
"styles/light": true,
"styles/dark": identity([theme.getTheme()]),
})}
/>
);
}

function cx(obj) {
const classes = [];
for (const [key, value] of Object.entries(obj)) {
if (value) {
classes.push(key);
}
}
return classes.join(" ");
}

function useTheme() {
return {
getTheme() {
return DARK;
},
};
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime"; // @compilationMode(infer) @enableAssumeHooksFollowRulesOfReact:false @customMacros(cx)
import { identity } from "shared-runtime";

const DARK = "dark";

function Component() {
const $ = _c(2);
const theme = useTheme();

const t0 = cx.foo({
"styles/light": true,
"styles/dark": identity([theme.getTheme()]),
});
let t1;
if ($[0] !== t0) {
t1 = <div className={t0} />;
$[0] = t0;
$[1] = t1;
} else {
t1 = $[1];
}
return t1;
}

function cx(obj) {
const classes = [];
for (const [key, value] of Object.entries(obj)) {
if (value) {
classes.push(key);
}
}
return classes.join(" ");
}

function useTheme() {
return {
getTheme() {
return DARK;
},
};
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};

```
### Eval output
(kind: exception) cx.foo is not a function
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// @compilationMode(infer) @enableAssumeHooksFollowRulesOfReact:false @customMacros(cx)
import { identity } from "shared-runtime";

const DARK = "dark";

function Component() {
const theme = useTheme();
return (
<div
className={cx.foo({
"styles/light": true,
"styles/dark": identity([theme.getTheme()]),
})}
/>
);
}

function cx(obj) {
const classes = [];
for (const [key, value] of Object.entries(obj)) {
if (value) {
classes.push(key);
}
}
return classes.join(" ");
}

function useTheme() {
return {
getTheme() {
return DARK;
},
};
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@

## Input

```javascript
import { makeArray } from "shared-runtime";

function Component() {
const items = makeArray("foo", "bar", "", null, "baz", false, "merp");
const classname = cx.namespace(...items.filter(isNonEmptyString));
return <div className={classname}>Ok</div>;
}

function isNonEmptyString(s) {
return typeof s === "string" && s.trim().length !== 0;
}

const cx = {
namespace(...items) {
return items.join(" ");
},
};

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime";
import { makeArray } from "shared-runtime";

function Component() {
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const items = makeArray("foo", "bar", "", null, "baz", false, "merp");
const classname = cx.namespace(...items.filter(isNonEmptyString));
t0 = <div className={classname}>Ok</div>;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}

function isNonEmptyString(s) {
return typeof s === "string" && s.trim().length !== 0;
}

const cx = {
namespace(...items) {
return items.join(" ");
},
};

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};

```
### Eval output
(kind: ok) <div class="foo bar baz merp">Ok</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { makeArray } from "shared-runtime";

function Component() {
const items = makeArray("foo", "bar", "", null, "baz", false, "merp");
const classname = cx.namespace(...items.filter(isNonEmptyString));
return <div className={classname}>Ok</div>;
}

function isNonEmptyString(s) {
return typeof s === "string" && s.trim().length !== 0;
}

const cx = {
namespace(...items) {
return items.join(" ");
},
};

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};

0 comments on commit dbc5f36

Please sign in to comment.