Skip to content

Commit 7606b29

Browse files
committed
[compiler][optim] Add shape for Array.from
(see title)
1 parent 54e602d commit 7606b29

10 files changed

+385
-20
lines changed

compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,29 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [
119119
],
120120
/*
121121
* https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.from
122-
* Array.from(arrayLike, optionalFn, optionalThis) not added because
123-
* the Effect of `arrayLike` is polymorphic i.e.
122+
* Array.from(arrayLike, optionalFn, optionalThis)
123+
* Note that the Effect of `arrayLike` is polymorphic i.e.
124124
* - Effect.read if
125125
* - it does not have an @iterator property and is array-like
126126
* (i.e. has a length property)
127127
* - it is an iterable object whose iterator does not mutate itself
128128
* - Effect.mutate if it is a self-mutative iterator (e.g. a generator
129129
* function)
130130
*/
131+
[
132+
'from',
133+
addFunction(DEFAULT_SHAPES, [], {
134+
positionalParams: [
135+
Effect.ConditionallyMutate,
136+
Effect.ConditionallyMutate,
137+
Effect.ConditionallyMutate,
138+
],
139+
restParam: Effect.Read,
140+
returnType: {kind: 'Object', shapeId: BuiltInArrayId},
141+
calleeEffect: Effect.Read,
142+
returnValueKind: ValueKind.Mutable,
143+
}),
144+
],
131145
[
132146
'of',
133147
// Array.of(element0, ..., elementN)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
2+
## Input
3+
4+
```javascript
5+
import {useIdentity, Stringify} from 'shared-runtime';
6+
7+
/**
8+
* TODO: Note that this `Array.from` is inferred to be mutating its first
9+
* argument. This is because React Compiler's typing system does not yet support
10+
* annotating a function with a set of argument match cases + distinct
11+
* definitions (polymorphism).
12+
*
13+
* In this case, we should be able to infer that the `Array.from` call is
14+
* not mutating its 0th argument.
15+
* The 0th argument should be typed as having `effect:Mutate` only when
16+
* (1) it might be a mutable iterable or
17+
* (2) the 1st argument might mutate its callee
18+
*/
19+
function Component({value}) {
20+
const arr = [{value: 'foo'}, {value: 'bar'}, {value}];
21+
useIdentity();
22+
const derived = Array.from(arr, (x, idx) => ({...x, id: idx}));
23+
return <Stringify>{derived.at(-1)}</Stringify>;
24+
}
25+
26+
export const FIXTURE_ENTRYPOINT = {
27+
fn: Component,
28+
params: [{value: 5}],
29+
sequentialRenders: [{value: 5}, {value: 6}, {value: 6}],
30+
};
31+
32+
```
33+
34+
## Code
35+
36+
```javascript
37+
import { c as _c } from "react/compiler-runtime";
38+
import { useIdentity, Stringify } from "shared-runtime";
39+
40+
/**
41+
* TODO: Note that this `Array.from` is inferred to be mutating its first
42+
* argument. This is because React Compiler's typing system does not yet support
43+
* annotating a function with a set of argument match cases + distinct
44+
* definitions (polymorphism).
45+
*
46+
* In this case, we should be able to infer that the `Array.from` call is
47+
* not mutating its 0th argument.
48+
* The 0th argument should be typed as having `effect:Mutate` only when
49+
* (1) it might be a mutable iterable or
50+
* (2) the 1st argument might mutate its callee
51+
*/
52+
function Component(t0) {
53+
const $ = _c(4);
54+
const { value } = t0;
55+
const arr = [{ value: "foo" }, { value: "bar" }, { value }];
56+
useIdentity();
57+
const derived = Array.from(arr, _temp);
58+
let t1;
59+
if ($[0] !== derived) {
60+
t1 = derived.at(-1);
61+
$[0] = derived;
62+
$[1] = t1;
63+
} else {
64+
t1 = $[1];
65+
}
66+
let t2;
67+
if ($[2] !== t1) {
68+
t2 = <Stringify>{t1}</Stringify>;
69+
$[2] = t1;
70+
$[3] = t2;
71+
} else {
72+
t2 = $[3];
73+
}
74+
return t2;
75+
}
76+
function _temp(x, idx) {
77+
return { ...x, id: idx };
78+
}
79+
80+
export const FIXTURE_ENTRYPOINT = {
81+
fn: Component,
82+
params: [{ value: 5 }],
83+
sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }],
84+
};
85+
86+
```
87+
88+
### Eval output
89+
(kind: ok) <div>{"children":{"value":5,"id":2}}</div>
90+
<div>{"children":{"value":6,"id":2}}</div>
91+
<div>{"children":{"value":6,"id":2}}</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {useIdentity, Stringify} from 'shared-runtime';
2+
3+
/**
4+
* TODO: Note that this `Array.from` is inferred to be mutating its first
5+
* argument. This is because React Compiler's typing system does not yet support
6+
* annotating a function with a set of argument match cases + distinct
7+
* definitions (polymorphism).
8+
*
9+
* In this case, we should be able to infer that the `Array.from` call is
10+
* not mutating its 0th argument.
11+
* The 0th argument should be typed as having `effect:Mutate` only when
12+
* (1) it might be a mutable iterable or
13+
* (2) the 1st argument might mutate its callee
14+
*/
15+
function Component({value}) {
16+
const arr = [{value: 'foo'}, {value: 'bar'}, {value}];
17+
useIdentity();
18+
const derived = Array.from(arr, (x, idx) => ({...x, id: idx}));
19+
return <Stringify>{derived.at(-1)}</Stringify>;
20+
}
21+
22+
export const FIXTURE_ENTRYPOINT = {
23+
fn: Component,
24+
params: [{value: 5}],
25+
sequentialRenders: [{value: 5}, {value: 6}, {value: 6}],
26+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
2+
## Input
3+
4+
```javascript
5+
import {useIdentity, Stringify} from 'shared-runtime';
6+
7+
/**
8+
* TODO: Note that this `Array.from` is inferred to be mutating its first
9+
* argument. This is because React Compiler's typing system does not yet support
10+
* annotating a function with a set of argument match cases + distinct
11+
* definitions (polymorphism)
12+
*
13+
* In this case, we should be able to infer that the `Array.from` call is
14+
* not mutating its 0th argument.
15+
* The 0th argument should be typed as having `effect:Mutate` only when
16+
* (1) it might be a mutable iterable or
17+
* (2) the 1st argument might mutate its callee
18+
*/
19+
function Component({value}) {
20+
const arr = [{value: 'foo'}, {value: 'bar'}, {value}];
21+
useIdentity();
22+
const derived = Array.from(arr);
23+
return <Stringify>{derived.at(-1)}</Stringify>;
24+
}
25+
26+
export const FIXTURE_ENTRYPOINT = {
27+
fn: Component,
28+
params: [{value: 5}],
29+
sequentialRenders: [{value: 5}, {value: 6}, {value: 6}],
30+
};
31+
32+
```
33+
34+
## Code
35+
36+
```javascript
37+
import { c as _c } from "react/compiler-runtime";
38+
import { useIdentity, Stringify } from "shared-runtime";
39+
40+
/**
41+
* TODO: Note that this `Array.from` is inferred to be mutating its first
42+
* argument. This is because React Compiler's typing system does not yet support
43+
* annotating a function with a set of argument match cases + distinct
44+
* definitions (polymorphism)
45+
*
46+
* In this case, we should be able to infer that the `Array.from` call is
47+
* not mutating its 0th argument.
48+
* The 0th argument should be typed as having `effect:Mutate` only when
49+
* (1) it might be a mutable iterable or
50+
* (2) the 1st argument might mutate its callee
51+
*/
52+
function Component(t0) {
53+
const $ = _c(4);
54+
const { value } = t0;
55+
const arr = [{ value: "foo" }, { value: "bar" }, { value }];
56+
useIdentity();
57+
const derived = Array.from(arr);
58+
let t1;
59+
if ($[0] !== derived) {
60+
t1 = derived.at(-1);
61+
$[0] = derived;
62+
$[1] = t1;
63+
} else {
64+
t1 = $[1];
65+
}
66+
let t2;
67+
if ($[2] !== t1) {
68+
t2 = <Stringify>{t1}</Stringify>;
69+
$[2] = t1;
70+
$[3] = t2;
71+
} else {
72+
t2 = $[3];
73+
}
74+
return t2;
75+
}
76+
77+
export const FIXTURE_ENTRYPOINT = {
78+
fn: Component,
79+
params: [{ value: 5 }],
80+
sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }],
81+
};
82+
83+
```
84+
85+
### Eval output
86+
(kind: ok) <div>{"children":{"value":5}}</div>
87+
<div>{"children":{"value":6}}</div>
88+
<div>{"children":{"value":6}}</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {useIdentity, Stringify} from 'shared-runtime';
2+
3+
/**
4+
* TODO: Note that this `Array.from` is inferred to be mutating its first
5+
* argument. This is because React Compiler's typing system does not yet support
6+
* annotating a function with a set of argument match cases + distinct
7+
* definitions (polymorphism)
8+
*
9+
* In this case, we should be able to infer that the `Array.from` call is
10+
* not mutating its 0th argument.
11+
* The 0th argument should be typed as having `effect:Mutate` only when
12+
* (1) it might be a mutable iterable or
13+
* (2) the 1st argument might mutate its callee
14+
*/
15+
function Component({value}) {
16+
const arr = [{value: 'foo'}, {value: 'bar'}, {value}];
17+
useIdentity();
18+
const derived = Array.from(arr);
19+
return <Stringify>{derived.at(-1)}</Stringify>;
20+
}
21+
22+
export const FIXTURE_ENTRYPOINT = {
23+
fn: Component,
24+
params: [{value: 5}],
25+
sequentialRenders: [{value: 5}, {value: 6}, {value: 6}],
26+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
2+
## Input
3+
4+
```javascript
5+
import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime';
6+
7+
function Component({value}) {
8+
const arr = [{value: 'foo'}, {value: 'bar'}, {value}];
9+
useIdentity();
10+
const derived = Array.from(arr, mutateAndReturn);
11+
return <Stringify>{derived.at(-1)}</Stringify>;
12+
}
13+
14+
export const FIXTURE_ENTRYPOINT = {
15+
fn: Component,
16+
params: [{value: 5}],
17+
sequentialRenders: [{value: 5}, {value: 6}, {value: 6}],
18+
};
19+
20+
```
21+
22+
## Code
23+
24+
```javascript
25+
import { c as _c } from "react/compiler-runtime";
26+
import { mutateAndReturn, Stringify, useIdentity } from "shared-runtime";
27+
28+
function Component(t0) {
29+
const $ = _c(4);
30+
const { value } = t0;
31+
const arr = [{ value: "foo" }, { value: "bar" }, { value }];
32+
useIdentity();
33+
const derived = Array.from(arr, mutateAndReturn);
34+
let t1;
35+
if ($[0] !== derived) {
36+
t1 = derived.at(-1);
37+
$[0] = derived;
38+
$[1] = t1;
39+
} else {
40+
t1 = $[1];
41+
}
42+
let t2;
43+
if ($[2] !== t1) {
44+
t2 = <Stringify>{t1}</Stringify>;
45+
$[2] = t1;
46+
$[3] = t2;
47+
} else {
48+
t2 = $[3];
49+
}
50+
return t2;
51+
}
52+
53+
export const FIXTURE_ENTRYPOINT = {
54+
fn: Component,
55+
params: [{ value: 5 }],
56+
sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }],
57+
};
58+
59+
```
60+
61+
### Eval output
62+
(kind: ok) <div>{"children":{"value":5,"wat0":"joe"}}</div>
63+
<div>{"children":{"value":6,"wat0":"joe"}}</div>
64+
<div>{"children":{"value":6,"wat0":"joe"}}</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime';
2+
3+
function Component({value}) {
4+
const arr = [{value: 'foo'}, {value: 'bar'}, {value}];
5+
useIdentity();
6+
const derived = Array.from(arr, mutateAndReturn);
7+
return <Stringify>{derived.at(-1)}</Stringify>;
8+
}
9+
10+
export const FIXTURE_ENTRYPOINT = {
11+
fn: Component,
12+
params: [{value: 5}],
13+
sequentialRenders: [{value: 5}, {value: 6}, {value: 6}],
14+
};

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md

+16-8
Original file line numberDiff line numberDiff line change
@@ -68,21 +68,29 @@ function Validate({ x, input }) {
6868
}
6969
function useFoo(input) {
7070
"use memo";
71-
const $ = _c(3);
71+
const $ = _c(5);
7272

7373
const x = Array.from([{}]);
7474
useIdentity();
75-
x.push([input]);
7675
let t0;
77-
if ($[0] !== input || $[1] !== x) {
78-
t0 = <Validate x={x} input={input} />;
76+
if ($[0] !== input) {
77+
t0 = [input];
7978
$[0] = input;
80-
$[1] = x;
81-
$[2] = t0;
79+
$[1] = t0;
80+
} else {
81+
t0 = $[1];
82+
}
83+
x.push(t0);
84+
let t1;
85+
if ($[2] !== input || $[3] !== x) {
86+
t1 = <Validate x={x} input={input} />;
87+
$[2] = input;
88+
$[3] = x;
89+
$[4] = t1;
8290
} else {
83-
t0 = $[2];
91+
t1 = $[4];
8492
}
85-
return t0;
93+
return t1;
8694
}
8795

8896
export const FIXTURE_ENTRYPOINT = {

0 commit comments

Comments
 (0)