From: zhiyuanzmj <260480378@qq.com> Date: Fri, 20 Jun 2025 00:16:47 +0000 (+0800) Subject: fix(compiler-vapor): prevent caching UpdateExpression (#13346) X-Git-Tag: v3.6.0-alpha.1~16^2~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ed05053fc151e9576e02816df5635972b02bd7b9;p=thirdparty%2Fvuejs%2Fcore.git fix(compiler-vapor): prevent caching UpdateExpression (#13346) --- diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap index 518c2a5fe7..454e50e9cb 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap @@ -32,3 +32,25 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { return n0 }" `; + +exports[`compiler: expression > update expression 1`] = ` +"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("
", true) + +export function render(_ctx) { + const n1 = t0() + const n0 = _child(n1) + const x1 = _child(n1) + _renderEffect(() => { + const _String = String + const _foo = _ctx.foo + + _setText(n0, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar)) + _setText(x1, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar)) + _setProp(n1, "id", _String(_foo.id++)) + _setProp(n1, "foo", _foo) + _setProp(n1, "bar", _ctx.bar++) + }) + return n1 +}" +`; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap index 4a691056ae..cb520a4b2a 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap @@ -21,7 +21,14 @@ export function render(_ctx) { const _setTemplateRef = _createTemplateRefSetter() const n0 = t0() let r0 - _renderEffect(() => r0 = _setTemplateRef(n0, bar => _ctx.foo = bar, r0)) + _renderEffect(() => { + const _foo = _ctx.foo + r0 = _setTemplateRef(n0, bar => { + _foo.value = bar + ;({ baz: _ctx.baz } = bar) + console.log(_foo.value, _ctx.baz) + }, r0) + }) return n0 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/expression.spec.ts b/packages/compiler-vapor/__tests__/transforms/expression.spec.ts index c97decd9dd..5983bde67d 100644 --- a/packages/compiler-vapor/__tests__/transforms/expression.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/expression.spec.ts @@ -1,9 +1,15 @@ import { BindingTypes } from '@vue/compiler-dom' -import { transformChildren, transformText } from '../../src' +import { + transformChildren, + transformElement, + transformText, + transformVBind, +} from '../../src' import { makeCompile } from './_utils' const compileWithExpression = makeCompile({ - nodeTransforms: [transformChildren, transformText], + nodeTransforms: [transformElement, transformChildren, transformText], + directiveTransforms: { bind: transformVBind }, }) describe('compiler: expression', () => { @@ -31,4 +37,14 @@ describe('compiler: expression', () => { expect(code).toMatchSnapshot() expect(code).contains(`$props['bar']`) }) + + test('update expression', () => { + const { code } = compileWithExpression(` +
+ {{ String(foo.id++) }} {{ foo }} {{ bar }} +
+ `) + expect(code).toMatchSnapshot() + expect(code).contains(`_String(_foo.id++)`) + }) }) diff --git a/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts index f026675e4e..b6bc479a01 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts @@ -83,7 +83,11 @@ describe('compiler: template ref transform', () => { test('function ref', () => { const { ir, code } = compileWithTransformRef( - `
`, + `
`, ) expect(ir.block.dynamic.children[0]).toMatchObject({ id: 0, @@ -103,7 +107,6 @@ describe('compiler: template ref transform', () => { type: IRNodeTypes.SET_TEMPLATE_REF, element: 0, value: { - content: 'bar => foo = bar', isStatic: false, }, }, @@ -112,7 +115,11 @@ describe('compiler: template ref transform', () => { ]) expect(code).toMatchSnapshot() expect(code).contains('const _setTemplateRef = _createTemplateRefSetter()') - expect(code).contains('_setTemplateRef(n0, bar => _ctx.foo = bar, r0)') + expect(code).contains(`_setTemplateRef(n0, bar => { + _foo.value = bar + ;({ baz: _ctx.baz } = bar) + console.log(_foo.value, _ctx.baz) + }, r0)`) }) test('ref + v-if', () => { diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts index eab50c6259..76b04f58d0 100644 --- a/packages/compiler-vapor/src/generators/expression.ts +++ b/packages/compiler-vapor/src/generators/expression.ts @@ -244,8 +244,13 @@ export function processExpressions( expressions: SimpleExpressionNode[], ): DeclarationResult { // analyze variables - const { seenVariable, variableToExpMap, expToVariableMap, seenIdentifier } = - analyzeExpressions(expressions) + const { + seenVariable, + variableToExpMap, + expToVariableMap, + seenIdentifier, + updatedVariable, + } = analyzeExpressions(expressions) // process repeated identifiers and member expressions // e.g., `foo[baz]` will be transformed into `foo_baz` @@ -255,6 +260,7 @@ export function processExpressions( variableToExpMap, expToVariableMap, seenIdentifier, + updatedVariable, ) // process duplicate expressions after identifier and member expression handling. @@ -263,6 +269,8 @@ export function processExpressions( context, expressions, varDeclarations, + updatedVariable, + expToVariableMap, ) return genDeclarations([...varDeclarations, ...expDeclarations], context) @@ -273,11 +281,13 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) { const variableToExpMap = new Map>() const expToVariableMap = new Map() const seenIdentifier = new Set() + const updatedVariable = new Set() const registerVariable = ( name: string, exp: SimpleExpressionNode, isIdentifier: boolean, + parentStack: Node[] = [], ) => { if (isIdentifier) seenIdentifier.add(name) seenVariable[name] = (seenVariable[name] || 0) + 1 @@ -286,6 +296,13 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) { (variableToExpMap.get(name) || new Set()).add(exp), ) expToVariableMap.set(exp, (expToVariableMap.get(exp) || []).concat(name)) + if ( + parentStack.some( + p => p.type === 'UpdateExpression' || p.type === 'AssignmentExpression', + ) + ) { + updatedVariable.add(name) + } } for (const exp of expressions) { @@ -299,14 +316,20 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) { const memberExp = extractMemberExpression(parent, name => { registerVariable(name, exp, true) }) - registerVariable(memberExp, exp, false) + registerVariable(memberExp, exp, false, parentStack) } else if (!parentStack.some(isMemberExpression)) { - registerVariable(currentNode.name, exp, true) + registerVariable(currentNode.name, exp, true, parentStack) } }) } - return { seenVariable, seenIdentifier, variableToExpMap, expToVariableMap } + return { + seenVariable, + seenIdentifier, + variableToExpMap, + expToVariableMap, + updatedVariable, + } } function processRepeatedVariables( @@ -315,9 +338,11 @@ function processRepeatedVariables( variableToExpMap: Map>, expToVariableMap: Map, seenIdentifier: Set, + updatedVariable: Set, ): DeclarationValue[] { const declarations: DeclarationValue[] = [] for (const [name, exps] of variableToExpMap) { + if (updatedVariable.has(name)) continue if (seenVariable[name] > 1 && exps.size > 0) { const isIdentifier = seenIdentifier.has(name) const varName = isIdentifier ? name : genVarName(name) @@ -409,12 +434,19 @@ function processRepeatedExpressions( context: CodegenContext, expressions: SimpleExpressionNode[], varDeclarations: DeclarationValue[], + updatedVariable: Set, + expToVariableMap: Map, ): DeclarationValue[] { const declarations: DeclarationValue[] = [] const seenExp = expressions.reduce( (acc, exp) => { + const variables = expToVariableMap.get(exp) // only handle expressions that are not identifiers - if (exp.ast && exp.ast.type !== 'Identifier') { + if ( + exp.ast && + exp.ast.type !== 'Identifier' && + !(variables && variables.some(v => updatedVariable.has(v))) + ) { acc[exp.content] = (acc[exp.content] || 0) + 1 } return acc