From 144aec74d354385f35f486d996f14d8e9cdfdde3 Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Mon, 6 May 2024 10:31:41 +0200 Subject: [PATCH] fix: allow to access private fields after `this` reassignment --- .changeset/gentle-toys-chew.md | 5 +++++ .../src/compiler/phases/2-analyze/index.js | 12 ++++++++++ .../3-transform/client/visitors/global.js | 12 ++++++++-- .../client/visitors/javascript-runes.js | 22 +++++++++++++++++-- .../svelte/src/compiler/phases/types.d.ts | 6 +++++ .../_config.js | 10 +++++++++ .../main.svelte | 17 ++++++++++++++ 7 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 .changeset/gentle-toys-chew.md create mode 100644 packages/svelte/tests/runtime-runes/samples/class-private-fields-reassigned-this/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/class-private-fields-reassigned-this/main.svelte diff --git a/.changeset/gentle-toys-chew.md b/.changeset/gentle-toys-chew.md new file mode 100644 index 0000000000..dfc9f2013b --- /dev/null +++ b/.changeset/gentle-toys-chew.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: allow to access private fields after `this` reassignment diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index e4d6f6a4db..8e59bf632b 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -1503,6 +1503,18 @@ const common_visitors = { return; } } + }, + ThisExpression(node, { next, path }) { + const parent = path.at(-1); + if (parent?.type === 'MemberExpression' && parent.object === node) { + return; + } + const class_declaration = path.find((path_element) => path_element.type === 'ClassDeclaration'); + if (class_declaration && class_declaration.type === 'ClassDeclaration') { + class_declaration.metadata = { + needs_private_getters: true + }; + } } }; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/global.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/global.js index 811407b35b..4c8c61f62f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/global.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/global.js @@ -12,12 +12,20 @@ export const global_visitors = { return serialize_get_binding(node, state); } }, - MemberExpression(node, { state, next }) { + MemberExpression(node, { state, next, path }) { + const class_declaration = path.find((path_element) => path_element.type === 'ClassDeclaration'); + const needs_private_getters = + class_declaration?.type === 'ClassDeclaration' && + !!class_declaration.metadata?.needs_private_getters; + if (node.object.type === 'ThisExpression') { // rewrite `this.#foo` as `this.#foo.v` inside a constructor if (node.property.type === 'PrivateIdentifier') { const field = state.private_state.get(node.property.name); - if (field) { + if (field && needs_private_getters && state.in_constructor) { + return b.member(b.member(b.this, field.id), b.id('v')); + } + if (field && !needs_private_getters) { return state.in_constructor ? b.member(node, b.id('v')) : b.call('$.get', node); } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js index 9f3192c638..56f4765b29 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js @@ -8,7 +8,12 @@ import { regex_invalid_identifier_chars } from '../../../patterns.js'; /** @type {import('../types.js').ComponentVisitors} */ export const javascript_visitors_runes = { - ClassBody(node, { state, visit }) { + ClassBody(node, { state, visit, path }) { + const parent = path.at(-1); + + const needs_private_getters = + parent?.type === 'ClassDeclaration' && !!parent.metadata?.needs_private_getters; + /** @type {Map} */ const public_state = new Map(); @@ -64,6 +69,19 @@ export const javascript_visitors_runes = { } } + if (needs_private_getters) { + // each `#foo = $state()` needs a backing `#_foo` field is the class needs private getters + for (const [name, field] of private_state) { + let deconflicted = name; + while (private_ids.includes(deconflicted)) { + deconflicted = '_' + deconflicted; + } + + private_ids.push(deconflicted); + field.id = b.private_id(deconflicted); + } + } + // each `foo = $state()` needs a backing `#foo` field for (const [name, field] of public_state) { let deconflicted = name; @@ -121,7 +139,7 @@ export const javascript_visitors_runes = { value = b.call('$.source'); } - if (is_private) { + if (is_private && !needs_private_getters) { body.push(b.prop_def(field.id, value)); } else { // #foo; diff --git a/packages/svelte/src/compiler/phases/types.d.ts b/packages/svelte/src/compiler/phases/types.d.ts index bd6cad135a..7266ba080d 100644 --- a/packages/svelte/src/compiler/phases/types.d.ts +++ b/packages/svelte/src/compiler/phases/types.d.ts @@ -103,4 +103,10 @@ declare module 'estree' { scope: Scope; }; } + + interface ClassDeclaration { + metadata?: { + needs_private_getters: boolean; + }; + } } diff --git a/packages/svelte/tests/runtime-runes/samples/class-private-fields-reassigned-this/_config.js b/packages/svelte/tests/runtime-runes/samples/class-private-fields-reassigned-this/_config.js new file mode 100644 index 0000000000..440659df88 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-private-fields-reassigned-this/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + async test({ assert, logs }) { + assert.deepEqual(logs, ['init', 1]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-private-fields-reassigned-this/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-private-fields-reassigned-this/main.svelte new file mode 100644 index 0000000000..1548672c0f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-private-fields-reassigned-this/main.svelte @@ -0,0 +1,17 @@ +