mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Update base for Update on "[compiler][ez] Rename disableMemoizationForDebugging to just disableMemoization"
Summary: We don't really need to make positive claims about what a particular mode is for in the name [ghstack-poisoned]
This commit is contained in:
+1
-197
@@ -7,32 +7,6 @@ aliases:
|
||||
- &environment
|
||||
TZ: /usr/share/zoneinfo/America/Los_Angeles
|
||||
|
||||
- &restore_yarn_cache_fixtures_dom
|
||||
restore_cache:
|
||||
name: Restore yarn cache for fixtures/dom
|
||||
keys:
|
||||
- v2-yarn_cache-{{ arch }}-{{ checksum "yarn.lock" }}-fixtures/dom
|
||||
|
||||
- &yarn_install_fixtures_dom
|
||||
run:
|
||||
name: Install dependencies in fixtures/dom
|
||||
working_directory: fixtures/dom
|
||||
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
|
||||
|
||||
- &yarn_install_fixtures_dom_retry
|
||||
run:
|
||||
name: Install dependencies in fixtures/dom (retry)
|
||||
when: on_fail
|
||||
working_directory: fixtures/dom
|
||||
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
|
||||
|
||||
- &save_yarn_cache_fixtures_dom
|
||||
save_cache:
|
||||
name: Save yarn cache for fixtures/dom
|
||||
key: v2-yarn_cache-{{ arch }}-{{ checksum "yarn.lock" }}-fixtures/dom
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
|
||||
- &TEST_PARALLELISM 20
|
||||
|
||||
- &attach_workspace
|
||||
@@ -97,7 +71,7 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- setup_node_modules
|
||||
- run: yarn build
|
||||
- run: yarn build --ci=circleci
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
@@ -222,52 +196,6 @@ jobs:
|
||||
RELEASE_CHANNEL: experimental
|
||||
command: ./scripts/circleci/run_devtools_e2e_tests.js
|
||||
|
||||
run_fixtures_flight_tests:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
# Fixture copies some built packages from the workroot after install.
|
||||
# That means dependencies of the built packages are not installed.
|
||||
# We need to install dependencies of the workroot to fulfill all dependency constraints
|
||||
- setup_node_modules
|
||||
- restore_cache:
|
||||
name: Restore yarn cache of fixture
|
||||
keys:
|
||||
- v2-yarn_cache_fixtures_flight-{{ arch }}-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: Install fixture dependencies
|
||||
working_directory: fixtures/flight
|
||||
command: |
|
||||
yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
|
||||
if [ $? -ne 0 ]; then
|
||||
yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
|
||||
fi
|
||||
- save_cache:
|
||||
name: Save yarn cache of fixture
|
||||
key: v2-yarn_cache_fixtures_flight-{{ arch }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
- run:
|
||||
working_directory: fixtures/flight
|
||||
name: Playwright install deps
|
||||
command: |
|
||||
npx playwright install
|
||||
sudo npx playwright install-deps
|
||||
- run:
|
||||
name: Run tests
|
||||
working_directory: fixtures/flight
|
||||
command: yarn test
|
||||
environment:
|
||||
# Otherwise the webserver is a blackbox
|
||||
DEBUG: pw:webserver
|
||||
- store_artifacts:
|
||||
path: fixtures/flight/playwright-report
|
||||
- store_artifacts:
|
||||
path: fixtures/flight/test-results
|
||||
|
||||
run_devtools_tests_for_versions:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
@@ -311,75 +239,6 @@ jobs:
|
||||
- store_artifacts:
|
||||
path: ./tmp/screenshots
|
||||
|
||||
yarn_lint_build:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- setup_node_modules
|
||||
- run: yarn lint-build
|
||||
|
||||
yarn_check_release_dependencies:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- setup_node_modules
|
||||
- run: yarn check-release-dependencies
|
||||
|
||||
|
||||
check_error_codes:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace: *attach_workspace
|
||||
- setup_node_modules
|
||||
- run:
|
||||
name: Search build artifacts for unminified errors
|
||||
command: |
|
||||
yarn extract-errors
|
||||
git diff --quiet || (echo "Found unminified errors. Either update the error codes map or disable error minification for the affected build, if appropriate." && false)
|
||||
|
||||
yarn_test_build:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
parallelism: *TEST_PARALLELISM
|
||||
parameters:
|
||||
args:
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- setup_node_modules
|
||||
- run: yarn test --build <<parameters.args>> --ci=circleci
|
||||
|
||||
RELEASE_CHANNEL_stable_yarn_test_dom_fixtures:
|
||||
docker: *docker
|
||||
environment: *environment
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- setup_node_modules
|
||||
- *restore_yarn_cache_fixtures_dom
|
||||
- *yarn_install_fixtures_dom
|
||||
- *yarn_install_fixtures_dom_retry
|
||||
- *save_yarn_cache_fixtures_dom
|
||||
- run:
|
||||
name: Run DOM fixture tests
|
||||
environment:
|
||||
RELEASE_CHANNEL: stable
|
||||
working_directory: fixtures/dom
|
||||
command: |
|
||||
yarn predev
|
||||
yarn test --maxWorkers=2
|
||||
|
||||
publish_prerelease:
|
||||
parameters:
|
||||
commit_sha:
|
||||
@@ -421,40 +280,6 @@ workflows:
|
||||
requires:
|
||||
- scrape_warning_messages
|
||||
- yarn_build
|
||||
- yarn_test_build:
|
||||
requires:
|
||||
- yarn_build
|
||||
matrix:
|
||||
parameters:
|
||||
args:
|
||||
# Intentionally passing these as strings instead of creating a
|
||||
# separate parameter per CLI argument, since it's easier to
|
||||
# control/see which combinations we want to run.
|
||||
- "-r=stable --env=development"
|
||||
- "-r=stable --env=production"
|
||||
- "-r=experimental --env=development"
|
||||
- "-r=experimental --env=production"
|
||||
|
||||
# Dev Tools
|
||||
- "--project=devtools -r=experimental"
|
||||
|
||||
# TODO: Update test config to support www build tests
|
||||
# - "-r=www-classic --env=development --variant=false"
|
||||
# - "-r=www-classic --env=production --variant=false"
|
||||
# - "-r=www-classic --env=development --variant=true"
|
||||
# - "-r=www-classic --env=production --variant=true"
|
||||
# - "-r=www-modern --env=development --variant=false"
|
||||
# - "-r=www-modern --env=production --variant=false"
|
||||
# - "-r=www-modern --env=development --variant=true"
|
||||
# - "-r=www-modern --env=production --variant=true"
|
||||
|
||||
# TODO: Update test config to support xplat build tests
|
||||
# - "-r=xplat --env=development --variant=false"
|
||||
# - "-r=xplat --env=development --variant=true"
|
||||
# - "-r=xplat --env=production --variant=false"
|
||||
# - "-r=xplat --env=production --variant=true"
|
||||
|
||||
# TODO: Test more persistent configurations?
|
||||
- download_base_build_for_sizebot:
|
||||
filters:
|
||||
branches:
|
||||
@@ -469,27 +294,6 @@ workflows:
|
||||
requires:
|
||||
- download_base_build_for_sizebot
|
||||
- yarn_build
|
||||
- yarn_lint_build:
|
||||
requires:
|
||||
- yarn_build
|
||||
- yarn_check_release_dependencies:
|
||||
requires:
|
||||
- yarn_build
|
||||
- check_error_codes:
|
||||
requires:
|
||||
- yarn_build
|
||||
- RELEASE_CHANNEL_stable_yarn_test_dom_fixtures:
|
||||
requires:
|
||||
- yarn_build
|
||||
- build_devtools_and_process_artifacts:
|
||||
requires:
|
||||
- yarn_build
|
||||
- run_devtools_e2e_tests:
|
||||
requires:
|
||||
- build_devtools_and_process_artifacts
|
||||
- run_fixtures_flight_tests:
|
||||
requires:
|
||||
- yarn_build
|
||||
|
||||
devtools_regression_tests:
|
||||
unless: << pipeline.parameters.prerelease_commit_sha >>
|
||||
|
||||
@@ -490,6 +490,7 @@ module.exports = {
|
||||
'packages/react-devtools-extensions/**/*.js',
|
||||
'packages/react-devtools-shared/src/hook.js',
|
||||
'packages/react-devtools-shared/src/backend/console.js',
|
||||
'packages/react-devtools-shared/src/backend/DevToolsComponentStackFrame.js',
|
||||
],
|
||||
globals: {
|
||||
__IS_CHROME__: 'readonly',
|
||||
|
||||
@@ -5,9 +5,12 @@ on:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
paths:
|
||||
- "compiler/**"
|
||||
- compiler/**
|
||||
- .github/workflows/compiler-playground.yml
|
||||
|
||||
env:
|
||||
TZ: /usr/share/zoneinfo/America/Los_Angeles
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: compiler
|
||||
@@ -20,8 +23,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: compiler/yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
|
||||
@@ -18,6 +18,7 @@ on:
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTFLAGS: -Dwarnings
|
||||
TZ: /usr/share/zoneinfo/America/Los_Angeles
|
||||
|
||||
defaults:
|
||||
run:
|
||||
|
||||
@@ -5,9 +5,12 @@ on:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
paths:
|
||||
- "compiler/**"
|
||||
- compiler/**
|
||||
- .github/workflows/compiler-typescript.yml
|
||||
|
||||
env:
|
||||
TZ: /usr/share/zoneinfo/America/Los_Angeles
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: compiler
|
||||
@@ -31,8 +34,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: compiler/yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
@@ -50,8 +53,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: compiler/yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
@@ -74,8 +77,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: compiler/yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
|
||||
@@ -5,6 +5,9 @@ on:
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
env:
|
||||
TZ: /usr/share/zoneinfo/America/Los_Angeles
|
||||
|
||||
jobs:
|
||||
check-repro:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -0,0 +1,517 @@
|
||||
name: (Runtime) Build and Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- compiler/**
|
||||
|
||||
env:
|
||||
TZ: /usr/share/zoneinfo/America/Los_Angeles
|
||||
|
||||
jobs:
|
||||
# ----- FLOW -----
|
||||
discover_flow_inline_configs:
|
||||
name: Discover flow inline configs
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.result }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/github-script@v7
|
||||
id: set-matrix
|
||||
with:
|
||||
script: |
|
||||
const inlinedHostConfigs = require('./scripts/shared/inlinedHostConfigs.js');
|
||||
return inlinedHostConfigs.map(config => config.shortName);
|
||||
|
||||
flow:
|
||||
name: Flow check ${{ matrix.flow_inline_config_shortname }}
|
||||
needs: discover_flow_inline_configs
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
flow_inline_config_shortname: ${{ fromJSON(needs.discover_flow_inline_configs.outputs.matrix) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: node ./scripts/tasks/flow-ci ${{ matrix.flow_inline_config_shortname }}
|
||||
|
||||
# ----- FIZZ -----
|
||||
check_generated_fizz_runtime:
|
||||
name: Confirm generated inline Fizz runtime is up to date
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: |
|
||||
yarn generate-inline-fizz-runtime
|
||||
git diff --quiet || (echo "There was a change to the Fizz runtime. Run `yarn generate-inline-fizz-runtime` and check in the result." && false)
|
||||
|
||||
# ----- FEATURE FLAGS -----
|
||||
flags:
|
||||
name: Check flags
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn flags
|
||||
|
||||
# ----- TESTS -----
|
||||
test:
|
||||
name: yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
params:
|
||||
- "-r=stable --env=development"
|
||||
- "-r=stable --env=production"
|
||||
- "-r=experimental --env=development"
|
||||
- "-r=experimental --env=production"
|
||||
- "-r=www-classic --env=development --variant=false"
|
||||
- "-r=www-classic --env=production --variant=false"
|
||||
- "-r=www-classic --env=development --variant=true"
|
||||
- "-r=www-classic --env=production --variant=true"
|
||||
- "-r=www-modern --env=development --variant=false"
|
||||
- "-r=www-modern --env=production --variant=false"
|
||||
- "-r=www-modern --env=development --variant=true"
|
||||
- "-r=www-modern --env=production --variant=true"
|
||||
- "-r=xplat --env=development --variant=false"
|
||||
- "-r=xplat --env=development --variant=true"
|
||||
- "-r=xplat --env=production --variant=false"
|
||||
- "-r=xplat --env=production --variant=true"
|
||||
# TODO: Test more persistent configurations?
|
||||
- "-r=stable --env=development --persistent"
|
||||
- "-r=experimental --env=development --persistent"
|
||||
shard:
|
||||
- 1/5
|
||||
- 2/5
|
||||
- 3/5
|
||||
- 4/5
|
||||
- 5/5
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn test ${{ matrix.params }} --ci=github --shard=${{ matrix.shard }}
|
||||
|
||||
# ----- BUILD -----
|
||||
build_and_lint:
|
||||
name: yarn build and lint
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
# yml is dumb. update the --total arg to yarn build if you change the number of workers
|
||||
worker_id: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
|
||||
release_channel: [stable, experimental]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11.0.22
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn build --index=${{ matrix.worker_id }} --total=20 --r=${{ matrix.release_channel }} --ci=github
|
||||
env:
|
||||
CI: github
|
||||
RELEASE_CHANNEL: ${{ matrix.release_channel }}
|
||||
NODE_INDEX: ${{ matrix.worker_id }}
|
||||
- name: Lint build
|
||||
run: yarn lint-build
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- name: Archive build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build_${{ matrix.worker_id }}_${{ matrix.release_channel }}
|
||||
path: |
|
||||
build
|
||||
|
||||
test_build:
|
||||
name: yarn test-build
|
||||
needs: build_and_lint
|
||||
strategy:
|
||||
matrix:
|
||||
test_params: [
|
||||
# Intentionally passing these as strings instead of creating a
|
||||
# separate parameter per CLI argument, since it's easier to
|
||||
# control/see which combinations we want to run.
|
||||
-r=stable --env=development,
|
||||
-r=stable --env=production,
|
||||
-r=experimental --env=development,
|
||||
-r=experimental --env=production,
|
||||
|
||||
# Dev Tools
|
||||
--project=devtools -r=experimental,
|
||||
|
||||
# TODO: Update test config to support www build tests
|
||||
# - "-r=www-classic --env=development --variant=false"
|
||||
# - "-r=www-classic --env=production --variant=false"
|
||||
# - "-r=www-classic --env=development --variant=true"
|
||||
# - "-r=www-classic --env=production --variant=true"
|
||||
# - "-r=www-modern --env=development --variant=false"
|
||||
# - "-r=www-modern --env=production --variant=false"
|
||||
# - "-r=www-modern --env=development --variant=true"
|
||||
# - "-r=www-modern --env=production --variant=true"
|
||||
|
||||
# TODO: Update test config to support xplat build tests
|
||||
# - "-r=xplat --env=development --variant=false"
|
||||
# - "-r=xplat --env=development --variant=true"
|
||||
# - "-r=xplat --env=production --variant=false"
|
||||
# - "-r=xplat --env=production --variant=true"
|
||||
|
||||
# TODO: Test more persistent configurations?
|
||||
]
|
||||
shard:
|
||||
- 1/3
|
||||
- 2/3
|
||||
- 3/3
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: build
|
||||
merge-multiple: true
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- run: yarn test --build ${{ matrix.test_params }} --shard=${{ matrix.shard }} --ci=github
|
||||
|
||||
process_artifacts_combined:
|
||||
name: Process artifacts combined
|
||||
needs: build_and_lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: build
|
||||
merge-multiple: true
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- run: echo ${{ github.sha }} >> build/COMMIT_SHA
|
||||
- name: Scrape warning messages
|
||||
run: |
|
||||
mkdir -p ./build/__test_utils__
|
||||
node ./scripts/print-warnings/print-warnings.js > build/__test_utils__/ReactAllWarnings.js
|
||||
# Compress build directory into a single tarball for easy download
|
||||
- run: tar -zcvf ./build.tgz ./build
|
||||
# TODO: Migrate scripts to use `build` directory instead of `build2`
|
||||
- run: cp ./build.tgz ./build2.tgz
|
||||
- name: Archive build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: combined_artifacts_${{ github.sha }}
|
||||
path: |
|
||||
./build.tgz
|
||||
./build2.tgz
|
||||
|
||||
check_error_codes:
|
||||
name: Search build artifacts for unminified errors
|
||||
needs: build_and_lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: build
|
||||
merge-multiple: true
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- name: Search build artifacts for unminified errors
|
||||
run: |
|
||||
yarn extract-errors
|
||||
git diff --quiet || (echo "Found unminified errors. Either update the error codes map or disable error minification for the affected build, if appropriate." && false)
|
||||
|
||||
check_release_dependencies:
|
||||
name: Check release dependencies
|
||||
needs: build_and_lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: build
|
||||
merge-multiple: true
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- run: yarn check-release-dependencies
|
||||
|
||||
RELEASE_CHANNEL_stable_yarn_test_dom_fixtures:
|
||||
name: Check fixtures DOM (stable)
|
||||
needs: build_and_lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: v2-yarn_cache_fixtures_dom-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
|
||||
working-directory: fixtures/dom
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: build
|
||||
merge-multiple: true
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- name: Run DOM fixture tests
|
||||
run: |
|
||||
yarn predev
|
||||
yarn test
|
||||
working-directory: fixtures/dom
|
||||
env:
|
||||
RELEASE_CHANNEL: stable
|
||||
|
||||
# ----- FLIGHT -----
|
||||
run_fixtures_flight_tests:
|
||||
name: Run fixtures Flight tests
|
||||
needs: build_and_lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
# Fixture copies some built packages from the workroot after install.
|
||||
# That means dependencies of the built packages are not installed.
|
||||
# We need to install dependencies of the workroot to fulfill all dependency constraints
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: v2-yarn_cache_fixtures_flight-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: build
|
||||
merge-multiple: true
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- name: Install fixture dependencies
|
||||
working-directory: fixtures/flight
|
||||
run: |
|
||||
yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
|
||||
if [ $? -ne 0 ]; then
|
||||
yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
|
||||
fi
|
||||
- name: Playwright install deps
|
||||
working-directory: fixtures/flight
|
||||
run: |
|
||||
npx playwright install
|
||||
sudo npx playwright install-deps
|
||||
- name: Run tests
|
||||
working-directory: fixtures/flight
|
||||
run: yarn test
|
||||
env:
|
||||
# Otherwise the webserver is a blackbox
|
||||
DEBUG: pw:webserver
|
||||
- name: Archive Flight fixture artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: flight-playwright-report
|
||||
path: fixtures/flight/playwright-report
|
||||
- name: Archive Flight fixture artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: flight-test-results
|
||||
path: fixtures/flight/test-results
|
||||
|
||||
# ----- DEVTOOLS -----
|
||||
build_devtools_and_process_artifacts:
|
||||
name: Build DevTools and process artifacts
|
||||
needs: build_and_lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: build
|
||||
merge-multiple: true
|
||||
- run: ./scripts/circleci/pack_and_store_devtools_artifacts.sh
|
||||
env:
|
||||
RELEASE_CHANNEL: experimental
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- name: Archive devtools build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: react-devtools
|
||||
path: build/devtools.tgz
|
||||
# Simplifies getting the extension for local testing
|
||||
- name: Archive chrome extension
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: react-devtools-chrome-extension
|
||||
path: build/devtools/chrome-extension.zip
|
||||
- name: Archive firefox extension
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: react-devtools-firefox-extension
|
||||
path: build/devtools/firefox-extension.zip
|
||||
|
||||
run_devtools_e2e_tests:
|
||||
name: Run DevTools e2e tests
|
||||
needs: build_devtools_and_process_artifacts
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- name: Restore archived build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: build
|
||||
merge-multiple: true
|
||||
- run: |
|
||||
npx playwright install
|
||||
sudo npx playwright install-deps
|
||||
- run: ./scripts/circleci/run_devtools_e2e_tests.js
|
||||
env:
|
||||
RELEASE_CHANNEL: experimental
|
||||
@@ -4,6 +4,9 @@ on:
|
||||
push:
|
||||
branches: [main, meta-www, meta-fbsource]
|
||||
|
||||
env:
|
||||
TZ: /usr/share/zoneinfo/America/Los_Angeles
|
||||
|
||||
jobs:
|
||||
download_artifacts:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
name: (Runtime) Fizz
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'compiler/**'
|
||||
|
||||
jobs:
|
||||
check_generated_fizz_runtime:
|
||||
name: Confirm generated inline Fizz runtime is up to date
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: |
|
||||
yarn generate-inline-fizz-runtime
|
||||
git diff --quiet || (echo "There was a change to the Fizz runtime. Run `yarn generate-inline-fizz-runtime` and check in the result." && false)
|
||||
@@ -1,28 +0,0 @@
|
||||
name: (Runtime) Flags
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'compiler/**'
|
||||
|
||||
jobs:
|
||||
flags:
|
||||
name: Check flags
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn flags
|
||||
@@ -1,47 +0,0 @@
|
||||
name: (Runtime) Flow
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'compiler/**'
|
||||
|
||||
jobs:
|
||||
discover_flow_inline_configs:
|
||||
name: Discover flow inline configs
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.result }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/github-script@v7
|
||||
id: set-matrix
|
||||
with:
|
||||
script: |
|
||||
const inlinedHostConfigs = require('./scripts/shared/inlinedHostConfigs.js');
|
||||
return inlinedHostConfigs.map(config => config.shortName);
|
||||
|
||||
flow:
|
||||
name: Flow check ${{ matrix.flow_inline_config_shortname }}
|
||||
needs: discover_flow_inline_configs
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
flow_inline_config_shortname: ${{ fromJSON(needs.discover_flow_inline_configs.outputs.matrix) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: node ./scripts/tasks/flow-ci ${{ matrix.flow_inline_config_shortname }}
|
||||
@@ -9,6 +9,10 @@ on:
|
||||
inputs:
|
||||
prerelease_commit_sha:
|
||||
required: false
|
||||
|
||||
env:
|
||||
TZ: /usr/share/zoneinfo/America/Los_Angeles
|
||||
|
||||
jobs:
|
||||
test_fuzz:
|
||||
if: inputs.prerelease_commit_sha == ''
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
name: (Runtime) Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'compiler/**'
|
||||
|
||||
env:
|
||||
# Number of workers (one per shard) to spawn
|
||||
SHARD_COUNT: 5
|
||||
|
||||
jobs:
|
||||
# Define the various test parameters and parallelism for this workflow
|
||||
build_test_params:
|
||||
name: Build test params
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
params: ${{ steps.define-params.outputs.result }}
|
||||
shard_id: ${{ steps.define-shards.outputs.result }}
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
id: define-shards
|
||||
with:
|
||||
script: |
|
||||
function range(from, to) {
|
||||
const arr = [];
|
||||
for (let n = from; n <= to; n++) {
|
||||
arr.push(n);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
return range(1, process.env.SHARD_COUNT);
|
||||
- uses: actions/github-script@v7
|
||||
id: define-params
|
||||
with:
|
||||
script: |
|
||||
return [
|
||||
"-r=stable --env=development",
|
||||
"-r=stable --env=production",
|
||||
"-r=experimental --env=development",
|
||||
"-r=experimental --env=production",
|
||||
"-r=www-classic --env=development --variant=false",
|
||||
"-r=www-classic --env=production --variant=false",
|
||||
"-r=www-classic --env=development --variant=true",
|
||||
"-r=www-classic --env=production --variant=true",
|
||||
"-r=www-modern --env=development --variant=false",
|
||||
"-r=www-modern --env=production --variant=false",
|
||||
"-r=www-modern --env=development --variant=true",
|
||||
"-r=www-modern --env=production --variant=true",
|
||||
"-r=xplat --env=development --variant=false",
|
||||
"-r=xplat --env=development --variant=true",
|
||||
"-r=xplat --env=production --variant=false",
|
||||
"-r=xplat --env=production --variant=true",
|
||||
// TODO: Test more persistent configurations?
|
||||
"-r=stable --env=development --persistent",
|
||||
"-r=experimental --env=development --persistent"
|
||||
];
|
||||
|
||||
# Spawn a job for each shard for a given set of test params
|
||||
test:
|
||||
name: yarn test ${{ matrix.params }} (Shard ${{ matrix.shard_id }})
|
||||
runs-on: ubuntu-latest
|
||||
needs: build_test_params
|
||||
strategy:
|
||||
matrix:
|
||||
params: ${{ fromJSON(needs.build_test_params.outputs.params) }}
|
||||
shard_id: ${{ fromJSON(needs.build_test_params.outputs.shard_id) }}
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
id: node_modules
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn test ${{ matrix.params }} --ci=github --shard=${{ matrix.shard_id }}/${{ env.SHARD_COUNT }}
|
||||
@@ -5,6 +5,9 @@ on:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TZ: /usr/share/zoneinfo/America/Los_Angeles
|
||||
|
||||
jobs:
|
||||
prettier:
|
||||
name: Run prettier
|
||||
@@ -13,8 +16,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
@@ -31,8 +34,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
@@ -49,8 +52,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
@@ -67,8 +70,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "yarn"
|
||||
node-version: 18.20.1
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Restore cached node_modules
|
||||
uses: actions/cache@v4
|
||||
|
||||
@@ -5,6 +5,9 @@ on:
|
||||
# Run hourly
|
||||
- cron: '0 * * * *'
|
||||
|
||||
env:
|
||||
TZ: /usr/share/zoneinfo/America/Los_Angeles
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# [React](https://react.dev/) · [](https://github.com/facebook/react/blob/main/LICENSE) [](https://www.npmjs.com/package/react) [](https://circleci.com/gh/facebook/react) [](https://legacy.reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
|
||||
# [React](https://react.dev/) · [](https://github.com/facebook/react/blob/main/LICENSE) [](https://www.npmjs.com/package/react) [](https://circleci.com/gh/facebook/react) [](https://github.com/facebook/react/actions/workflows/runtime_build_and_test.yml) [](https://github.com/facebook/react/actions/workflows/compiler_typescript.yml) [](https://legacy.reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
|
||||
|
||||
React is a JavaScript library for building user interfaces.
|
||||
|
||||
@@ -18,7 +18,7 @@ React has been designed for gradual adoption from the start, and **you can use a
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find the React documentation [on the website](https://react.dev/).
|
||||
You can find the React documentation [on the website](https://react.dev/).
|
||||
|
||||
Check out the [Getting Started](https://react.dev/learn) page for a quick overview.
|
||||
|
||||
@@ -55,7 +55,7 @@ root.render(<HelloMessage name="Taylor" />);
|
||||
|
||||
This example will render "Hello Taylor" into a container on the page.
|
||||
|
||||
You'll notice that we used an HTML-like syntax; [we call it JSX](https://react.dev/learn#writing-markup-with-jsx). JSX is not required to use React, but it makes code more readable, and writing it feels like writing HTML.
|
||||
You'll notice that we used an HTML-like syntax; [we call it JSX](https://react.dev/learn#writing-markup-with-jsx). JSX is not required to use React, but it makes code more readable, and writing it feels like writing HTML.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ import {
|
||||
default as Output,
|
||||
PrintedCompilerPipelineValue,
|
||||
} from "./Output";
|
||||
import { printFunctionWithOutlined } from "babel-plugin-react-compiler/src/HIR/PrintHIR";
|
||||
import { printReactiveFunctionWithOutlined } from "babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction";
|
||||
|
||||
function parseInput(input: string, language: "flow" | "typescript") {
|
||||
// Extract the first line to quickly check for custom test directives
|
||||
@@ -242,7 +244,7 @@ function compile(source: string): [CompilerOutput, "flow" | "typescript"] {
|
||||
kind: "hir",
|
||||
fnName,
|
||||
name: result.name,
|
||||
value: printHIR(result.value.body),
|
||||
value: printFunctionWithOutlined(result.value),
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -251,7 +253,7 @@ function compile(source: string): [CompilerOutput, "flow" | "typescript"] {
|
||||
kind: "reactive",
|
||||
fnName,
|
||||
name: result.name,
|
||||
value: printReactiveFunction(result.value),
|
||||
value: printReactiveFunctionWithOutlined(result.value),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.19.1",
|
||||
"@babel/generator": "^7.19.1",
|
||||
"@babel/parser": "^7.19.1",
|
||||
"@babel/plugin-syntax-typescript": "^7.18.6",
|
||||
"@babel/plugin-transform-block-scoping": "^7.18.9",
|
||||
@@ -55,9 +56,6 @@
|
||||
},
|
||||
"resolutions": {
|
||||
"./**/@babel/parser": "7.7.4",
|
||||
"./**/@babel/types": "7.7.4",
|
||||
"@babel/core": "7.2.0",
|
||||
"@babel/traverse": "7.1.6",
|
||||
"@babel/generator": "7.2.0"
|
||||
"./**/@babel/types": "7.7.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { basename } from "path";
|
||||
const e2eTransformerCacheKey = 1;
|
||||
const forgetOptions: EnvironmentConfig = validateEnvironmentConfig({
|
||||
enableAssumeHooksFollowRulesOfReact: true,
|
||||
enableFunctionOutlining: false,
|
||||
});
|
||||
const debugMode = process.env["DEBUG_FORGET_COMPILER"] != null;
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ import {
|
||||
validateUseMemo,
|
||||
} from "../Validation";
|
||||
import { validateLocalsNotReassignedAfterRender } from "../Validation/ValidateLocalsNotReassignedAfterRender";
|
||||
import { memoizeExistingUseMemos } from "../ReactiveScopes/MemoizeExistingUseMemos";
|
||||
import { outlineFunctions } from "../Optimization/OutlineFunctions";
|
||||
|
||||
export type CompilerPipelineValue =
|
||||
| { kind: "ast"; name: string; value: CodegenFunction }
|
||||
@@ -118,6 +118,7 @@ export function* run(
|
||||
): Generator<CompilerPipelineValue, CodegenFunction> {
|
||||
const contextIdentifiers = findContextIdentifiers(func);
|
||||
const env = new Environment(
|
||||
func.scope,
|
||||
fnType,
|
||||
config,
|
||||
contextIdentifiers,
|
||||
@@ -154,7 +155,7 @@ function* runWithEnvironment(
|
||||
validateContextVariableLValues(hir);
|
||||
validateUseMemo(hir);
|
||||
|
||||
if (env.config.enablePreserveExistingManualUseMemo !== "hook") {
|
||||
if (!env.preserveManualMemo()) {
|
||||
dropManualMemoization(hir);
|
||||
yield log({ kind: "hir", name: "DropManualMemoization", value: hir });
|
||||
}
|
||||
@@ -238,6 +239,11 @@ function* runWithEnvironment(
|
||||
inferReactiveScopeVariables(hir);
|
||||
yield log({ kind: "hir", name: "InferReactiveScopeVariables", value: hir });
|
||||
|
||||
if (env.config.enableFunctionOutlining) {
|
||||
outlineFunctions(hir);
|
||||
yield log({ kind: "hir", name: "OutlineFunctions", value: hir });
|
||||
}
|
||||
|
||||
alignMethodCallScopes(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
@@ -267,19 +273,6 @@ function* runWithEnvironment(
|
||||
value: hir,
|
||||
});
|
||||
|
||||
if (
|
||||
env.config.enablePreserveExistingManualUseMemo === "scope" ||
|
||||
env.config.enableChangeDetection != null ||
|
||||
env.config.disableMemoizationForDebugging
|
||||
) {
|
||||
memoizeExistingUseMemos(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
name: "MemoizeExistingUseMemos",
|
||||
value: hir,
|
||||
});
|
||||
}
|
||||
|
||||
alignReactiveScopesToBlockScopesHIR(hir);
|
||||
yield log({
|
||||
kind: "hir",
|
||||
@@ -390,12 +383,14 @@ function* runWithEnvironment(
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
pruneNonReactiveDependencies(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PruneNonReactiveDependencies",
|
||||
value: reactiveFunction,
|
||||
});
|
||||
if (env.config.enableChangeDetection == null) {
|
||||
pruneNonReactiveDependencies(reactiveFunction);
|
||||
yield log({
|
||||
kind: "reactive",
|
||||
name: "PruneNonReactiveDependencies",
|
||||
value: reactiveFunction,
|
||||
});
|
||||
}
|
||||
|
||||
pruneUnusedScopes(reactiveFunction);
|
||||
yield log({
|
||||
@@ -489,6 +484,9 @@ function* runWithEnvironment(
|
||||
|
||||
const ast = codegenFunction(reactiveFunction, uniqueIdentifiers).unwrap();
|
||||
yield log({ kind: "ast", name: "Codegen", value: ast });
|
||||
for (const outlined of ast.outlined) {
|
||||
yield log({ kind: "ast", name: "Codegen (outlined)", value: outlined.fn });
|
||||
}
|
||||
|
||||
/**
|
||||
* This flag should be only set for unit / fixture tests to check
|
||||
|
||||
@@ -87,6 +87,12 @@ export type BabelFn =
|
||||
| NodePath<t.ArrowFunctionExpression>;
|
||||
|
||||
export type CompileResult = {
|
||||
/**
|
||||
* Distinguishes existing functions that were compiled ('original') from
|
||||
* functions which were outlined. Only original functions need to be gated
|
||||
* if gating mode is enabled.
|
||||
*/
|
||||
kind: "original" | "outlined";
|
||||
originalFn: BabelFn;
|
||||
compiledFn: CodegenFunction;
|
||||
};
|
||||
@@ -265,6 +271,11 @@ export function compileProgram(
|
||||
);
|
||||
const lintError = suppressionsToCompilerError(suppressions);
|
||||
let hasCriticalError = lintError != null;
|
||||
const queue: Array<{
|
||||
kind: "original" | "outlined";
|
||||
fn: BabelFn;
|
||||
fnType: ReactFunctionType;
|
||||
}> = [];
|
||||
const compiledFns: Array<CompileResult> = [];
|
||||
|
||||
const traverseFunction = (fn: BabelFn, pass: CompilerPass): void => {
|
||||
@@ -281,6 +292,47 @@ export function compileProgram(
|
||||
ALREADY_COMPILED.add(fn.node);
|
||||
fn.skip();
|
||||
|
||||
queue.push({ kind: "original", fn, fnType });
|
||||
};
|
||||
|
||||
// Main traversal to compile with Forget
|
||||
program.traverse(
|
||||
{
|
||||
ClassDeclaration(node: NodePath<t.ClassDeclaration>) {
|
||||
/*
|
||||
* Don't visit functions defined inside classes, because they
|
||||
* can reference `this` which is unsafe for compilation
|
||||
*/
|
||||
node.skip();
|
||||
return;
|
||||
},
|
||||
|
||||
ClassExpression(node: NodePath<t.ClassExpression>) {
|
||||
/*
|
||||
* Don't visit functions defined inside classes, because they
|
||||
* can reference `this` which is unsafe for compilation
|
||||
*/
|
||||
node.skip();
|
||||
return;
|
||||
},
|
||||
|
||||
FunctionDeclaration: traverseFunction,
|
||||
|
||||
FunctionExpression: traverseFunction,
|
||||
|
||||
ArrowFunctionExpression: traverseFunction,
|
||||
},
|
||||
{
|
||||
...pass,
|
||||
opts: { ...pass.opts, ...pass.opts },
|
||||
filename: pass.filename ?? null,
|
||||
}
|
||||
);
|
||||
|
||||
const processFn = (
|
||||
fn: BabelFn,
|
||||
fnType: ReactFunctionType
|
||||
): null | CodegenFunction => {
|
||||
if (lintError != null) {
|
||||
/**
|
||||
* Note that Babel does not attach comment nodes to nodes; they are dangling off of the
|
||||
@@ -335,52 +387,59 @@ export function compileProgram(
|
||||
} catch (err) {
|
||||
hasCriticalError ||= isCriticalError(err);
|
||||
handleError(err, pass, fn.node.loc ?? null);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!pass.opts.noEmit && !hasCriticalError) {
|
||||
compiledFns.push({ originalFn: fn, compiledFn });
|
||||
return compiledFn;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Main traversal to compile with Forget
|
||||
program.traverse(
|
||||
{
|
||||
ClassDeclaration(node: NodePath<t.ClassDeclaration>) {
|
||||
/*
|
||||
* Don't visit functions defined inside classes, because they
|
||||
* can reference `this` which is unsafe for compilation
|
||||
*/
|
||||
node.skip();
|
||||
return;
|
||||
},
|
||||
|
||||
ClassExpression(node: NodePath<t.ClassExpression>) {
|
||||
/*
|
||||
* Don't visit functions defined inside classes, because they
|
||||
* can reference `this` which is unsafe for compilation
|
||||
*/
|
||||
node.skip();
|
||||
return;
|
||||
},
|
||||
|
||||
FunctionDeclaration: traverseFunction,
|
||||
|
||||
FunctionExpression: traverseFunction,
|
||||
|
||||
ArrowFunctionExpression: traverseFunction,
|
||||
},
|
||||
{
|
||||
...pass,
|
||||
opts: { ...pass.opts, ...pass.opts },
|
||||
filename: pass.filename ?? null,
|
||||
while (queue.length !== 0) {
|
||||
const current = queue.shift()!;
|
||||
const compiled = processFn(current.fn, current.fnType);
|
||||
if (compiled === null) {
|
||||
continue;
|
||||
}
|
||||
);
|
||||
for (const outlined of compiled.outlined) {
|
||||
CompilerError.invariant(outlined.fn.outlined.length === 0, {
|
||||
reason: "Unexpected nested outlined functions",
|
||||
loc: outlined.fn.loc,
|
||||
});
|
||||
const fn = current.fn.insertAfter(
|
||||
createNewFunctionNode(current.fn, outlined.fn)
|
||||
)[0]!;
|
||||
fn.skip();
|
||||
ALREADY_COMPILED.add(fn.node);
|
||||
if (outlined.type !== null) {
|
||||
CompilerError.throwTodo({
|
||||
reason: `Implement support for outlining React functions (components/hooks)`,
|
||||
loc: outlined.fn.loc,
|
||||
});
|
||||
/*
|
||||
* Above should be as simple as the following, but needs testing:
|
||||
* queue.push({
|
||||
* kind: "outlined",
|
||||
* fn,
|
||||
* fnType: outlined.type,
|
||||
* });
|
||||
*/
|
||||
}
|
||||
}
|
||||
compiledFns.push({
|
||||
kind: current.kind,
|
||||
compiledFn: compiled,
|
||||
originalFn: current.fn,
|
||||
});
|
||||
}
|
||||
|
||||
if (pass.opts.gating != null) {
|
||||
const error = checkFunctionReferencedBeforeDeclarationAtTopLevel(
|
||||
program,
|
||||
compiledFns.map(({ originalFn }) => originalFn)
|
||||
compiledFns.map((result) => {
|
||||
return result.originalFn;
|
||||
})
|
||||
);
|
||||
if (error) {
|
||||
handleError(error, pass, null);
|
||||
@@ -455,10 +514,11 @@ export function compileProgram(
|
||||
* Only insert Forget-ified functions if we have not encountered a critical
|
||||
* error elsewhere in the file, regardless of bailout mode.
|
||||
*/
|
||||
for (const { originalFn, compiledFn } of compiledFns) {
|
||||
for (const result of compiledFns) {
|
||||
const { kind, originalFn, compiledFn } = result;
|
||||
const transformedFn = createNewFunctionNode(originalFn, compiledFn);
|
||||
|
||||
if (gating != null) {
|
||||
if (gating != null && kind === "original") {
|
||||
insertGatedFunctionDeclaration(originalFn, transformedFn, gating);
|
||||
} else {
|
||||
originalFn.replaceWith(transformedFn);
|
||||
|
||||
@@ -129,6 +129,7 @@ export function lower(
|
||||
reactive: false,
|
||||
loc: param.node.loc ?? GeneratedSource,
|
||||
};
|
||||
promoteTemporary(place.identifier);
|
||||
params.push(place);
|
||||
lowerAssignment(
|
||||
builder,
|
||||
@@ -1332,6 +1333,7 @@ function lowerStatement(
|
||||
return;
|
||||
}
|
||||
case "TypeAlias":
|
||||
case "TSInterfaceDeclaration":
|
||||
case "TSTypeAliasDeclaration": {
|
||||
// We do not preserve type annotations/syntax through transformation
|
||||
return;
|
||||
@@ -1358,7 +1360,6 @@ function lowerStatement(
|
||||
case "TSEnumDeclaration":
|
||||
case "TSExportAssignment":
|
||||
case "TSImportEqualsDeclaration":
|
||||
case "TSInterfaceDeclaration":
|
||||
case "TSModuleDeclaration":
|
||||
case "TSNamespaceExportDeclaration":
|
||||
case "WithStatement": {
|
||||
|
||||
@@ -23,14 +23,17 @@ import {
|
||||
BuiltInType,
|
||||
Effect,
|
||||
FunctionType,
|
||||
HIRFunction,
|
||||
IdentifierId,
|
||||
NonLocalBinding,
|
||||
PolyType,
|
||||
ScopeId,
|
||||
Type,
|
||||
ValidatedIdentifier,
|
||||
ValueKind,
|
||||
makeBlockId,
|
||||
makeIdentifierId,
|
||||
makeIdentifierName,
|
||||
makeScopeId,
|
||||
} from "./HIR";
|
||||
import {
|
||||
@@ -41,6 +44,7 @@ import {
|
||||
ShapeRegistry,
|
||||
addHook,
|
||||
} from "./ObjectShape";
|
||||
import { Scope as BabelScope } from "@babel/traverse";
|
||||
|
||||
export const ExternalFunctionSchema = z.object({
|
||||
// Source for the imported module that exports the `importSpecifierName` functions
|
||||
@@ -182,9 +186,7 @@ const EnvironmentConfigSchema = z.object({
|
||||
* that the memoized values remain memoized, the compiler will simply not prune existing calls to
|
||||
* useMemo/useCallback.
|
||||
*/
|
||||
enablePreserveExistingManualUseMemo: z
|
||||
.nullable(z.enum(["hook", "scope"]))
|
||||
.default(null),
|
||||
enablePreserveExistingManualUseMemo: z.boolean().default(false),
|
||||
|
||||
// 🌲
|
||||
enableForest: z.boolean().default(false),
|
||||
@@ -285,6 +287,12 @@ const EnvironmentConfigSchema = z.object({
|
||||
*/
|
||||
enableInstructionReordering: z.boolean().default(false),
|
||||
|
||||
/**
|
||||
* Enables function outlinining, where anonymous functions that do not close over
|
||||
* local variables can be extracted into top-level helper functions.
|
||||
*/
|
||||
enableFunctionOutlining: z.boolean().default(true),
|
||||
|
||||
/*
|
||||
* Enables instrumentation codegen. This emits a dev-mode only call to an
|
||||
* instrumentation function, for components and hooks that Forget compiles.
|
||||
@@ -479,31 +487,6 @@ export function parseConfigPragma(pragma: string): EnvironmentConfig {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
key === "enablePreserveExistingManualUseMemo" &&
|
||||
(val === undefined || val === "true" || val === "scope")
|
||||
) {
|
||||
maybeConfig[key] = "scope";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key === "enablePreserveExistingManualUseMemo" && val === "hook") {
|
||||
maybeConfig[key] = "hook";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
key === "enablePreserveExistingManualUseMemo" &&
|
||||
!(val === "false" || val === "off")
|
||||
) {
|
||||
CompilerError.throwInvalidConfig({
|
||||
reason: `Invalid setting '${val}' for 'enablePreserveExistingManualUseMemo'. Valid settings are 'hook', 'scope', or 'off'.`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof defaultConfig[key as keyof EnvironmentConfig] !== "boolean") {
|
||||
// skip parsing non-boolean properties
|
||||
continue;
|
||||
@@ -552,6 +535,11 @@ export class Environment {
|
||||
#nextIdentifer: number = 0;
|
||||
#nextBlock: number = 0;
|
||||
#nextScope: number = 0;
|
||||
#scope: BabelScope;
|
||||
#outlinedFunctions: Array<{
|
||||
fn: HIRFunction;
|
||||
type: ReactFunctionType | null;
|
||||
}> = [];
|
||||
logger: Logger | null;
|
||||
filename: string | null;
|
||||
code: string | null;
|
||||
@@ -563,6 +551,7 @@ export class Environment {
|
||||
#hoistedIdentifiers: Set<t.Identifier>;
|
||||
|
||||
constructor(
|
||||
scope: BabelScope,
|
||||
fnType: ReactFunctionType,
|
||||
config: EnvironmentConfig,
|
||||
contextIdentifiers: Set<t.Identifier>,
|
||||
@@ -571,6 +560,7 @@ export class Environment {
|
||||
code: string | null,
|
||||
useMemoCacheIdentifier: string
|
||||
) {
|
||||
this.#scope = scope;
|
||||
this.fnType = fnType;
|
||||
this.config = config;
|
||||
this.filename = filename;
|
||||
@@ -631,6 +621,24 @@ export class Environment {
|
||||
return this.#hoistedIdentifiers.has(node);
|
||||
}
|
||||
|
||||
generateGloballyUniqueIdentifierName(
|
||||
name: string | null
|
||||
): ValidatedIdentifier {
|
||||
const identifierNode = this.#scope.generateUidIdentifier(name ?? undefined);
|
||||
return makeIdentifierName(identifierNode.name);
|
||||
}
|
||||
|
||||
outlineFunction(fn: HIRFunction, type: ReactFunctionType | null): void {
|
||||
this.#outlinedFunctions.push({ fn, type });
|
||||
}
|
||||
|
||||
getOutlinedFunctions(): Array<{
|
||||
fn: HIRFunction;
|
||||
type: ReactFunctionType | null;
|
||||
}> {
|
||||
return this.#outlinedFunctions;
|
||||
}
|
||||
|
||||
getGlobalDeclaration(binding: NonLocalBinding): Global | null {
|
||||
if (this.config.hookPattern != null) {
|
||||
const match = new RegExp(this.config.hookPattern).exec(binding.name);
|
||||
@@ -766,6 +774,14 @@ export class Environment {
|
||||
return DefaultMutatingHook;
|
||||
}
|
||||
}
|
||||
|
||||
preserveManualMemo(): boolean {
|
||||
return (
|
||||
this.config.enablePreserveExistingManualUseMemo ||
|
||||
this.config.disableMemoizationForDebugging ||
|
||||
this.config.enableChangeDetection != null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// From https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js#LL18C1-L23C2
|
||||
|
||||
@@ -771,12 +771,7 @@ export type ManualMemoDependency = {
|
||||
kind: "NamedLocal";
|
||||
value: Place;
|
||||
}
|
||||
| {
|
||||
kind: "InlinedGlobal";
|
||||
value: Place;
|
||||
name: string;
|
||||
}
|
||||
| { kind: "Global"; binding: LoadGlobal };
|
||||
| { kind: "Global"; identifierName: string };
|
||||
path: Array<string>;
|
||||
};
|
||||
|
||||
@@ -1173,7 +1168,7 @@ export type NonLocalBinding =
|
||||
imported: string;
|
||||
}
|
||||
// let, const, function, etc declared in the module but outside the current component/hook
|
||||
| { kind: "ModuleLocal"; name: string }
|
||||
| { kind: "ModuleLocal"; name: string; immutable: boolean }
|
||||
// an unresolved binding
|
||||
| { kind: "Global"; name: string };
|
||||
|
||||
@@ -1434,8 +1429,6 @@ export type ReactiveScope = {
|
||||
merged: Set<ScopeId>;
|
||||
|
||||
loc: SourceLocation;
|
||||
|
||||
source: boolean;
|
||||
};
|
||||
|
||||
export type ReactiveScopeDependencies = Set<ReactiveScopeDependency>;
|
||||
|
||||
@@ -271,9 +271,14 @@ export default class HIRBuilder {
|
||||
module: importDeclaration.node.source.value,
|
||||
};
|
||||
} else {
|
||||
const immutable =
|
||||
(path.isVariableDeclaration() && path.node.kind === "const") ||
|
||||
path.isClassDeclaration() ||
|
||||
path.isClassExpression();
|
||||
return {
|
||||
kind: "ModuleLocal",
|
||||
name: originalName,
|
||||
immutable,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import {
|
||||
makeInstructionId,
|
||||
} from ".";
|
||||
import { getPlaceScope } from "../ReactiveScopes/BuildReactiveBlocks";
|
||||
import { isMutable } from "../ReactiveScopes/InferReactiveScopeVariables";
|
||||
import { isMutableAtInstruction } from "../ReactiveScopes/InferReactiveScopeVariables";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
import { getOrInsertDefault } from "../Utils/utils";
|
||||
import {
|
||||
@@ -254,7 +254,7 @@ function visitPlace(
|
||||
* of the stack to the mutated outer scope.
|
||||
*/
|
||||
const placeScope = getPlaceScope(id, place);
|
||||
if (placeScope != null && isMutable({ id } as any, place)) {
|
||||
if (placeScope != null && isMutableAtInstruction({ id } as any, place)) {
|
||||
const placeScopeIdx = activeScopes.indexOf(placeScope);
|
||||
if (placeScopeIdx !== -1 && placeScopeIdx !== activeScopes.length - 1) {
|
||||
joined.union([placeScope, ...activeScopes.slice(placeScopeIdx + 1)]);
|
||||
|
||||
@@ -41,6 +41,14 @@ export type Options = {
|
||||
indent: number;
|
||||
};
|
||||
|
||||
export function printFunctionWithOutlined(fn: HIRFunction): string {
|
||||
const output = [printFunction(fn)];
|
||||
for (const outlined of fn.env.getOutlinedFunctions()) {
|
||||
output.push(`\nfunction ${outlined.fn.id}:\n${printHIR(outlined.fn.body)}`);
|
||||
}
|
||||
return output.join("\n");
|
||||
}
|
||||
|
||||
export function printFunction(fn: HIRFunction): string {
|
||||
const output = [];
|
||||
let definition = "";
|
||||
@@ -843,14 +851,7 @@ export function printManualMemoDependency(
|
||||
): string {
|
||||
let rootStr;
|
||||
if (val.root.kind === "Global") {
|
||||
rootStr = val.root.binding.binding.name;
|
||||
} else if (val.root.kind === "InlinedGlobal") {
|
||||
const nameStr = nameOnly
|
||||
? val.root.value.identifier.name != null
|
||||
? printName(val.root.value.identifier.name)
|
||||
: String(val.root.value.identifier.id)
|
||||
: printIdentifier(val.root.value.identifier);
|
||||
rootStr = `G(${val.root.name}=${nameStr})`;
|
||||
rootStr = val.root.identifierName;
|
||||
} else {
|
||||
CompilerError.invariant(val.root.value.identifier.name?.kind === "named", {
|
||||
reason: "DepsValidation: expected named local variable in depslist",
|
||||
|
||||
@@ -229,10 +229,7 @@ export function* eachInstructionValueOperand(
|
||||
case "StartMemoize": {
|
||||
if (instrValue.deps != null) {
|
||||
for (const dep of instrValue.deps) {
|
||||
if (
|
||||
dep.root.kind === "NamedLocal" ||
|
||||
dep.root.kind === "InlinedGlobal"
|
||||
) {
|
||||
if (dep.root.kind === "NamedLocal") {
|
||||
yield dep.root.value;
|
||||
}
|
||||
}
|
||||
@@ -557,10 +554,7 @@ export function mapInstructionValueOperands(
|
||||
case "StartMemoize": {
|
||||
if (instrValue.deps != null) {
|
||||
for (const dep of instrValue.deps) {
|
||||
if (
|
||||
dep.root.kind === "NamedLocal" ||
|
||||
dep.root.kind === "InlinedGlobal"
|
||||
) {
|
||||
if (dep.root.kind === "NamedLocal") {
|
||||
dep.root.value = fn(dep.root.value);
|
||||
}
|
||||
}
|
||||
|
||||
+21
-68
@@ -58,7 +58,7 @@ export function collectMaybeMemoDependencies(
|
||||
return {
|
||||
root: {
|
||||
kind: "Global",
|
||||
binding: value,
|
||||
identifierName: value.binding.name,
|
||||
},
|
||||
path: [],
|
||||
};
|
||||
@@ -173,53 +173,24 @@ function makeManualMemoizationMarkers(
|
||||
env: Environment,
|
||||
depsList: Array<ManualMemoDependency> | null,
|
||||
memoDecl: Place,
|
||||
manualMemoId: number,
|
||||
isManualUseMemoEnabled: boolean
|
||||
): [
|
||||
Array<TInstruction<StartMemoize> | TInstruction<LoadGlobal>>,
|
||||
TInstruction<FinishMemoize>,
|
||||
] {
|
||||
let globals: Array<TInstruction<StartMemoize> | TInstruction<LoadGlobal>> =
|
||||
[];
|
||||
for (const dep of depsList ?? []) {
|
||||
if (dep.root.kind === "Global" && isManualUseMemoEnabled) {
|
||||
const place = createTemporaryPlace(env, dep.root.binding.loc);
|
||||
globals.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: place,
|
||||
value: {
|
||||
kind: "LoadGlobal",
|
||||
binding: dep.root.binding.binding,
|
||||
loc: dep.root.binding.loc,
|
||||
},
|
||||
loc: dep.root.binding.loc,
|
||||
});
|
||||
dep.root = {
|
||||
kind: "InlinedGlobal",
|
||||
value: place,
|
||||
name: dep.root.binding.binding.name,
|
||||
};
|
||||
}
|
||||
}
|
||||
manualMemoId: number
|
||||
): [TInstruction<StartMemoize>, TInstruction<FinishMemoize>] {
|
||||
return [
|
||||
[
|
||||
...globals,
|
||||
{
|
||||
id: makeInstructionId(0),
|
||||
lvalue: createTemporaryPlace(env, fnExpr.loc),
|
||||
value: {
|
||||
kind: "StartMemoize",
|
||||
manualMemoId,
|
||||
/*
|
||||
* Use deps list from source instead of inferred deps
|
||||
* as dependencies
|
||||
*/
|
||||
deps: depsList,
|
||||
loc: fnExpr.loc,
|
||||
},
|
||||
{
|
||||
id: makeInstructionId(0),
|
||||
lvalue: createTemporaryPlace(env, fnExpr.loc),
|
||||
value: {
|
||||
kind: "StartMemoize",
|
||||
manualMemoId,
|
||||
/*
|
||||
* Use deps list from source instead of inferred deps
|
||||
* as dependencies
|
||||
*/
|
||||
deps: depsList,
|
||||
loc: fnExpr.loc,
|
||||
},
|
||||
],
|
||||
loc: fnExpr.loc,
|
||||
},
|
||||
{
|
||||
id: makeInstructionId(0),
|
||||
lvalue: createTemporaryPlace(env, fnExpr.loc),
|
||||
@@ -362,14 +333,9 @@ function extractManualMemoizationArgs(
|
||||
* eg `React.useMemo()`.
|
||||
*/
|
||||
export function dropManualMemoization(func: HIRFunction): void {
|
||||
const isManualUseMemoEnabled =
|
||||
func.env.config.enablePreserveExistingManualUseMemo === "scope" ||
|
||||
func.env.config.enableChangeDetection != null ||
|
||||
func.env.config.disableMemoizationForDebugging;
|
||||
const isValidationEnabled =
|
||||
func.env.config.validatePreserveExistingMemoizationGuarantees ||
|
||||
func.env.config.enablePreserveExistingMemoizationGuarantees ||
|
||||
isManualUseMemoEnabled;
|
||||
func.env.config.enablePreserveExistingMemoizationGuarantees;
|
||||
const sidemap: IdentifierSidemap = {
|
||||
functions: new Map(),
|
||||
manualMemos: new Map(),
|
||||
@@ -390,11 +356,7 @@ export function dropManualMemoization(func: HIRFunction): void {
|
||||
*/
|
||||
const queuedInserts: Map<
|
||||
InstructionId,
|
||||
Array<
|
||||
| TInstruction<StartMemoize>
|
||||
| TInstruction<FinishMemoize>
|
||||
| TInstruction<LoadGlobal>
|
||||
>
|
||||
TInstruction<StartMemoize> | TInstruction<FinishMemoize>
|
||||
> = new Map();
|
||||
for (const [_, block] of func.body.blocks) {
|
||||
for (let i = 0; i < block.instructions.length; i++) {
|
||||
@@ -457,8 +419,7 @@ export function dropManualMemoization(func: HIRFunction): void {
|
||||
func.env,
|
||||
depsList,
|
||||
memoDecl,
|
||||
nextManualMemoId++,
|
||||
isManualUseMemoEnabled
|
||||
nextManualMemoId++
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -475,7 +436,7 @@ export function dropManualMemoization(func: HIRFunction): void {
|
||||
* ```
|
||||
*/
|
||||
queuedInserts.set(manualMemo.loadInstr.id, startMarker);
|
||||
queuedInserts.set(instr.id, [finishMarker]);
|
||||
queuedInserts.set(instr.id, finishMarker);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -496,16 +457,8 @@ export function dropManualMemoization(func: HIRFunction): void {
|
||||
const insertInstr = queuedInserts.get(instr.id);
|
||||
if (insertInstr != null) {
|
||||
nextInstructions = nextInstructions ?? block.instructions.slice(0, i);
|
||||
const postInstructions: Array<Instruction> = [];
|
||||
insertInstr.forEach((instr) => {
|
||||
if (instr.value.kind === "LoadGlobal") {
|
||||
nextInstructions?.push(instr);
|
||||
} else {
|
||||
postInstructions.push(instr);
|
||||
}
|
||||
});
|
||||
nextInstructions.push(instr);
|
||||
postInstructions.forEach((instr) => nextInstructions?.push(instr));
|
||||
nextInstructions.push(insertInstr);
|
||||
} else if (nextInstructions != null) {
|
||||
nextInstructions.push(instr);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
} from "../HIR/visitors";
|
||||
import {
|
||||
findDisjointMutableValues,
|
||||
isMutable,
|
||||
isMutableAtInstruction,
|
||||
} from "../ReactiveScopes/InferReactiveScopeVariables";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
@@ -232,7 +232,7 @@ export function inferReactivePlaces(fn: HIRFunction): void {
|
||||
case Effect.Store:
|
||||
case Effect.ConditionallyMutate:
|
||||
case Effect.Mutate: {
|
||||
if (isMutable(instruction, operand)) {
|
||||
if (isMutableAtInstruction(instruction, operand)) {
|
||||
const resolvedId = identifierMapping.get(operand.identifier);
|
||||
if (resolvedId !== undefined) {
|
||||
reactiveIdentifiers.markReactiveIdentifier(resolvedId);
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { HIRFunction } from "../HIR";
|
||||
|
||||
export function outlineFunctions(fn: HIRFunction): void {
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
const { value } = instr;
|
||||
|
||||
if (
|
||||
value.kind === "FunctionExpression" ||
|
||||
value.kind === "ObjectMethod"
|
||||
) {
|
||||
// Recurse in case there are inner functions which can be outlined
|
||||
outlineFunctions(value.loweredFunc.func);
|
||||
}
|
||||
|
||||
if (
|
||||
value.kind === "FunctionExpression" &&
|
||||
value.loweredFunc.dependencies.length === 0 &&
|
||||
value.loweredFunc.func.context.length === 0 &&
|
||||
// TODO: handle outlining named functions
|
||||
value.loweredFunc.func.id === null
|
||||
) {
|
||||
const loweredFunc = value.loweredFunc.func;
|
||||
|
||||
const id = fn.env.generateGloballyUniqueIdentifierName(loweredFunc.id);
|
||||
loweredFunc.id = id.value;
|
||||
|
||||
fn.env.outlineFunction(loweredFunc, null);
|
||||
instr.value = {
|
||||
kind: "LoadGlobal",
|
||||
binding: {
|
||||
kind: "Global",
|
||||
name: id.value,
|
||||
},
|
||||
loc: value.loc,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
-13
@@ -7,7 +7,12 @@
|
||||
|
||||
import * as t from "@babel/types";
|
||||
import { createHmac } from "crypto";
|
||||
import { pruneHoistedContexts, pruneUnusedLValues, pruneUnusedLabels } from ".";
|
||||
import {
|
||||
pruneHoistedContexts,
|
||||
pruneUnusedLValues,
|
||||
pruneUnusedLabels,
|
||||
renameVariables,
|
||||
} from ".";
|
||||
import { CompilerError, ErrorSeverity } from "../CompilerError";
|
||||
import { Environment, EnvironmentConfig, ExternalFunction } from "../HIR";
|
||||
import {
|
||||
@@ -45,6 +50,7 @@ import { assertExhaustive } from "../Utils/utils";
|
||||
import { buildReactiveFunction } from "./BuildReactiveFunction";
|
||||
import { SINGLE_CHILD_FBT_TAGS } from "./MemoizeFbtAndMacroOperandsInSameScope";
|
||||
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
|
||||
import { ReactFunctionType } from "../HIR/Environment";
|
||||
|
||||
export const MEMO_CACHE_SENTINEL = "react.memo_cache_sentinel";
|
||||
export const EARLY_RETURN_SENTINEL = "react.early_return_sentinel";
|
||||
@@ -85,6 +91,11 @@ export type CodegenFunction = {
|
||||
* because they were part of a pruned memo block.
|
||||
*/
|
||||
prunedMemoValues: number;
|
||||
|
||||
outlined: Array<{
|
||||
fn: CodegenFunction;
|
||||
type: ReactFunctionType | null;
|
||||
}>;
|
||||
};
|
||||
|
||||
export function codegenFunction(
|
||||
@@ -258,6 +269,29 @@ export function codegenFunction(
|
||||
compiled.body.body.unshift(test);
|
||||
}
|
||||
|
||||
const outlined: CodegenFunction["outlined"] = [];
|
||||
for (const { fn: outlinedFunction, type } of cx.env.getOutlinedFunctions()) {
|
||||
const reactiveFunction = buildReactiveFunction(outlinedFunction);
|
||||
pruneUnusedLabels(reactiveFunction);
|
||||
pruneUnusedLValues(reactiveFunction);
|
||||
pruneHoistedContexts(reactiveFunction);
|
||||
|
||||
const identifiers = renameVariables(reactiveFunction);
|
||||
const codegen = codegenReactiveFunction(
|
||||
new Context(
|
||||
cx.env,
|
||||
reactiveFunction.id ?? "[[ anonymous ]]",
|
||||
identifiers
|
||||
),
|
||||
reactiveFunction
|
||||
);
|
||||
if (codegen.isErr()) {
|
||||
return codegen;
|
||||
}
|
||||
outlined.push({ fn: codegen.unwrap(), type });
|
||||
}
|
||||
compiled.outlined = outlined;
|
||||
|
||||
return compileResult;
|
||||
}
|
||||
|
||||
@@ -306,6 +340,7 @@ function codegenReactiveFunction(
|
||||
memoValues: countMemoBlockVisitor.memoValues,
|
||||
prunedMemoBlocks: countMemoBlockVisitor.prunedMemoBlocks,
|
||||
prunedMemoValues: countMemoBlockVisitor.prunedMemoValues,
|
||||
outlined: [],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -622,11 +657,7 @@ function codegenReactiveScope(
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
cx.env.config.disableMemoizationForDebugging &&
|
||||
!scope.source &&
|
||||
cx.env.config.enableChangeDetection == null
|
||||
) {
|
||||
if (cx.env.config.disableMemoizationForDebugging) {
|
||||
testCondition = t.logicalExpression(
|
||||
"||",
|
||||
testCondition,
|
||||
@@ -636,10 +667,7 @@ function codegenReactiveScope(
|
||||
let computationBlock = codegenBlock(cx, block);
|
||||
|
||||
let memoStatement;
|
||||
if (
|
||||
cx.env.config.enableChangeDetection != null &&
|
||||
changeExpressions.length > 0
|
||||
) {
|
||||
if (cx.env.config.enableChangeDetection != null) {
|
||||
const loc =
|
||||
typeof scope.loc === "symbol"
|
||||
? "unknown location"
|
||||
@@ -647,9 +675,9 @@ function codegenReactiveScope(
|
||||
const detectionFunction =
|
||||
cx.env.config.enableChangeDetection.structuralCheck;
|
||||
const cacheLoadOldValueStatements: Array<t.Statement> = [];
|
||||
const restoreOldValueStatements: Array<t.Statement> = [];
|
||||
const changeDetectionStatements: Array<t.Statement> = [];
|
||||
const idempotenceDetectionStatements: Array<t.Statement> = [];
|
||||
const restoreOldValueStatements: Array<t.Statement> = [];
|
||||
|
||||
for (const {
|
||||
name: { name: nameStr },
|
||||
@@ -691,7 +719,7 @@ function codegenReactiveScope(
|
||||
t.variableDeclarator(t.identifier(loadNameStr), genSlot()),
|
||||
])
|
||||
);
|
||||
if (scope.source || !cx.env.config.disableMemoizationForDebugging) {
|
||||
if (!cx.env.config.disableMemoizationForDebugging) {
|
||||
restoreOldValueStatements.push(
|
||||
t.expressionStatement(
|
||||
t.assignmentExpression("=", t.identifier(nameStr), restoredValue)
|
||||
@@ -740,7 +768,6 @@ function codegenReactiveScope(
|
||||
t.blockStatement([
|
||||
...cacheLoadOldValueStatements,
|
||||
...changeDetectionStatements,
|
||||
...restoreOldValueStatements,
|
||||
])
|
||||
),
|
||||
...cacheStoreStatements,
|
||||
|
||||
+164
-100
@@ -111,13 +111,16 @@ export function inferReactiveScopeVariables(fn: HIRFunction): void {
|
||||
earlyReturnValue: null,
|
||||
merged: new Set(),
|
||||
loc: identifier.loc,
|
||||
source: false,
|
||||
};
|
||||
scopes.set(groupIdentifier, scope);
|
||||
} else {
|
||||
scope.range.start = makeInstructionId(
|
||||
Math.min(scope.range.start, identifier.mutableRange.start)
|
||||
);
|
||||
if (scope.range.start === 0) {
|
||||
scope.range.start = identifier.mutableRange.start;
|
||||
} else if (identifier.mutableRange.start !== 0) {
|
||||
scope.range.start = makeInstructionId(
|
||||
Math.min(scope.range.start, identifier.mutableRange.start)
|
||||
);
|
||||
}
|
||||
scope.range.end = makeInstructionId(
|
||||
Math.max(scope.range.end, identifier.mutableRange.end)
|
||||
);
|
||||
@@ -162,10 +165,7 @@ export function inferReactiveScopeVariables(fn: HIRFunction): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeLocation(
|
||||
l: SourceLocation,
|
||||
r: SourceLocation
|
||||
): SourceLocation {
|
||||
function mergeLocation(l: SourceLocation, r: SourceLocation): SourceLocation {
|
||||
if (l === GeneratedSource) {
|
||||
return r;
|
||||
} else if (r === GeneratedSource) {
|
||||
@@ -185,25 +185,23 @@ export function mergeLocation(
|
||||
}
|
||||
|
||||
// Is the operand mutable at this given instruction
|
||||
export function isMutable({ id }: Instruction, place: Place): boolean {
|
||||
export function isMutableAtInstruction(
|
||||
{ id }: Instruction,
|
||||
place: Place
|
||||
): boolean {
|
||||
const range = place.identifier.mutableRange;
|
||||
return id >= range.start && id < range.end;
|
||||
}
|
||||
|
||||
export function mayAllocate(
|
||||
env: Environment,
|
||||
instruction: Instruction,
|
||||
conservative: boolean
|
||||
): boolean {
|
||||
function mayAllocate(env: Environment, instruction: Instruction): boolean {
|
||||
const { value } = instruction;
|
||||
switch (value.kind) {
|
||||
case "Destructure": {
|
||||
return (
|
||||
doesPatternContainSpreadElement(value.lvalue.pattern) || conservative
|
||||
);
|
||||
return doesPatternContainSpreadElement(value.lvalue.pattern);
|
||||
}
|
||||
case "PostfixUpdate":
|
||||
case "PrefixUpdate":
|
||||
case "Await":
|
||||
case "DeclareLocal":
|
||||
case "DeclareContext":
|
||||
case "StoreLocal":
|
||||
@@ -214,31 +212,26 @@ export function mayAllocate(
|
||||
case "LoadContext":
|
||||
case "StoreContext":
|
||||
case "PropertyDelete":
|
||||
case "ComputedLoad":
|
||||
case "ComputedDelete":
|
||||
case "JSXText":
|
||||
case "TemplateLiteral":
|
||||
case "Primitive":
|
||||
case "GetIterator":
|
||||
case "IteratorNext":
|
||||
case "NextPropertyOf":
|
||||
case "Debugger":
|
||||
case "StartMemoize":
|
||||
case "FinishMemoize":
|
||||
case "UnaryExpression":
|
||||
case "BinaryExpression":
|
||||
case "PropertyLoad":
|
||||
case "StoreGlobal": {
|
||||
return false;
|
||||
}
|
||||
case "PropertyLoad":
|
||||
case "NextPropertyOf":
|
||||
case "ComputedLoad":
|
||||
case "Await": {
|
||||
return conservative;
|
||||
}
|
||||
case "CallExpression":
|
||||
case "MethodCall": {
|
||||
return (
|
||||
conservative || instruction.lvalue.identifier.type.kind !== "Primitive"
|
||||
);
|
||||
return instruction.lvalue.identifier.type.kind !== "Primitive";
|
||||
}
|
||||
case "RegExpLiteral":
|
||||
case "PropertyStore":
|
||||
@@ -263,80 +256,85 @@ export function mayAllocate(
|
||||
}
|
||||
}
|
||||
|
||||
export function collectMutableOperands(
|
||||
fn: HIRFunction,
|
||||
instr: Instruction,
|
||||
conservative: boolean
|
||||
): Array<Identifier> {
|
||||
const operands: Array<Identifier> = [];
|
||||
const range = instr.lvalue.identifier.mutableRange;
|
||||
if (range.end > range.start + 1 || mayAllocate(fn.env, instr, conservative)) {
|
||||
operands.push(instr.lvalue!.identifier);
|
||||
/*
|
||||
* These instructions may pick up external changes due to rules of react violations.
|
||||
* Instructions should be included here if they may change without their inputs changing.
|
||||
* For example, PostfixUpdate is not included because it only has a changed lval if
|
||||
* it has a changed argument, but LoadProperty is included because the argument can be
|
||||
* mutated elsewhere.
|
||||
*/
|
||||
function mayHaveChanged(env: Environment, instruction: Instruction): boolean {
|
||||
if (env.config.enableChangeDetection == null) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
instr.value.kind === "StoreLocal" ||
|
||||
instr.value.kind === "StoreContext"
|
||||
) {
|
||||
if (
|
||||
instr.value.lvalue.place.identifier.mutableRange.end >
|
||||
instr.value.lvalue.place.identifier.mutableRange.start + 1
|
||||
) {
|
||||
operands.push(instr.value.lvalue.place.identifier);
|
||||
switch (instruction.value.kind) {
|
||||
case "Await":
|
||||
case "ComputedLoad":
|
||||
case "Destructure":
|
||||
case "GetIterator":
|
||||
case "IteratorNext":
|
||||
case "NextPropertyOf":
|
||||
case "PropertyLoad":
|
||||
case "CallExpression":
|
||||
case "MethodCall":
|
||||
case "NewExpression": {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
isMutable(instr, instr.value.value) &&
|
||||
instr.value.value.identifier.mutableRange.start > 0
|
||||
) {
|
||||
operands.push(instr.value.value.identifier);
|
||||
case "LoadGlobal": {
|
||||
return (
|
||||
instruction.value.binding.kind === "ModuleLocal" &&
|
||||
!instruction.value.binding.immutable
|
||||
);
|
||||
}
|
||||
} else if (instr.value.kind === "Destructure") {
|
||||
for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
|
||||
if (
|
||||
place.identifier.mutableRange.end >
|
||||
place.identifier.mutableRange.start + 1
|
||||
) {
|
||||
operands.push(place.identifier);
|
||||
}
|
||||
case "PostfixUpdate":
|
||||
case "PrefixUpdate":
|
||||
case "DeclareLocal":
|
||||
case "DeclareContext":
|
||||
case "StoreLocal":
|
||||
case "MetaProperty":
|
||||
case "TypeCastExpression":
|
||||
case "LoadLocal":
|
||||
case "LoadContext":
|
||||
case "StoreContext":
|
||||
case "PropertyDelete":
|
||||
case "ComputedDelete":
|
||||
case "JSXText":
|
||||
case "TemplateLiteral":
|
||||
case "Primitive":
|
||||
case "Debugger":
|
||||
case "StartMemoize":
|
||||
case "FinishMemoize":
|
||||
case "UnaryExpression":
|
||||
case "BinaryExpression":
|
||||
case "StoreGlobal":
|
||||
case "RegExpLiteral":
|
||||
case "PropertyStore":
|
||||
case "ComputedStore":
|
||||
case "ArrayExpression":
|
||||
case "JsxExpression":
|
||||
case "JsxFragment":
|
||||
case "ObjectExpression":
|
||||
case "UnsupportedNode":
|
||||
case "ObjectMethod":
|
||||
case "FunctionExpression":
|
||||
case "TaggedTemplateExpression": {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
isMutable(instr, instr.value.value) &&
|
||||
instr.value.value.identifier.mutableRange.start > 0
|
||||
) {
|
||||
operands.push(instr.value.value.identifier);
|
||||
}
|
||||
} else if (instr.value.kind === "MethodCall") {
|
||||
for (const operand of eachInstructionOperand(instr)) {
|
||||
if (
|
||||
isMutable(instr, operand) &&
|
||||
/*
|
||||
* exclude global variables from being added to scopes, we can't recreate them!
|
||||
* TODO: improve handling of module-scoped variables and globals
|
||||
*/
|
||||
operand.identifier.mutableRange.start > 0
|
||||
) {
|
||||
operands.push(operand.identifier);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Ensure that the ComputedLoad to resolve the method is in the same scope as the
|
||||
* call itself
|
||||
*/
|
||||
operands.push(instr.value.property.identifier);
|
||||
} else {
|
||||
for (const operand of eachInstructionOperand(instr)) {
|
||||
if (
|
||||
isMutable(instr, operand) &&
|
||||
/*
|
||||
* exclude global variables from being added to scopes, we can't recreate them!
|
||||
* TODO: improve handling of module-scoped variables and globals
|
||||
*/
|
||||
operand.identifier.mutableRange.start > 0
|
||||
) {
|
||||
operands.push(operand.identifier);
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
instruction.value,
|
||||
`Unexpected value kind \`${(instruction.value as any).kind}\``
|
||||
);
|
||||
}
|
||||
}
|
||||
return operands;
|
||||
}
|
||||
|
||||
function isIdentifierMutable(id: Identifier): boolean {
|
||||
return id.mutableRange.end > id.mutableRange.start + 1;
|
||||
}
|
||||
|
||||
function identifierHasMutableRange(id: Identifier): boolean {
|
||||
return id.mutableRange.start > 0;
|
||||
}
|
||||
|
||||
export function findDisjointMutableValues(
|
||||
@@ -351,7 +349,7 @@ export function findDisjointMutableValues(
|
||||
for (const phi of block.phis) {
|
||||
if (
|
||||
// The phi was reset because it was not mutated after creation
|
||||
phi.id.mutableRange.start + 1 !== phi.id.mutableRange.end &&
|
||||
isIdentifierMutable(phi.id) &&
|
||||
phi.id.mutableRange.end >
|
||||
(block.instructions.at(0)?.id ?? block.terminal.id)
|
||||
) {
|
||||
@@ -366,11 +364,77 @@ export function findDisjointMutableValues(
|
||||
}
|
||||
|
||||
for (const instr of block.instructions) {
|
||||
const operands = collectMutableOperands(
|
||||
fn,
|
||||
instr,
|
||||
fn.env.config.enableChangeDetection != null
|
||||
);
|
||||
const operands: Array<Identifier> = [];
|
||||
if (
|
||||
isIdentifierMutable(instr.lvalue.identifier) ||
|
||||
mayAllocate(fn.env, instr) ||
|
||||
mayHaveChanged(fn.env, instr)
|
||||
) {
|
||||
operands.push(instr.lvalue!.identifier);
|
||||
}
|
||||
if (
|
||||
instr.value.kind === "StoreLocal" ||
|
||||
instr.value.kind === "StoreContext"
|
||||
) {
|
||||
if (isIdentifierMutable(instr.value.lvalue.place.identifier)) {
|
||||
operands.push(instr.value.lvalue.place.identifier);
|
||||
}
|
||||
if (
|
||||
isMutableAtInstruction(instr, instr.value.value) &&
|
||||
identifierHasMutableRange(instr.value.value.identifier)
|
||||
) {
|
||||
operands.push(instr.value.value.identifier);
|
||||
}
|
||||
} else if (instr.value.kind === "Destructure") {
|
||||
for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
|
||||
if (
|
||||
isIdentifierMutable(place.identifier) ||
|
||||
mayHaveChanged(fn.env, instr)
|
||||
) {
|
||||
operands.push(place.identifier);
|
||||
}
|
||||
}
|
||||
if (
|
||||
(isMutableAtInstruction(instr, instr.value.value) &&
|
||||
identifierHasMutableRange(instr.value.value.identifier)) ||
|
||||
mayHaveChanged(fn.env, instr)
|
||||
) {
|
||||
operands.push(instr.value.value.identifier);
|
||||
}
|
||||
} else if (instr.value.kind === "MethodCall") {
|
||||
for (const operand of eachInstructionOperand(instr)) {
|
||||
if (
|
||||
(isMutableAtInstruction(instr, operand) &&
|
||||
/*
|
||||
* exclude global variables from being added to scopes, we can't recreate them!
|
||||
* TODO: improve handling of module-scoped variables and globals
|
||||
*/
|
||||
identifierHasMutableRange(operand.identifier)) ||
|
||||
mayHaveChanged(fn.env, instr)
|
||||
) {
|
||||
operands.push(operand.identifier);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Ensure that the ComputedLoad to resolve the method is in the same scope as the
|
||||
* call itself
|
||||
*/
|
||||
operands.push(instr.value.property.identifier);
|
||||
} else {
|
||||
for (const operand of eachInstructionOperand(instr)) {
|
||||
if (
|
||||
(isMutableAtInstruction(instr, operand) &&
|
||||
/*
|
||||
* exclude global variables from being added to scopes, we can't recreate them!
|
||||
* TODO: improve handling of module-scoped variables and globals
|
||||
*/
|
||||
identifierHasMutableRange(operand.identifier)) ||
|
||||
mayHaveChanged(fn.env, instr)
|
||||
) {
|
||||
operands.push(operand.identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (operands.length !== 0) {
|
||||
scopeIdentifiers.union(operands);
|
||||
}
|
||||
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import {
|
||||
BasicBlock,
|
||||
HIRFunction,
|
||||
Identifier,
|
||||
makeInstructionId,
|
||||
ReactiveScope,
|
||||
ReactiveScopeDependencies,
|
||||
} from "../HIR";
|
||||
import { eachTerminalSuccessor } from "../HIR/visitors";
|
||||
import {
|
||||
collectMutableOperands,
|
||||
mergeLocation,
|
||||
} from "./InferReactiveScopeVariables";
|
||||
|
||||
export function memoizeExistingUseMemos(fn: HIRFunction): void {
|
||||
visitBlock(fn, fn.body.blocks.get(fn.body.entry)!, null, new Map());
|
||||
}
|
||||
|
||||
let ctr = 0;
|
||||
function nextId(): number {
|
||||
return ctr++;
|
||||
}
|
||||
|
||||
type CurrentScope =
|
||||
| null
|
||||
| { kind: "pending"; deps: ReactiveScopeDependencies; id: number }
|
||||
| { kind: "available"; scope: ReactiveScope; id: number };
|
||||
|
||||
function visitBlock(
|
||||
fn: HIRFunction,
|
||||
block: BasicBlock,
|
||||
scope: CurrentScope,
|
||||
seen: Map<number, CurrentScope>
|
||||
): void {
|
||||
const visited = seen.get(block.id);
|
||||
if (visited === undefined) {
|
||||
seen.set(block.id, scope);
|
||||
} else {
|
||||
CompilerError.invariant(
|
||||
visited === null ? scope === null : visited.id === scope?.id,
|
||||
{
|
||||
reason:
|
||||
"MemoizeExistingUseMemos: visiting the same block with different scopes",
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
function extend(
|
||||
currentScope: ReactiveScope,
|
||||
operands: Iterable<Identifier>
|
||||
): void {
|
||||
for (const operand of operands) {
|
||||
currentScope.range.start = makeInstructionId(
|
||||
Math.min(currentScope.range.start, operand.mutableRange.start)
|
||||
);
|
||||
currentScope.range.end = makeInstructionId(
|
||||
Math.max(currentScope.range.end, operand.mutableRange.end)
|
||||
);
|
||||
currentScope.loc = mergeLocation(currentScope.loc, operand.loc);
|
||||
operand.scope = currentScope;
|
||||
operand.mutableRange = currentScope.range;
|
||||
}
|
||||
}
|
||||
|
||||
let currentScope = scope;
|
||||
for (const instruction of block.instructions) {
|
||||
if (instruction.value.kind === "StartMemoize") {
|
||||
const deps: ReactiveScopeDependencies = new Set();
|
||||
for (const dep of instruction.value.deps ?? []) {
|
||||
CompilerError.invariant(dep.root.kind !== "Global", {
|
||||
reason:
|
||||
"MemoizeExistingUseMemos: Globals should have been replaced with InlineGlobals",
|
||||
loc: instruction.loc,
|
||||
suggestions: null,
|
||||
description: null,
|
||||
});
|
||||
deps.add({ identifier: dep.root.value.identifier, path: dep.path });
|
||||
}
|
||||
currentScope = { kind: "pending", id: nextId(), deps };
|
||||
} else if (instruction.value.kind === "FinishMemoize") {
|
||||
currentScope = null;
|
||||
} else if (currentScope != null) {
|
||||
const operands = collectMutableOperands(fn, instruction, true);
|
||||
if (operands.length > 0) {
|
||||
if (currentScope.kind === "pending") {
|
||||
currentScope = {
|
||||
kind: "available",
|
||||
id: currentScope.id,
|
||||
scope: {
|
||||
id: fn.env.nextScopeId,
|
||||
range: { start: instruction.id, end: instruction.id },
|
||||
dependencies: currentScope.deps,
|
||||
declarations: new Map(),
|
||||
reassignments: new Set(),
|
||||
earlyReturnValue: null,
|
||||
merged: new Set(),
|
||||
loc: instruction.loc,
|
||||
source: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
extend(currentScope.scope, operands);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const successor of eachTerminalSuccessor(block.terminal)) {
|
||||
visitBlock(fn, fn.body.blocks.get(successor)!, currentScope, seen);
|
||||
}
|
||||
}
|
||||
+1
@@ -43,6 +43,7 @@ export function memoizeFbtAndMacroOperandsInSameScope(fn: HIRFunction): void {
|
||||
const fbtMacroTags = new Set([
|
||||
...FBT_TAGS,
|
||||
...(fn.env.config.customMacros ?? []),
|
||||
...(fn.env.preserveManualMemo() ? ["useMemo", "useCallback"] : []),
|
||||
]);
|
||||
const fbtValues: Set<IdentifierId> = new Set();
|
||||
while (true) {
|
||||
|
||||
+9
-9
@@ -15,6 +15,7 @@ import {
|
||||
ReactiveFunction,
|
||||
ReactiveScope,
|
||||
ReactiveScopeBlock,
|
||||
ReactiveScopeDependencies,
|
||||
ReactiveScopeDependency,
|
||||
ReactiveStatement,
|
||||
Type,
|
||||
@@ -108,7 +109,7 @@ class FindLastUsageVisitor extends ReactiveFunctionVisitor<void> {
|
||||
}
|
||||
}
|
||||
|
||||
class Transform extends ReactiveFunctionTransform<ReactiveScope | null> {
|
||||
class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | null> {
|
||||
lastUsage: Map<IdentifierId, InstructionId>;
|
||||
|
||||
constructor(lastUsage: Map<IdentifierId, InstructionId>) {
|
||||
@@ -118,13 +119,12 @@ class Transform extends ReactiveFunctionTransform<ReactiveScope | null> {
|
||||
|
||||
override transformScope(
|
||||
scopeBlock: ReactiveScopeBlock,
|
||||
state: ReactiveScope | null
|
||||
state: ReactiveScopeDependencies | null
|
||||
): Transformed<ReactiveStatement> {
|
||||
this.visitScope(scopeBlock, scopeBlock.scope);
|
||||
this.visitScope(scopeBlock, scopeBlock.scope.dependencies);
|
||||
if (
|
||||
state !== null &&
|
||||
areEqualDependencies(state.dependencies, scopeBlock.scope.dependencies) &&
|
||||
state.source === scopeBlock.scope.source
|
||||
areEqualDependencies(state, scopeBlock.scope.dependencies)
|
||||
) {
|
||||
return { kind: "replace-many", value: scopeBlock.instructions };
|
||||
} else {
|
||||
@@ -132,7 +132,10 @@ class Transform extends ReactiveFunctionTransform<ReactiveScope | null> {
|
||||
}
|
||||
}
|
||||
|
||||
override visitBlock(block: ReactiveBlock, state: ReactiveScope | null): void {
|
||||
override visitBlock(
|
||||
block: ReactiveBlock,
|
||||
state: ReactiveScopeDependencies | null
|
||||
): void {
|
||||
// Pass 1: visit nested blocks to potentially merge their scopes
|
||||
this.traverseBlock(block, state);
|
||||
|
||||
@@ -414,9 +417,6 @@ function canMergeScopes(
|
||||
current: ReactiveScopeBlock,
|
||||
next: ReactiveScopeBlock
|
||||
): boolean {
|
||||
if (current.scope.source !== next.scope.source) {
|
||||
return false;
|
||||
}
|
||||
// Don't merge scopes with reassignments
|
||||
if (
|
||||
current.scope.reassignments.size !== 0 ||
|
||||
|
||||
+17
-1
@@ -17,6 +17,7 @@ import {
|
||||
ReactiveValue,
|
||||
} from "../HIR/HIR";
|
||||
import {
|
||||
printFunction,
|
||||
printIdentifier,
|
||||
printInstructionValue,
|
||||
printPlace,
|
||||
@@ -24,8 +25,24 @@ import {
|
||||
} from "../HIR/PrintHIR";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
|
||||
export function printReactiveFunctionWithOutlined(
|
||||
fn: ReactiveFunction
|
||||
): string {
|
||||
const writer = new Writer();
|
||||
writeReactiveFunction(fn, writer);
|
||||
for (const outlined of fn.env.getOutlinedFunctions()) {
|
||||
writer.writeLine("\nfunction " + printFunction(outlined.fn));
|
||||
}
|
||||
return writer.complete();
|
||||
}
|
||||
|
||||
export function printReactiveFunction(fn: ReactiveFunction): string {
|
||||
const writer = new Writer();
|
||||
writeReactiveFunction(fn, writer);
|
||||
return writer.complete();
|
||||
}
|
||||
|
||||
function writeReactiveFunction(fn: ReactiveFunction, writer: Writer): void {
|
||||
writer.writeLine(`function ${fn.id !== null ? fn.id : "<unknown>"}(`);
|
||||
writer.indented(() => {
|
||||
for (const param of fn.params) {
|
||||
@@ -39,7 +56,6 @@ export function printReactiveFunction(fn: ReactiveFunction): string {
|
||||
writer.writeLine(") {");
|
||||
writeReactiveInstructions(writer, fn.body);
|
||||
writer.writeLine("}");
|
||||
return writer.complete();
|
||||
}
|
||||
|
||||
export function printReactiveScopeSummary(scope: ReactiveScope): string {
|
||||
|
||||
+8
-2
@@ -24,8 +24,6 @@ import { EARLY_RETURN_SENTINEL } from "./CodegenReactiveFunction";
|
||||
import { ReactiveFunctionTransform, Transformed } from "./visitors";
|
||||
|
||||
/**
|
||||
* TODO: Actualy propagate early return information, for now we throw a Todo bailout.
|
||||
*
|
||||
* This pass ensures that reactive blocks honor the control flow behavior of the
|
||||
* original code including early return semantics. Specifically, if a reactive
|
||||
* scope early returned during the previous execution and the inputs to that block
|
||||
@@ -135,6 +133,14 @@ class Transform extends ReactiveFunctionTransform<State> {
|
||||
scopeBlock: ReactiveScopeBlock,
|
||||
parentState: State
|
||||
): void {
|
||||
/**
|
||||
* Exit early if an earlier pass has already created an early return,
|
||||
* which may happen in alternate compiler configurations.
|
||||
*/
|
||||
if (scopeBlock.scope.earlyReturnValue !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const innerState: State = {
|
||||
withinReactiveScope: true,
|
||||
earlyReturnValue: parentState.earlyReturnValue,
|
||||
|
||||
+1
-3
@@ -685,9 +685,7 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
|
||||
const scopeDependencies = context.enter(scope.scope, () => {
|
||||
this.visitBlock(scope.instructions, context);
|
||||
});
|
||||
if (!scope.scope.source) {
|
||||
scope.scope.dependencies = scopeDependencies;
|
||||
}
|
||||
scope.scope.dependencies = scopeDependencies;
|
||||
}
|
||||
|
||||
override visitPrunedScope(
|
||||
|
||||
+1
-1
@@ -183,7 +183,7 @@ class Visitor extends ReactiveFunctionVisitor<CreateUpdate> {
|
||||
ident.path.forEach((key) => {
|
||||
target &&= this.paths.get(target)?.get(key);
|
||||
});
|
||||
if (target && this.map.get(target) === "Create" && !scope.scope.source) {
|
||||
if (target && this.map.get(target) === "Create") {
|
||||
scope.scope.dependencies.delete(ident);
|
||||
}
|
||||
});
|
||||
|
||||
+7
-3
@@ -934,10 +934,14 @@ class PruneScopesTransform extends ReactiveFunctionTransform<
|
||||
* is early-returned from within the scope. For now we intentionaly keep
|
||||
* these scopes, and let them get pruned later by PruneUnusedScopes
|
||||
* _after_ handling the early-return case in PropagateEarlyReturns.
|
||||
*
|
||||
* Also keep the scope if an early return was created by some earlier pass,
|
||||
* which may happen in alternate compiler configurations.
|
||||
*/
|
||||
if (
|
||||
scopeBlock.scope.declarations.size === 0 &&
|
||||
scopeBlock.scope.reassignments.size === 0
|
||||
(scopeBlock.scope.declarations.size === 0 &&
|
||||
scopeBlock.scope.reassignments.size === 0) ||
|
||||
scopeBlock.scope.earlyReturnValue !== null
|
||||
) {
|
||||
return { kind: "keep" };
|
||||
}
|
||||
@@ -949,7 +953,7 @@ class PruneScopesTransform extends ReactiveFunctionTransform<
|
||||
Array.from(scopeBlock.scope.reassignments).some((identifier) =>
|
||||
state.has(identifier.id)
|
||||
);
|
||||
if (hasMemoizedOutput || scopeBlock.scope.source) {
|
||||
if (hasMemoizedOutput) {
|
||||
return { kind: "keep" };
|
||||
} else {
|
||||
this.prunedScopes.add(scopeBlock.scope.id);
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ class Visitor extends ReactiveFunctionVisitor<ReactiveIdentifiers> {
|
||||
this.traverseScope(scopeBlock, state);
|
||||
for (const dep of scopeBlock.scope.dependencies) {
|
||||
const isReactive = state.has(dep.identifier.id);
|
||||
if (!isReactive && !scopeBlock.scope.source) {
|
||||
if (!isReactive) {
|
||||
scopeBlock.scope.dependencies.delete(dep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ class Transform extends ReactiveFunctionTransform<State> {
|
||||
this.visitScope(scopeBlock, scopeState);
|
||||
if (
|
||||
!scopeState.hasReturnStatement &&
|
||||
!scopeBlock.scope.source &&
|
||||
scopeBlock.scope.reassignments.size === 0 &&
|
||||
(scopeBlock.scope.declarations.size === 0 ||
|
||||
/*
|
||||
|
||||
@@ -124,11 +124,11 @@ class OkImpl<T> implements Result<T, never> {
|
||||
return this;
|
||||
}
|
||||
|
||||
isOk(): boolean {
|
||||
isOk(): this is OkImpl<T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
isErr(): boolean {
|
||||
isErr(): this is ErrImpl<never> {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -199,11 +199,11 @@ class ErrImpl<E> implements Result<never, E> {
|
||||
return fn(this.val);
|
||||
}
|
||||
|
||||
isOk(): boolean {
|
||||
isOk(): this is OkImpl<never> {
|
||||
return false;
|
||||
}
|
||||
|
||||
isErr(): boolean {
|
||||
isErr(): this is ErrImpl<E> {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@ import generate from "@babel/generator";
|
||||
import * as t from "@babel/types";
|
||||
import chalk from "chalk";
|
||||
import { HIR, HIRFunction, ReactiveFunction } from "../HIR/HIR";
|
||||
import { printFunction, printHIR } from "../HIR/PrintHIR";
|
||||
import { CodegenFunction, printReactiveFunction } from "../ReactiveScopes";
|
||||
import { printFunctionWithOutlined, printHIR } from "../HIR/PrintHIR";
|
||||
import { CodegenFunction } from "../ReactiveScopes";
|
||||
import { printReactiveFunctionWithOutlined } from "../ReactiveScopes/PrintReactiveFunction";
|
||||
|
||||
let ENABLED: boolean = false;
|
||||
|
||||
@@ -79,7 +80,7 @@ export function logCodegenFunction(step: string, fn: CodegenFunction): void {
|
||||
|
||||
export function logHIRFunction(step: string, fn: HIRFunction): void {
|
||||
if (ENABLED) {
|
||||
const printed = printFunction(fn);
|
||||
const printed = printFunctionWithOutlined(fn);
|
||||
if (printed !== lastLogged) {
|
||||
lastLogged = printed;
|
||||
process.stdout.write(`${chalk.green(step)}:\n${printed}\n\n`);
|
||||
@@ -91,7 +92,7 @@ export function logHIRFunction(step: string, fn: HIRFunction): void {
|
||||
|
||||
export function logReactiveFunction(step: string, fn: ReactiveFunction): void {
|
||||
if (ENABLED) {
|
||||
const printed = printReactiveFunction(fn);
|
||||
const printed = printReactiveFunctionWithOutlined(fn);
|
||||
if (printed !== lastLogged) {
|
||||
lastLogged = printed;
|
||||
process.stdout.write(`${chalk.green(step)}:\n${printed}\n\n`);
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ import {
|
||||
isUseInsertionEffectHookType,
|
||||
isUseLayoutEffectHookType,
|
||||
} from "../HIR";
|
||||
import { isMutable } from "../ReactiveScopes/InferReactiveScopeVariables";
|
||||
import { isMutableAtInstruction } from "../ReactiveScopes/InferReactiveScopeVariables";
|
||||
import {
|
||||
ReactiveFunctionVisitor,
|
||||
visitReactiveFunction,
|
||||
@@ -99,7 +99,7 @@ class Visitor extends ReactiveFunctionVisitor<CompilerError> {
|
||||
const deps = instruction.value.args[1]!;
|
||||
if (
|
||||
deps.kind === "Identifier" &&
|
||||
(isMutable(instruction as Instruction, deps) ||
|
||||
(isMutableAtInstruction(instruction as Instruction, deps) ||
|
||||
isUnmemoized(deps.identifier, this.scopes))
|
||||
) {
|
||||
state.push({
|
||||
|
||||
+6
-11
@@ -25,7 +25,7 @@ import {
|
||||
import { printManualMemoDependency } from "../HIR/PrintHIR";
|
||||
import { eachInstructionValueOperand } from "../HIR/visitors";
|
||||
import { collectMaybeMemoDependencies } from "../Inference/DropManualMemoization";
|
||||
import { isMutable } from "../ReactiveScopes/InferReactiveScopeVariables";
|
||||
import { isMutableAtInstruction } from "../ReactiveScopes/InferReactiveScopeVariables";
|
||||
import {
|
||||
ReactiveFunctionVisitor,
|
||||
visitReactiveFunction,
|
||||
@@ -146,13 +146,9 @@ function compareDeps(
|
||||
const rootsEqual =
|
||||
(inferred.root.kind === "Global" &&
|
||||
source.root.kind === "Global" &&
|
||||
inferred.root.binding.binding.name ===
|
||||
source.root.binding.binding.name) ||
|
||||
((inferred.root.kind === "NamedLocal" ||
|
||||
inferred.root.kind === "InlinedGlobal") &&
|
||||
(source.root.kind === "NamedLocal" ||
|
||||
source.root.kind === "InlinedGlobal") &&
|
||||
source.root.kind === inferred.root.kind &&
|
||||
inferred.root.identifierName === source.root.identifierName) ||
|
||||
(inferred.root.kind === "NamedLocal" &&
|
||||
source.root.kind === "NamedLocal" &&
|
||||
inferred.root.value.identifier.id === source.root.value.identifier.id);
|
||||
if (!rootsEqual) {
|
||||
return CompareDependencyResult.RootDifference;
|
||||
@@ -382,8 +378,7 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
|
||||
|
||||
if (
|
||||
state.manualMemoState != null &&
|
||||
state.manualMemoState.depsFromSource != null &&
|
||||
!scopeBlock.scope.source
|
||||
state.manualMemoState.depsFromSource != null
|
||||
) {
|
||||
for (const dep of scopeBlock.scope.dependencies) {
|
||||
validateInferredDep(
|
||||
@@ -469,7 +464,7 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
|
||||
instruction.value as InstructionValue
|
||||
)) {
|
||||
if (
|
||||
isMutable(instruction as Instruction, value) ||
|
||||
isMutableAtInstruction(instruction as Instruction, value) ||
|
||||
(isDecl && isUnmemoized(value.identifier, this.scopes))
|
||||
) {
|
||||
state.errors.push({
|
||||
|
||||
+25
-30
@@ -40,57 +40,52 @@ import { useCallback, useEffect, useState } from "react";
|
||||
let someGlobal = {};
|
||||
|
||||
function Component() {
|
||||
const $ = _c(7);
|
||||
const $ = _c(6);
|
||||
const [state, setState] = useState(someGlobal);
|
||||
|
||||
const setGlobal = _temp;
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {
|
||||
someGlobal.value = true;
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const setGlobal = t0;
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = () => {
|
||||
setGlobal();
|
||||
};
|
||||
t2 = [];
|
||||
t1 = [];
|
||||
$[0] = t0;
|
||||
$[1] = t1;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
t1 = $[1];
|
||||
t2 = $[2];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
useEffect(t0, t1);
|
||||
let t2;
|
||||
let t3;
|
||||
let t4;
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = () => {
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = () => {
|
||||
setState(someGlobal.value);
|
||||
};
|
||||
t4 = [someGlobal];
|
||||
t3 = [someGlobal];
|
||||
$[2] = t2;
|
||||
$[3] = t3;
|
||||
$[4] = t4;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
t3 = $[3];
|
||||
t4 = $[4];
|
||||
}
|
||||
useEffect(t3, t4);
|
||||
useEffect(t2, t3);
|
||||
|
||||
const t5 = String(state);
|
||||
let t6;
|
||||
if ($[5] !== t5) {
|
||||
t6 = <div>{t5}</div>;
|
||||
const t4 = String(state);
|
||||
let t5;
|
||||
if ($[4] !== t4) {
|
||||
t5 = <div>{t4}</div>;
|
||||
$[4] = t4;
|
||||
$[5] = t5;
|
||||
$[6] = t6;
|
||||
} else {
|
||||
t6 = $[6];
|
||||
t5 = $[5];
|
||||
}
|
||||
return t6;
|
||||
return t5;
|
||||
}
|
||||
function _temp() {
|
||||
someGlobal.value = true;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+25
-30
@@ -39,57 +39,52 @@ import { useEffect, useState } from "react";
|
||||
let someGlobal = {};
|
||||
|
||||
function Component() {
|
||||
const $ = _c(7);
|
||||
const $ = _c(6);
|
||||
const [state, setState] = useState(someGlobal);
|
||||
|
||||
const setGlobal = _temp;
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {
|
||||
someGlobal.value = true;
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const setGlobal = t0;
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = () => {
|
||||
setGlobal();
|
||||
};
|
||||
t2 = [];
|
||||
t1 = [];
|
||||
$[0] = t0;
|
||||
$[1] = t1;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
t1 = $[1];
|
||||
t2 = $[2];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
useEffect(t0, t1);
|
||||
let t2;
|
||||
let t3;
|
||||
let t4;
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = () => {
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = () => {
|
||||
setState(someGlobal.value);
|
||||
};
|
||||
t4 = [someGlobal];
|
||||
t3 = [someGlobal];
|
||||
$[2] = t2;
|
||||
$[3] = t3;
|
||||
$[4] = t4;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
t3 = $[3];
|
||||
t4 = $[4];
|
||||
}
|
||||
useEffect(t3, t4);
|
||||
useEffect(t2, t3);
|
||||
|
||||
const t5 = String(state);
|
||||
let t6;
|
||||
if ($[5] !== t5) {
|
||||
t6 = <div>{t5}</div>;
|
||||
const t4 = String(state);
|
||||
let t5;
|
||||
if ($[4] !== t4) {
|
||||
t5 = <div>{t4}</div>;
|
||||
$[4] = t4;
|
||||
$[5] = t5;
|
||||
$[6] = t6;
|
||||
} else {
|
||||
t6 = $[6];
|
||||
t5 = $[5];
|
||||
}
|
||||
return t6;
|
||||
return t5;
|
||||
}
|
||||
function _temp() {
|
||||
someGlobal.value = true;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+25
-30
@@ -39,57 +39,52 @@ import { useEffect, useState } from "react";
|
||||
let someGlobal = false;
|
||||
|
||||
function Component() {
|
||||
const $ = _c(7);
|
||||
const $ = _c(6);
|
||||
const [state, setState] = useState(someGlobal);
|
||||
|
||||
const setGlobal = _temp;
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {
|
||||
someGlobal = true;
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const setGlobal = t0;
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = () => {
|
||||
setGlobal();
|
||||
};
|
||||
t2 = [];
|
||||
t1 = [];
|
||||
$[0] = t0;
|
||||
$[1] = t1;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
t1 = $[1];
|
||||
t2 = $[2];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
useEffect(t0, t1);
|
||||
let t2;
|
||||
let t3;
|
||||
let t4;
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = () => {
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = () => {
|
||||
setState(someGlobal);
|
||||
};
|
||||
t4 = [someGlobal];
|
||||
t3 = [someGlobal];
|
||||
$[2] = t2;
|
||||
$[3] = t3;
|
||||
$[4] = t4;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
t3 = $[3];
|
||||
t4 = $[4];
|
||||
}
|
||||
useEffect(t3, t4);
|
||||
useEffect(t2, t3);
|
||||
|
||||
const t5 = String(state);
|
||||
let t6;
|
||||
if ($[5] !== t5) {
|
||||
t6 = <div>{t5}</div>;
|
||||
const t4 = String(state);
|
||||
let t5;
|
||||
if ($[4] !== t4) {
|
||||
t5 = <div>{t4}</div>;
|
||||
$[4] = t4;
|
||||
$[5] = t5;
|
||||
$[6] = t6;
|
||||
} else {
|
||||
t6 = $[6];
|
||||
t5 = $[5];
|
||||
}
|
||||
return t6;
|
||||
return t5;
|
||||
}
|
||||
function _temp() {
|
||||
someGlobal = true;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+20
-24
@@ -36,48 +36,44 @@ import { useEffect, useState } from "react";
|
||||
let someGlobal = false;
|
||||
|
||||
function Component() {
|
||||
const $ = _c(6);
|
||||
const $ = _c(5);
|
||||
const [state, setState] = useState(someGlobal);
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {
|
||||
someGlobal = true;
|
||||
};
|
||||
|
||||
t1 = [];
|
||||
t0 = [];
|
||||
$[0] = t0;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
t1 = $[1];
|
||||
}
|
||||
useEffect(t0, t1);
|
||||
useEffect(_temp, t0);
|
||||
let t1;
|
||||
let t2;
|
||||
let t3;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = () => {
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = () => {
|
||||
setState(someGlobal);
|
||||
};
|
||||
t3 = [someGlobal];
|
||||
t2 = [someGlobal];
|
||||
$[1] = t1;
|
||||
$[2] = t2;
|
||||
$[3] = t3;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
t2 = $[2];
|
||||
t3 = $[3];
|
||||
}
|
||||
useEffect(t2, t3);
|
||||
useEffect(t1, t2);
|
||||
|
||||
const t4 = String(state);
|
||||
let t5;
|
||||
if ($[4] !== t4) {
|
||||
t5 = <div>{t4}</div>;
|
||||
const t3 = String(state);
|
||||
let t4;
|
||||
if ($[3] !== t3) {
|
||||
t4 = <div>{t3}</div>;
|
||||
$[3] = t3;
|
||||
$[4] = t4;
|
||||
$[5] = t5;
|
||||
} else {
|
||||
t5 = $[5];
|
||||
t4 = $[4];
|
||||
}
|
||||
return t5;
|
||||
return t4;
|
||||
}
|
||||
function _temp() {
|
||||
someGlobal = true;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+5
-5
@@ -27,13 +27,9 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component() {
|
||||
const $ = _c(1);
|
||||
const onClick = _temp;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const onClick = () => {
|
||||
someUnknownGlobal = true;
|
||||
moduleLocal = true;
|
||||
};
|
||||
|
||||
t0 = <div onClick={onClick} />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
@@ -41,6 +37,10 @@ function Component() {
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp() {
|
||||
someUnknownGlobal = true;
|
||||
moduleLocal = true;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+13
-17
@@ -16,7 +16,7 @@ function Component(props) {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(8);
|
||||
const $ = _c(7);
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
@@ -37,25 +37,21 @@ function Component(props) {
|
||||
t2 = $[3];
|
||||
}
|
||||
const x = t2;
|
||||
let t3;
|
||||
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = () => "this closure gets stringified, not called";
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
const y = x.join(t3);
|
||||
const y = x.join(_temp);
|
||||
foo(y);
|
||||
let t4;
|
||||
if ($[5] !== x || $[6] !== y) {
|
||||
t4 = [x, y];
|
||||
$[5] = x;
|
||||
$[6] = y;
|
||||
$[7] = t4;
|
||||
let t3;
|
||||
if ($[4] !== x || $[5] !== y) {
|
||||
t3 = [x, y];
|
||||
$[4] = x;
|
||||
$[5] = y;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t4 = $[7];
|
||||
t3 = $[6];
|
||||
}
|
||||
return t4;
|
||||
return t3;
|
||||
}
|
||||
function _temp() {
|
||||
return "this closure gets stringified, not called";
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+4
-1
@@ -44,7 +44,7 @@ function Component(props) {
|
||||
const items = t1;
|
||||
let t2;
|
||||
if ($[4] !== items) {
|
||||
t2 = items.map((item_0) => item_0);
|
||||
t2 = items.map(_temp);
|
||||
$[4] = items;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
@@ -53,6 +53,9 @@ function Component(props) {
|
||||
const mapped = t2;
|
||||
return mapped;
|
||||
}
|
||||
function _temp(item_0) {
|
||||
return item_0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+4
-1
@@ -33,7 +33,7 @@ function Component(props) {
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const y = x.map((item) => item);
|
||||
const y = x.map(_temp);
|
||||
t1 = [x, y];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
@@ -41,6 +41,9 @@ function Component(props) {
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
function _temp(item) {
|
||||
return item;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+4
-1
@@ -33,7 +33,7 @@ function Component(props) {
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const y = x.map((item) => item);
|
||||
const y = x.map(_temp);
|
||||
t1 = [x, y];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
@@ -41,6 +41,9 @@ function Component(props) {
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
function _temp(item) {
|
||||
return item;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+5
-4
@@ -28,10 +28,7 @@ function Component(props) {
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const x = [];
|
||||
const y = x.map((item) => {
|
||||
item.updated = true;
|
||||
return item;
|
||||
});
|
||||
const y = x.map(_temp);
|
||||
t0 = [x, y];
|
||||
$[0] = t0;
|
||||
} else {
|
||||
@@ -39,6 +36,10 @@ function Component(props) {
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp(item) {
|
||||
item.updated = true;
|
||||
return item;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+5
-4
@@ -28,10 +28,7 @@ function Component(props) {
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const x = [];
|
||||
const y = x.map((item) => {
|
||||
item.updated = true;
|
||||
return item;
|
||||
});
|
||||
const y = x.map(_temp);
|
||||
t0 = [x, y];
|
||||
$[0] = t0;
|
||||
} else {
|
||||
@@ -39,6 +36,10 @@ function Component(props) {
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp(item) {
|
||||
item.updated = true;
|
||||
return item;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+4
-1
@@ -28,7 +28,7 @@ function Component(props) {
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const x = [{}];
|
||||
const y = x.map((item) => item);
|
||||
const y = x.map(_temp);
|
||||
y[0].flag = true;
|
||||
t0 = [x, y];
|
||||
$[0] = t0;
|
||||
@@ -37,6 +37,9 @@ function Component(props) {
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp(item) {
|
||||
return item;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+17
-21
@@ -21,33 +21,29 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(5);
|
||||
const $ = _c(4);
|
||||
const f = _temp;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = (item) => item;
|
||||
$[0] = t0;
|
||||
if ($[0] !== props.items) {
|
||||
t0 = [...props.items].map(f);
|
||||
$[0] = props.items;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
t0 = $[1];
|
||||
}
|
||||
const f = t0;
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[1] !== props.items) {
|
||||
t1 = [...props.items].map(f);
|
||||
$[1] = props.items;
|
||||
$[2] = t1;
|
||||
if ($[2] !== x) {
|
||||
t1 = [x, f];
|
||||
$[2] = x;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
t1 = $[3];
|
||||
}
|
||||
const x = t1;
|
||||
let t2;
|
||||
if ($[3] !== x) {
|
||||
t2 = [x, f];
|
||||
$[3] = x;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
return t2;
|
||||
return t1;
|
||||
}
|
||||
function _temp(item) {
|
||||
return item;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+5
-13
@@ -21,22 +21,14 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function useFoo() {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {
|
||||
"worklet";
|
||||
return 1;
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const update = t0;
|
||||
const update = _temp;
|
||||
return update;
|
||||
}
|
||||
function _temp() {
|
||||
"worklet";
|
||||
return 1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
|
||||
+5
-13
@@ -16,22 +16,14 @@ function component(a) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function component(a) {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = function () {
|
||||
let z_0;
|
||||
mutate(z_0);
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const x = t0;
|
||||
const x = _temp;
|
||||
return x;
|
||||
}
|
||||
function _temp() {
|
||||
let z_0;
|
||||
mutate(z_0);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
-1
@@ -30,7 +30,6 @@ function Component(props) {
|
||||
if (!condition) {
|
||||
let old$x = $[1];
|
||||
$structuralCheck(old$x, x, "x", "Component", "cached", "(3:6)");
|
||||
x = old$x;
|
||||
}
|
||||
$[0] = props.value;
|
||||
$[1] = x;
|
||||
|
||||
-1
@@ -30,7 +30,6 @@ function Component(props) {
|
||||
if (!condition) {
|
||||
let old$x = $[1];
|
||||
$structuralCheck(old$x, x, "x", "Component", "cached", "(3:6)");
|
||||
x = $restore(old$x);
|
||||
}
|
||||
$[0] = props.value;
|
||||
$[1] = $store(x);
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableChangeDetection
|
||||
let glob = 1;
|
||||
|
||||
function Component(props) {
|
||||
const a = props.x;
|
||||
const { b, ...c } = props.y;
|
||||
const d = glob;
|
||||
return (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
{c}
|
||||
{d}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { $structuralCheck } from "react-compiler-runtime";
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableChangeDetection
|
||||
let glob = 1;
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(11);
|
||||
let t0;
|
||||
{
|
||||
t0 = props.x;
|
||||
let condition = $[0] !== props.x;
|
||||
if (!condition) {
|
||||
let old$t0 = $[1];
|
||||
$structuralCheck(old$t0, t0, "t0", "Component", "cached", "(5:5)");
|
||||
}
|
||||
$[0] = props.x;
|
||||
$[1] = t0;
|
||||
if (condition) {
|
||||
t0 = props.x;
|
||||
$structuralCheck($[1], t0, "t0", "Component", "recomputed", "(5:5)");
|
||||
t0 = $[1];
|
||||
}
|
||||
}
|
||||
const a = t0;
|
||||
let b;
|
||||
let c;
|
||||
{
|
||||
({ b, ...c } = props.y);
|
||||
let condition = $[2] !== props.y;
|
||||
if (!condition) {
|
||||
let old$b = $[3];
|
||||
let old$c = $[4];
|
||||
$structuralCheck(old$b, b, "b", "Component", "cached", "(6:6)");
|
||||
$structuralCheck(old$c, c, "c", "Component", "cached", "(6:6)");
|
||||
}
|
||||
$[2] = props.y;
|
||||
$[3] = b;
|
||||
$[4] = c;
|
||||
if (condition) {
|
||||
({ b, ...c } = props.y);
|
||||
$structuralCheck($[3], b, "b", "Component", "recomputed", "(6:6)");
|
||||
b = $[3];
|
||||
$structuralCheck($[4], c, "c", "Component", "recomputed", "(6:6)");
|
||||
c = $[4];
|
||||
}
|
||||
}
|
||||
let t1;
|
||||
{
|
||||
t1 = glob;
|
||||
let condition = $[5] === Symbol.for("react.memo_cache_sentinel");
|
||||
if (!condition) {
|
||||
let old$t1 = $[5];
|
||||
$structuralCheck(old$t1, t1, "t1", "Component", "cached", "(13:13)");
|
||||
}
|
||||
$[5] = t1;
|
||||
if (condition) {
|
||||
t1 = glob;
|
||||
$structuralCheck($[5], t1, "t1", "Component", "recomputed", "(13:13)");
|
||||
t1 = $[5];
|
||||
}
|
||||
}
|
||||
let t2;
|
||||
{
|
||||
t2 = (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
{c}
|
||||
{t1}
|
||||
</div>
|
||||
);
|
||||
let condition = $[6] !== a || $[7] !== b || $[8] !== c || $[9] !== t1;
|
||||
if (!condition) {
|
||||
let old$t2 = $[10];
|
||||
$structuralCheck(old$t2, t2, "t2", "Component", "cached", "(9:14)");
|
||||
}
|
||||
$[6] = a;
|
||||
$[7] = b;
|
||||
$[8] = c;
|
||||
$[9] = t1;
|
||||
$[10] = t2;
|
||||
if (condition) {
|
||||
t2 = (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
{c}
|
||||
{t1}
|
||||
</div>
|
||||
);
|
||||
$structuralCheck($[10], t2, "t2", "Component", "recomputed", "(9:14)");
|
||||
t2 = $[10];
|
||||
}
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// @enableChangeDetection
|
||||
let glob = 1;
|
||||
|
||||
function Component(props) {
|
||||
const a = props.x;
|
||||
const { b, ...c } = props.y;
|
||||
const d = glob;
|
||||
return (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
{c}
|
||||
{d}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+11
-15
@@ -22,24 +22,20 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(3);
|
||||
const $ = _c(2);
|
||||
const cb = _temp;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = (x, y, z) => x + y + z;
|
||||
$[0] = t0;
|
||||
if ($[0] !== props.id) {
|
||||
t0 = <Stringify cb={cb} id={props.id} />;
|
||||
$[0] = props.id;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
t0 = $[1];
|
||||
}
|
||||
const cb = t0;
|
||||
let t1;
|
||||
if ($[1] !== props.id) {
|
||||
t1 = <Stringify cb={cb} id={props.id} />;
|
||||
$[1] = props.id;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
function _temp(x, y, z) {
|
||||
return x + y + z;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+15
-15
@@ -30,30 +30,30 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // Should print A, B, arg, original
|
||||
function Component() {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = (o) => {
|
||||
o.f = () => console.log("new");
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const changeF = t0;
|
||||
const $ = _c(1);
|
||||
const changeF = _temp2;
|
||||
let x;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
x = { f: () => console.log("original") };
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
x = { f: _temp3 };
|
||||
|
||||
(console.log("A"), x)[(console.log("B"), "f")](
|
||||
(changeF(x), console.log("arg"), 1),
|
||||
);
|
||||
$[1] = x;
|
||||
$[0] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
x = $[0];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
function _temp3() {
|
||||
return console.log("original");
|
||||
}
|
||||
function _temp2(o) {
|
||||
o.f = _temp;
|
||||
}
|
||||
function _temp() {
|
||||
return console.log("new");
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+4
-1
@@ -27,11 +27,14 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Component() {
|
||||
const fn = () => ({ x: "value" });
|
||||
const fn = _temp;
|
||||
|
||||
invoke(fn);
|
||||
return 3;
|
||||
}
|
||||
function _temp() {
|
||||
return { x: "value" };
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+2
-1
@@ -31,7 +31,7 @@ function Foo() {
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = function a(t2) {
|
||||
const x_0 = t2 === undefined ? () => {} : t2;
|
||||
const x_0 = t2 === undefined ? _temp : t2;
|
||||
return (function b(t3) {
|
||||
const y_0 = t3 === undefined ? [] : t3;
|
||||
return [x_0, y_0];
|
||||
@@ -44,6 +44,7 @@ function Foo() {
|
||||
t0 = t1;
|
||||
return t0;
|
||||
}
|
||||
function _temp() {}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
|
||||
+2
-1
@@ -25,7 +25,7 @@ function Component(t0) {
|
||||
const $ = _c(2);
|
||||
let t1;
|
||||
if ($[0] !== t0) {
|
||||
t1 = t0 === undefined ? identity([() => {}, true, 42, "hello"]) : t0;
|
||||
t1 = t0 === undefined ? identity([_temp, true, 42, "hello"]) : t0;
|
||||
$[0] = t0;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
@@ -34,6 +34,7 @@ function Component(t0) {
|
||||
const x = t1;
|
||||
return x;
|
||||
}
|
||||
function _temp() {}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+2
-11
@@ -16,20 +16,11 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(t0) {
|
||||
const $ = _c(2);
|
||||
let t1;
|
||||
if ($[0] !== t0) {
|
||||
t1 = t0 === undefined ? () => {} : t0;
|
||||
$[0] = t0;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const x = t1;
|
||||
const x = t0 === undefined ? _temp : t0;
|
||||
return x;
|
||||
}
|
||||
function _temp() {}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+4
-11
@@ -16,20 +16,13 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(t0) {
|
||||
const $ = _c(2);
|
||||
let t1;
|
||||
if ($[0] !== t0) {
|
||||
t1 = t0 === undefined ? () => [-1, true, 42, "hello"] : t0;
|
||||
$[0] = t0;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const x = t1;
|
||||
const x = t0 === undefined ? _temp : t0;
|
||||
return x;
|
||||
}
|
||||
function _temp() {
|
||||
return [-1, true, 42, "hello"];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+16
-22
@@ -58,37 +58,31 @@ function unsafeUpdateConst() {
|
||||
}
|
||||
|
||||
function Component() {
|
||||
const $ = _c(3);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {
|
||||
unsafeResetConst();
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
useState(t0);
|
||||
const $ = _c(2);
|
||||
useState(_temp);
|
||||
|
||||
unsafeUpdateConst();
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = [{ pretendConst }];
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
t0 = t1;
|
||||
const value = t0;
|
||||
let t2;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = [{ pretendConst }];
|
||||
t2 = <ValidateMemoization inputs={[]} output={value} />;
|
||||
$[1] = t2;
|
||||
} else {
|
||||
t2 = $[1];
|
||||
}
|
||||
t1 = t2;
|
||||
const value = t1;
|
||||
let t3;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = <ValidateMemoization inputs={[]} output={value} />;
|
||||
$[2] = t3;
|
||||
} else {
|
||||
t3 = $[2];
|
||||
}
|
||||
return t3;
|
||||
return t2;
|
||||
}
|
||||
function _temp() {
|
||||
unsafeResetConst();
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+17
-23
@@ -61,45 +61,39 @@ function unsafeUpdateConst() {
|
||||
}
|
||||
|
||||
function Component() {
|
||||
const $ = _c(4);
|
||||
const $ = _c(3);
|
||||
if (
|
||||
$[0] !== "4bf230b116dd95f382060ad17350e116395e41ed757e51fd074ea0b4ed281272"
|
||||
) {
|
||||
for (let $i = 0; $i < 4; $i += 1) {
|
||||
for (let $i = 0; $i < 3; $i += 1) {
|
||||
$[$i] = Symbol.for("react.memo_cache_sentinel");
|
||||
}
|
||||
$[0] = "4bf230b116dd95f382060ad17350e116395e41ed757e51fd074ea0b4ed281272";
|
||||
}
|
||||
let t0;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {
|
||||
unsafeResetConst();
|
||||
};
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
useState(t0);
|
||||
useState(_temp);
|
||||
|
||||
unsafeUpdateConst();
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = [{ pretendConst }];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
t0 = t1;
|
||||
const value = t0;
|
||||
let t2;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = [{ pretendConst }];
|
||||
t2 = <ValidateMemoization inputs={[pretendConst]} output={value} />;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
}
|
||||
t1 = t2;
|
||||
const value = t1;
|
||||
let t3;
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = <ValidateMemoization inputs={[pretendConst]} output={value} />;
|
||||
$[3] = t3;
|
||||
} else {
|
||||
t3 = $[3];
|
||||
}
|
||||
return t3;
|
||||
return t2;
|
||||
}
|
||||
function _temp() {
|
||||
unsafeResetConst();
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+27
-33
@@ -42,47 +42,41 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { fbt } from "fbt";
|
||||
|
||||
function Component() {
|
||||
const $ = _c(2);
|
||||
const $ = _c(1);
|
||||
const buttonLabel = _temp;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {
|
||||
if (!someCondition) {
|
||||
return fbt._("Purchase as a gift", null, { hk: "1gHj4g" });
|
||||
} else {
|
||||
if (
|
||||
!iconOnly &&
|
||||
showPrice &&
|
||||
item?.current_gift_offer?.price?.formatted != null
|
||||
) {
|
||||
return fbt._(
|
||||
"Gift | {price}",
|
||||
[fbt._param("price", item?.current_gift_offer?.price?.formatted)],
|
||||
{ hk: "3GTnGE" },
|
||||
);
|
||||
} else {
|
||||
if (!iconOnly && !showPrice) {
|
||||
return fbt._("Gift", null, { hk: "3fqfrk" });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const buttonLabel = t0;
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = (
|
||||
t0 = (
|
||||
<View>
|
||||
<Button text={buttonLabel()} />
|
||||
</View>
|
||||
);
|
||||
$[1] = t1;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp() {
|
||||
if (!someCondition) {
|
||||
return fbt._("Purchase as a gift", null, { hk: "1gHj4g" });
|
||||
} else {
|
||||
if (
|
||||
!iconOnly &&
|
||||
showPrice &&
|
||||
item?.current_gift_offer?.price?.formatted != null
|
||||
) {
|
||||
return fbt._(
|
||||
"Gift | {price}",
|
||||
[fbt._param("price", item?.current_gift_offer?.price?.formatted)],
|
||||
{ hk: "3GTnGE" },
|
||||
);
|
||||
} else {
|
||||
if (!iconOnly && !showPrice) {
|
||||
return fbt._("Gift", null, { hk: "3fqfrk" });
|
||||
}
|
||||
}
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+10
-16
@@ -19,29 +19,23 @@ function Component(props) {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(3);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = (object, key, value) => {
|
||||
object.updated = true;
|
||||
object[key] = value;
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const mutate = t0;
|
||||
const $ = _c(2);
|
||||
const mutate = _temp;
|
||||
let x;
|
||||
if ($[1] !== props) {
|
||||
if ($[0] !== props) {
|
||||
x = makeObject(props);
|
||||
mutate(x);
|
||||
$[1] = props;
|
||||
$[2] = x;
|
||||
$[0] = props;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[2];
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
function _temp(object, key, value) {
|
||||
object.updated = true;
|
||||
object[key] = value;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+4
-1
@@ -36,7 +36,7 @@ function hoisting(cond) {
|
||||
items.push(bar());
|
||||
};
|
||||
|
||||
const bar = () => true;
|
||||
const bar = _temp;
|
||||
foo();
|
||||
}
|
||||
$[0] = cond;
|
||||
@@ -46,6 +46,9 @@ function hoisting(cond) {
|
||||
}
|
||||
return items;
|
||||
}
|
||||
function _temp() {
|
||||
return true;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: hoisting,
|
||||
|
||||
+4
-1
@@ -36,7 +36,7 @@ function hoisting() {
|
||||
},
|
||||
};
|
||||
|
||||
const bar = () => 1;
|
||||
const bar = _temp;
|
||||
|
||||
t0 = x.foo();
|
||||
$[0] = t0;
|
||||
@@ -45,6 +45,9 @@ function hoisting() {
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: hoisting,
|
||||
|
||||
+10
-10
@@ -28,18 +28,9 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Foo(t0) {
|
||||
const $ = _c(1);
|
||||
const outer = _temp;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const outer = (val) => {
|
||||
const fact = (x) => {
|
||||
if (x <= 0) {
|
||||
return 1;
|
||||
}
|
||||
return x * fact(x - 1);
|
||||
};
|
||||
return fact(val);
|
||||
};
|
||||
|
||||
t1 = outer(3);
|
||||
$[0] = t1;
|
||||
} else {
|
||||
@@ -47,6 +38,15 @@ function Foo(t0) {
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
function _temp(val) {
|
||||
const fact = (x) => {
|
||||
if (x <= 0) {
|
||||
return 1;
|
||||
}
|
||||
return x * fact(x - 1);
|
||||
};
|
||||
return fact(val);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
|
||||
+4
-1
@@ -31,7 +31,7 @@ function hoisting() {
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const foo = () => bar();
|
||||
|
||||
const bar = () => 1;
|
||||
const bar = _temp;
|
||||
|
||||
t0 = foo();
|
||||
$[0] = t0;
|
||||
@@ -40,6 +40,9 @@ function hoisting() {
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: hoisting,
|
||||
|
||||
+6
-7
@@ -26,15 +26,9 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(t0) {
|
||||
const $ = _c(1);
|
||||
const outer = _temp;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const outer = () => {
|
||||
const inner = () => x;
|
||||
|
||||
const x = 3;
|
||||
return inner();
|
||||
};
|
||||
|
||||
t1 = <div>{outer()}</div>;
|
||||
$[0] = t1;
|
||||
} else {
|
||||
@@ -42,6 +36,11 @@ function Component(t0) {
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
function _temp() {
|
||||
const inner = () => x;
|
||||
const x = 3;
|
||||
return inner();
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Foo() {
|
||||
type X = number;
|
||||
interface Bar {
|
||||
baz: number;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Foo() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) 0
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
function Foo() {
|
||||
type X = number;
|
||||
interface Bar {
|
||||
baz: number;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [],
|
||||
};
|
||||
+4
-11
@@ -49,17 +49,7 @@ function Component(t0) {
|
||||
let t1;
|
||||
if ($[0] !== items) {
|
||||
t1 =
|
||||
items.length > 0 ? (
|
||||
<Foo
|
||||
value={
|
||||
<Bar>
|
||||
{items.map((item) => (
|
||||
<Item key={item.id} item={item} />
|
||||
))}
|
||||
</Bar>
|
||||
}
|
||||
/>
|
||||
) : null;
|
||||
items.length > 0 ? <Foo value={<Bar>{items.map(_temp)}</Bar>} /> : null;
|
||||
$[0] = items;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
@@ -67,6 +57,9 @@ function Component(t0) {
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
function _temp(item) {
|
||||
return <Item key={item.id} item={item} />;
|
||||
}
|
||||
|
||||
function Foo(t0) {
|
||||
const { value } = t0;
|
||||
|
||||
+4
-12
@@ -39,18 +39,7 @@ function Component(t0) {
|
||||
const { items } = t0;
|
||||
let t1;
|
||||
if ($[0] !== items) {
|
||||
t1 =
|
||||
items.length > 0 ? (
|
||||
<Foo
|
||||
value={
|
||||
<>
|
||||
{items.map((item) => (
|
||||
<Stringify key={item.id} item={item} />
|
||||
))}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
) : null;
|
||||
t1 = items.length > 0 ? <Foo value={<>{items.map(_temp)}</>} /> : null;
|
||||
$[0] = items;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
@@ -58,6 +47,9 @@ function Component(t0) {
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
function _temp(item) {
|
||||
return <Stringify key={item.id} item={item} />;
|
||||
}
|
||||
|
||||
function Foo(t0) {
|
||||
const $ = _c(2);
|
||||
|
||||
+2
-1
@@ -29,7 +29,7 @@ function Foo() {
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = function a(t2) {
|
||||
const x_0 = t2 === undefined ? () => {} : t2;
|
||||
const x_0 = t2 === undefined ? _temp : t2;
|
||||
return x_0;
|
||||
};
|
||||
$[0] = t1;
|
||||
@@ -39,6 +39,7 @@ function Foo() {
|
||||
t0 = t1;
|
||||
return t0;
|
||||
}
|
||||
function _temp() {}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
|
||||
+5
-9
@@ -33,17 +33,10 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(3);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.items) {
|
||||
let t1;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = (item) => item != null;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
t0 = props.items.filter(t1);
|
||||
t0 = props.items.filter(_temp);
|
||||
$[0] = props.items;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
@@ -52,6 +45,9 @@ function Component(props) {
|
||||
const filtered = t0;
|
||||
return filtered;
|
||||
}
|
||||
function _temp(item) {
|
||||
return item != null;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
+38
-57
@@ -32,88 +32,69 @@ import { c as _c } from "react/compiler-runtime"; // @disableMemoizationForDebug
|
||||
import { useMemo } from "react";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(9);
|
||||
const $ = _c(7);
|
||||
const a = useMemo(() => <div>{props.a}</div>, [props]);
|
||||
let t0;
|
||||
{
|
||||
t0 = props.b;
|
||||
let condition = $[0] !== props.b || true;
|
||||
if (!condition) {
|
||||
let old$t0 = $[1];
|
||||
$structuralCheck(old$t0, t0, "t0", "Component", "cached", "(6:6)");
|
||||
}
|
||||
$[0] = props.b;
|
||||
$[1] = t0;
|
||||
if (condition) {
|
||||
t0 = props.b;
|
||||
$structuralCheck($[1], t0, "t0", "Component", "recomputed", "(6:6)");
|
||||
t0 = $[1];
|
||||
}
|
||||
}
|
||||
let t1;
|
||||
{
|
||||
t1 = <div>{props.a}</div>;
|
||||
let condition = $[0] !== props;
|
||||
t1 = <div>{t0}</div>;
|
||||
let condition = $[2] !== t0 || true;
|
||||
if (!condition) {
|
||||
let old$t1 = $[1];
|
||||
$structuralCheck(old$t1, t1, "t1", "Component", "cached", "(5:5)");
|
||||
t1 = old$t1;
|
||||
let old$t1 = $[3];
|
||||
$structuralCheck(old$t1, t1, "t1", "Component", "cached", "(6:6)");
|
||||
}
|
||||
$[0] = props;
|
||||
$[1] = t1;
|
||||
$[2] = t0;
|
||||
$[3] = t1;
|
||||
if (condition) {
|
||||
t1 = <div>{props.a}</div>;
|
||||
$structuralCheck($[1], t1, "t1", "Component", "recomputed", "(5:5)");
|
||||
t1 = $[1];
|
||||
t1 = <div>{t0}</div>;
|
||||
$structuralCheck($[3], t1, "t1", "Component", "recomputed", "(6:6)");
|
||||
t1 = $[3];
|
||||
}
|
||||
}
|
||||
t0 = t1;
|
||||
const a = t0;
|
||||
const b = t1;
|
||||
let t2;
|
||||
{
|
||||
t2 = props.b;
|
||||
let condition = $[2] !== props.b;
|
||||
if (!condition) {
|
||||
let old$t2 = $[3];
|
||||
$structuralCheck(old$t2, t2, "t2", "Component", "cached", "(6:6)");
|
||||
}
|
||||
$[2] = props.b;
|
||||
$[3] = t2;
|
||||
if (condition) {
|
||||
t2 = props.b;
|
||||
$structuralCheck($[3], t2, "t2", "Component", "recomputed", "(6:6)");
|
||||
t2 = $[3];
|
||||
}
|
||||
}
|
||||
let t3;
|
||||
{
|
||||
t3 = <div>{t2}</div>;
|
||||
let condition = $[4] !== t2;
|
||||
if (!condition) {
|
||||
let old$t3 = $[5];
|
||||
$structuralCheck(old$t3, t3, "t3", "Component", "cached", "(6:6)");
|
||||
}
|
||||
$[4] = t2;
|
||||
$[5] = t3;
|
||||
if (condition) {
|
||||
t3 = <div>{t2}</div>;
|
||||
$structuralCheck($[5], t3, "t3", "Component", "recomputed", "(6:6)");
|
||||
t3 = $[5];
|
||||
}
|
||||
}
|
||||
const b = t3;
|
||||
let t4;
|
||||
{
|
||||
t4 = (
|
||||
t2 = (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
</div>
|
||||
);
|
||||
let condition = $[6] !== a || $[7] !== b;
|
||||
let condition = $[4] !== a || $[5] !== b || true;
|
||||
if (!condition) {
|
||||
let old$t4 = $[8];
|
||||
$structuralCheck(old$t4, t4, "t4", "Component", "cached", "(8:11)");
|
||||
let old$t2 = $[6];
|
||||
$structuralCheck(old$t2, t2, "t2", "Component", "cached", "(8:11)");
|
||||
}
|
||||
$[6] = a;
|
||||
$[7] = b;
|
||||
$[8] = t4;
|
||||
$[4] = a;
|
||||
$[5] = b;
|
||||
$[6] = t2;
|
||||
if (condition) {
|
||||
t4 = (
|
||||
t2 = (
|
||||
<div>
|
||||
{a}
|
||||
{b}
|
||||
</div>
|
||||
);
|
||||
$structuralCheck($[8], t4, "t4", "Component", "recomputed", "(8:11)");
|
||||
t4 = $[8];
|
||||
$structuralCheck($[6], t2, "t2", "Component", "recomputed", "(8:11)");
|
||||
t2 = $[6];
|
||||
}
|
||||
}
|
||||
return t4;
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @disableMemoizationForDebugging
|
||||
import { useMemo } from "react";
|
||||
|
||||
const w = 42;
|
||||
|
||||
function Component(a) {
|
||||
let x = useMemo(() => a.x, [a, w]);
|
||||
return <div>{x}</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ x: 42 }],
|
||||
isComponent: true,
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @disableMemoizationForDebugging
|
||||
import { useMemo } from "react";
|
||||
|
||||
const w = 42;
|
||||
|
||||
function Component(a) {
|
||||
const $ = _c(5);
|
||||
const t0 = w;
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== a || $[1] !== t0) {
|
||||
t2 = a.x;
|
||||
$[0] = a;
|
||||
$[1] = t0;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
}
|
||||
t1 = t2;
|
||||
const x = t1;
|
||||
let t3;
|
||||
if ($[3] !== x || true) {
|
||||
t3 = <div>{x}</div>;
|
||||
$[3] = x;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ x: 42 }],
|
||||
isComponent: true,
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>42</div>
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
// @disableMemoizationForDebugging
|
||||
import { useMemo } from "react";
|
||||
|
||||
const w = 42;
|
||||
|
||||
function Component(a) {
|
||||
let x = useMemo(() => a.x, [a, w]);
|
||||
return <div>{x}</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ x: 42 }],
|
||||
isComponent: true,
|
||||
};
|
||||
+4
-1
@@ -18,7 +18,7 @@ function Component(props) {
|
||||
const item = useFragment(graphql`...`, props.item);
|
||||
let t0;
|
||||
if ($[0] !== item.items) {
|
||||
t0 = item.items?.map((item_0) => renderItem(item_0)) ?? [];
|
||||
t0 = item.items?.map(_temp) ?? [];
|
||||
$[0] = item.items;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
@@ -26,6 +26,9 @@ function Component(props) {
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp(item_0) {
|
||||
return renderItem(item_0);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
// test outlined functions with destructured parameters - the
|
||||
// temporary for the destructured param must be promoted
|
||||
return (
|
||||
<>
|
||||
{props.items.map(({ id, name }) => (
|
||||
<Stringify key={id} name={name} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ items: [{ id: 1, name: "one" }] }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
let t0;
|
||||
if ($[0] !== props.items) {
|
||||
t0 = props.items.map(_temp);
|
||||
$[0] = props.items;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
let t1;
|
||||
if ($[2] !== t0) {
|
||||
t1 = <>{t0}</>;
|
||||
$[2] = t0;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
function _temp(t0) {
|
||||
const { id, name } = t0;
|
||||
return <Stringify key={id} name={name} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ items: [{ id: 1, name: "one" }] }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"name":"one"}</div>
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
// test outlined functions with destructured parameters - the
|
||||
// temporary for the destructured param must be promoted
|
||||
return (
|
||||
<>
|
||||
{props.items.map(({ id, name }) => (
|
||||
<Stringify key={id} name={name} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ items: [{ id: 1, name: "one" }] }],
|
||||
};
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
return (
|
||||
<div>
|
||||
{props.items.map((item) => (
|
||||
<Stringify key={item.id} item={item.name} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ items: [{ id: 1, name: "one" }] }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
let t0;
|
||||
if ($[0] !== props.items) {
|
||||
t0 = props.items.map(_temp);
|
||||
$[0] = props.items;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
let t1;
|
||||
if ($[2] !== t0) {
|
||||
t1 = <div>{t0}</div>;
|
||||
$[2] = t0;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
function _temp(item) {
|
||||
return <Stringify key={item.id} item={item.name} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ items: [{ id: 1, name: "one" }] }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div><div>{"item":"one"}</div></div>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
return (
|
||||
<div>
|
||||
{props.items.map((item) => (
|
||||
<Stringify key={item.id} item={item.name} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ items: [{ id: 1, name: "one" }] }],
|
||||
};
|
||||
+5
-10
@@ -22,22 +22,17 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
|
||||
import { useCallback } from "react";
|
||||
import { CONST_STRING0 } from "shared-runtime";
|
||||
|
||||
// It's correct to infer a useCallback block has no reactive dependencies
|
||||
function useFoo() {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => [CONST_STRING0];
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
return _temp;
|
||||
}
|
||||
function _temp() {
|
||||
return [CONST_STRING0];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+11
-14
@@ -40,7 +40,7 @@ import { useCallback } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Foo(t0) {
|
||||
const $ = _c(9);
|
||||
const $ = _c(8);
|
||||
const { arr1, arr2, foo } = t0;
|
||||
let t1;
|
||||
let getVal1;
|
||||
@@ -49,14 +49,8 @@ function Foo(t0) {
|
||||
|
||||
let y;
|
||||
y = [];
|
||||
let t2;
|
||||
if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = () => ({ x: 2 });
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
getVal1 = t2;
|
||||
|
||||
getVal1 = _temp;
|
||||
|
||||
t1 = () => [y];
|
||||
foo ? (y = x.concat(arr2)) : y;
|
||||
@@ -71,16 +65,19 @@ function Foo(t0) {
|
||||
}
|
||||
const getVal2 = t1;
|
||||
let t2;
|
||||
if ($[6] !== getVal1 || $[7] !== getVal2) {
|
||||
if ($[5] !== getVal1 || $[6] !== getVal2) {
|
||||
t2 = <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
|
||||
$[6] = getVal1;
|
||||
$[7] = getVal2;
|
||||
$[8] = t2;
|
||||
$[5] = getVal1;
|
||||
$[6] = getVal2;
|
||||
$[7] = t2;
|
||||
} else {
|
||||
t2 = $[8];
|
||||
t2 = $[7];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
function _temp() {
|
||||
return { x: 2 };
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableForest
|
||||
function Component({ base, start, increment, test }) {
|
||||
let value = base;
|
||||
for (let i = start; i < test; i += increment) {
|
||||
value += i;
|
||||
}
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ base: 0, start: 0, test: 10, increment: 1 }],
|
||||
sequentialRenders: [
|
||||
{ base: 0, start: 1, test: 10, increment: 1 },
|
||||
{ base: 0, start: 0, test: 10, increment: 2 },
|
||||
{ base: 2, start: 0, test: 10, increment: 2 },
|
||||
{ base: 0, start: 0, test: 11, increment: 2 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableForest
|
||||
function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const { base, start, increment, test } = t0;
|
||||
let value;
|
||||
if ($[0] !== base || $[1] !== start || $[2] !== test || $[3] !== increment) {
|
||||
value = base;
|
||||
for (let i = start; i < test; i = i + increment, i) {
|
||||
value = value + i;
|
||||
}
|
||||
$[0] = base;
|
||||
$[1] = start;
|
||||
$[2] = test;
|
||||
$[3] = increment;
|
||||
$[4] = value;
|
||||
} else {
|
||||
value = $[4];
|
||||
}
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ base: 0, start: 0, test: 10, increment: 1 }],
|
||||
sequentialRenders: [
|
||||
{ base: 0, start: 1, test: 10, increment: 1 },
|
||||
{ base: 0, start: 0, test: 10, increment: 2 },
|
||||
{ base: 2, start: 0, test: 10, increment: 2 },
|
||||
{ base: 0, start: 0, test: 11, increment: 2 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>45</div>
|
||||
<div>20</div>
|
||||
<div>22</div>
|
||||
<div>30</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user