diff --git a/src/bindings.mjs b/src/bindings.mjs index a495e3c8f5b..ca7341bf1f9 100644 --- a/src/bindings.mjs +++ b/src/bindings.mjs @@ -471,6 +471,14 @@ export function registerGlobalIdent( // For constants that's easy but for lets this severely restricts what we can track. In most cases just the typeof. // See getCleanTypingObject() typing: getCleanTypingObject(), + + // Array>, available after phase1 + // Only set for functions that get called at least once. And only for "ident" calls (that + // means `f()` not `x.f()` kinds of calls). Consumer still has to verify the function does + // not escape. If a param index is "undefined" then either a call did not have a primitive + // there, or two calls had different primitives there. Otherwise, all ident-calls had the + // same primitive in that index. + callerArgs: undefined, }; ASSERT(name); ASSERT(!/^\$\$\d+$/.test(name), 'Should not be calling this function for special param name idents $$123'); diff --git a/src/index.mjs b/src/index.mjs index 36bcfc73262..486b341cfe4 100644 --- a/src/index.mjs +++ b/src/index.mjs @@ -10,6 +10,7 @@ import { phase0 } from './normalize/phase0.mjs'; import { phase1 } from './normalize/phase1.mjs'; import { phase2 } from './normalize/phase2.mjs'; import { phase3 } from './normalize/phase3.mjs'; +import { phase1_1 } from './normalize/phase1_1.mjs'; export function preval({ entryPointFile, stdio, verbose, verboseTracing, resolve, req, stopAfterNormalize, refTracing, options = {} }) { if (stdio) setStdio(stdio, verbose); @@ -242,6 +243,7 @@ export function preval({ entryPointFile, stdio, verbose, verboseTracing, resolve ++phase1s; phase1(fdata, resolve, req, firstAfterParse, passes, phase1s, !firstAfterParse && options.refTest); // I want a phase1 because I want the scope tracking set up for normalizing bindings + phase1_1(fdata, resolve, req, firstAfterParse, passes, phase1s, !firstAfterParse && options.refTest); contents.lastPhase1Ast = fdata.tenkoOutput.ast; options?.onAfterPhase(1, passes, phaseLoop, fdata, false, options); diff --git a/src/normalize/phase1_1.mjs b/src/normalize/phase1_1.mjs new file mode 100644 index 00000000000..62048841c72 --- /dev/null +++ b/src/normalize/phase1_1.mjs @@ -0,0 +1,295 @@ +import walk from '../../lib/walk.mjs'; +import * as AST from '../ast.mjs'; +import { VERBOSE_TRACING, RED, BLUE, DIM, RESET, setVerboseTracing } from '../constants.mjs'; +import { + ASSERT, + log, + group, + groupEnd, + vlog, + vgroup, + vgroupEnd, + tmat, + fmat, + source, + REF_TRACK_TRACING, + assertNoDupeNodes, +} from '../utils.mjs'; + +// This phase walks the AST a few times to discover things for which it needs phase1 to complete +// Currently it discovers call args (for which it needs the meta.typing data) + +export function phase1_1(fdata, resolve, req, firstAfterParse, passes, phase1s, refTest) { + const ast = fdata.tenkoOutput.ast; + + const start = Date.now(); + + group( + '\n\n\n##################################\n## phase1.1 (first=' + + firstAfterParse + + ', refTest=' + + !!refTest + + ') :: ' + + fdata.fname + + ', pass=' + passes + ', phase1s=', phase1s, ', len:', fdata.len, '\n##################################\n\n\n', + ); + try { + if (VERBOSE_TRACING || REF_TRACK_TRACING) { + const code = fmat(tmat(ast, true), true); + console.log('\nCurrent state (start of phase1)\n--------------\n' + code + '\n--------------\n'); + } + } catch (e) { + vlog('printing ast failed'); + console.dir(ast, { depth: null }); + + throw e; + } + vlog('\n\n\n#################################################################### phase1.1 [',passes,'::', phase1s, ']\n\n\n'); + + const tracingValueBefore = VERBOSE_TRACING; + if (passes > 1 || phase1s > 1) { + vlog('(Disabling verbose tracing for phase 1 after the first pass)'); + setVerboseTracing(false); + } + + let called = 0; + + // Discover param types. If an ident always gets invoked in the same way then that's information + // we can use with the function param. We still have to confirm that the ref doesn't escape + // anywhere because then we may not be able to determine how the function gets called. + + function _callWalker(node, before, nodeType, path) { + if (before) return; + if (nodeType !== 'CallExpression') return; + if (node.callee.type !== 'Identifier') return; + + const calleeName = node.callee.name; + + const meta = fdata.globallyUniqueNamingRegistry.get(calleeName); + ASSERT(meta); + // Currently we only track whether the same arg type was passed on in every call that we could see + // The consumer of this data is to verify that the function does not escape + // If the callerArgs value is not set then we assume this is the first call occurrence + // If it exists, the type must either match and otherwise it is set to undefined, indicating "unknown" + // If it's undefined then this simple check could not determine a monomorphic primitive type for this arg index + + vlog('Processing a CallExpression for "', calleeName, '"'); + if (meta.callerArgs?.length === 0) { + // Already invalidated before. Or called with no args, which we wouldn't handle well in this context. + vlog('-', calleeName, ', ignored, callerArgs is empty array so we ignore further calls to this func'); + return; + } + + if (node.arguments.some(anode => anode.type === 'SpreadElement')) { + // Too risky. We could still support some specific cases if we wanted. + vlog('- bail:', calleeName, 'was seen to have a spreadElement in at least one call arg, sealing func'); + meta.callerArgs = []; + return; + } + + // Note: function escape analysis happens in the next block. Here we just track calls, regardless. + if (meta.callerArgs) { + node.arguments.forEach((anode, i) => { + if (AST.isPrimitive(anode)) { + const seen = AST.getPrimitiveType(anode); + if (seen !== meta.callerArgs[i]) { + vlog('- bail', calleeName, ', index', i, ': at least one call had a different type than another', seen, meta.callerArgs[i]); + meta.callerArgs[i] = undefined; + } + } + else if (anode.type === 'Identifier') { + const m = fdata.globallyUniqueNamingRegistry.get(anode.name); + if (!m.typing.mustBeType || m.typing.mustBeType !== meta.callerArgs[i]) { + vlog('- bail', calleeName, ', index', i, ': at least one call had a different mustBeType than another or none at all', m.typing.mustBeType, meta.callerArgs[i]); + meta.callerArgs[i] = undefined; + } + } + else { + vlog('- bail', calleeName, ', index', i, ': unexpected arg type', anode.type); + meta.callerArgs[i] = undefined; + } + }); + if (node.arguments.length < meta.callerArgs.length) { + vlog('-', calleeName, ' was called with fewer args than the previous calls, marking the remaining args as `undefined` from index', node.arguments.length, 'onward'); + for (let i=node.arguments.length; i { + if (AST.isPrimitive(anode)) { + const seen = AST.getPrimitiveType(anode); + vlog('- first:', calleeName, ', index', i, ': found arg as primitive:', seen); + return seen; + } + if (anode.type === 'Identifier') { + const m = fdata.globallyUniqueNamingRegistry.get(anode.name); + vlog('- first:', calleeName, ', index', i, ': ident with mustBeType:', m.typing.mustBeType); + if (m.typing.mustBeType) return m.typing.mustBeType; + return undefined; + } + else { + vlog('- bail:', calleeName, ', index', i, ': not a primitive and not an identifier:', anode.type); + return undefined; + } + }); + } + vlog('-', calleeName, ': callerArgs after call step =', meta.callerArgs); + } + + const now = Date.now(); + group('Walking AST to collect func call arg types...'); + walk(_callWalker, ast, 'ast'); + groupEnd(); + log('Walked AST to collect func call arg types', Date.now() - now, 'ms'); + + // Set the param typing if we know + const now2 = Date.now(); + function _paramWalker(node, before, nodeType, path) { + if (before) return; + if (nodeType !== 'FunctionExpression') return; + + const pathNodes = path.nodes; + const pathProps = path.props; + const pathIndexes = path.indexes; + + const parentNode = pathNodes[pathNodes.length - 2]; + const parentProp = pathProps[pathProps.length - 1]; + const parentIndex = pathIndexes[pathIndexes.length - 1]; + + let funcName; + if (parentNode.type === 'VariableDeclarator') { + funcName = parentNode.id.name; + } + else if (parentNode.type === 'AssignmentExpression' && parentNode.left.type === 'Identifier') { + funcName = parentNode.left.name; + } + else { + // fizzle + vlog('Bail: function with pid @', +node.$p.pid, 'is not the init of a var decl nor the rhs of an assignment', parentNode.type); + return; + } + + const meta = fdata.globallyUniqueNamingRegistry.get(funcName); + vlog('Processing callerArgs for function "', funcName, '":', meta.callerArgs); + if (!meta.callerArgs) { + vlog('- bail: no callerArgs set so maybe function wasnt (clearly) called at all?'); + return; + } + if (!meta.callerArgs.length) { + vlog('- bail: callerArgs is empty array, either at least one call passed on zero args or there was a blocking problem'); + return; + } + + vlog('- Verify whether func escapes...'); + // We have to do escape analysis first. We must assert the function always gets called. + // Writes are not relevant. We're only tracking how it gets called. So if + // you're something like `let f = function(){}; f(); f = 1; f();` so be it. + // Confirm that all usages of the ident are regular calls. We may be able + // to extend this later to other cases as they present themselves. + if (meta.reads.some(read => { + if (read.parentNode.type !== 'CallExpression' || read.parentProp !== 'callee') { + vlog(' - Found a case where it was not a call:', read.parentNode.type); + // Hmmm + // At least one read was not callee of a call expression + // There's one thing we'll try to salvage: an alias where the alias always gets called. + // If we can verify the alias then we merge the alias callerArgs with this one and move on. + + const isAssign = read.parentNode.type === 'AssignmentExpression' && read.parentProp === 'right' && read.parentNode.left.type === 'Identifier'; + const isVar = !isAssign && read.parentNode.type === 'VariableDeclarator' && read.parentProp === 'init'; + + vlog(' - Is it aliased?:', isAssign, isVar); + + if (isAssign || isVar) { + vlog(' - Yes, trying to confirm whether the alias is always called so we can merge their callerArgs'); + // Trace the alias. + const aliasName = isAssign ? read.parentNode.left.name : read.parentNode.id.name; + const aliasMeta = fdata.globallyUniqueNamingRegistry.get(aliasName); + if (aliasMeta.writes.length === 1 && aliasMeta.reads.every(aliasRead => aliasRead.parentNode.type === 'CallExpression' && aliasRead.parentProp === 'callee')) { + vlog(' - Confirmed that the alias "', aliasName, '" is always called. Merging callerArgs', meta.callerArgs, aliasMeta.callerArgs); + // We should be able to use/merge the callerArgs of this alias since it was only assigned our target function + // and only called otherwise. Effectively, it's an identical alias and so all calls can be attributed to our + // target function. + if (meta.callerArgs) { + for (let i=0, len = Math.min(aliasMeta.callerArgs?.length ?? 0, meta.callerArgs?.length ?? 0); i { + const paramName = pnode.$p.paramVarDeclRef?.name ?? ''; + if (pnode.rest) { // A Param has param.rest, not 'RestElement' + // fizzle the remainder + meta.callerArgs.length = i; // removes the remainder to indicate "do not use" + vlog(' - param', i, '(', paramName, '); Bail: The function has at least one rest param. Bailing on this and the remainder params'); + return true; + } + if (meta.callerArgs[i] === undefined) { + vlog(' - param', i, '(', paramName, '); Bail: No consistent primitive type found in all calls'); + return; + } + if (!pnode.$p.paramVarDeclRef) { + vlog(' - param', i, '(', paramName, '); Bail: No local decl to update...'); + return; + } + + const m = fdata.globallyUniqueNamingRegistry.get(paramName); + if (m.writes.length > 1) { + vlog(' - param', i, '(', paramName, '); Bail: The param is written to later so we bail on callerArgs for this param for now'); + // This param is assigned to multiple times. Risky and we should bail for now. + meta.callerArgs[i] = undefined; // seal + } + else if (m.typing.mustBeType === undefined) { + // Not yet determined so now we do + vlog(' - param', i, '(', paramName, '); Ok! Marking param as:', meta.callerArgs[i], ', writes:', m.writes.length); + m.typing.mustBeType = meta.callerArgs[i]; + } + else if (m.typing.mustBeType !== false && m.typing.mustBeType !== meta.callerArgs[i]) { + // It was determined but different from this information. Not sure how I feel about that... + // This does happen legit when there's an assignment to the param, so that can happen. + ASSERT(m.writes.length > 1, 'this is a bad omen. unless the param was assigned to, it means the heuristic was incorrect...'); + vlog(' - param', i, '(', paramName, '); Caller arg was', meta.callerArgs[i], 'but mustBeType was already set to', m.typing.mustBeType, '; setting it to false now'); + m.typing.mustBeType = false; + } + }); + vlog('- Caller args after update:', meta.callerArgs); + } + + vlog(''); + group('Walking AST to apply callArgs and assign type to params...'); + walk(_paramWalker, ast, 'ast'); + groupEnd(); + log('Walked AST to type params, in', Date.now() - now2, 'ms'); + + assertNoDupeNodes(ast, 'body'); + + setVerboseTracing(tracingValueBefore); + + log('\n\nEnd of phase 1. Walker called', called, 'times, took', Date.now() - start, 'ms'); + + groupEnd(); +} diff --git a/tests/cases/function_inlining/expr_assign_of_typeof_param_with_spread_before2.md b/tests/cases/function_inlining/expr_assign_of_typeof_param_with_spread_before2.md new file mode 100644 index 00000000000..7781b9b679a --- /dev/null +++ b/tests/cases/function_inlining/expr_assign_of_typeof_param_with_spread_before2.md @@ -0,0 +1,122 @@ +# Preval test case + +# expr_assign_of_typeof_param_with_spread_before2.md + +> Function inlining > Expr assign of typeof param with spread before2 +> +> A function that does a simple thing may need to be inlined in trivial cases. + +In this case the assignment used a param. + +The call has a spread before the param index so we must bail. + +## Input + +`````js filename=intro +let x = 0; +function f() { + function g(a, b) { + x = typeof b; + } + const arr = $(['uh', 'oh', 'no']); + g(...arr, 10, 20, 30, 40, 50, 60); +} +f(); +$(x); +````` + +## Pre Normal + + +`````js filename=intro +let f = function () { + debugger; + let g = function ($$0, $$1) { + let a = $$0; + let b = $$1; + debugger; + x = typeof b; + }; + const arr = $([`uh`, `oh`, `no`]); + g(...arr, 10, 20, 30, 40, 50, 60); +}; +let x = 0; +f(); +$(x); +````` + +## Normalized + + +`````js filename=intro +let f = function () { + debugger; + let g = function ($$0, $$1) { + let a = $$0; + let b = $$1; + debugger; + x = typeof b; + return undefined; + }; + const tmpCallCallee = $; + const tmpCalleeParam = [`uh`, `oh`, `no`]; + const arr = tmpCallCallee(tmpCalleeParam); + g(...arr, 10, 20, 30, 40, 50, 60); + return undefined; +}; +let x = 0; +f(); +$(x); +````` + +## Output + + +`````js filename=intro +let x = 0; +const g = function ($$0, $$1) { + const b = $$1; + debugger; + x = typeof b; + return undefined; +}; +const tmpCalleeParam = [`uh`, `oh`, `no`]; +const arr = $(tmpCalleeParam); +g(...arr, 10, 20); +$(x); +````` + +## PST Output + +With rename=true + +`````js filename=intro +let a = 0; +const b = function($$0,$$1 ) { + const c = d; + debugger; + a = typeof c; + return undefined; +}; +const e = [ "uh", "oh", "no" ]; +const f = $( e ); +b( ... f, 10, 20 ); +$( a ); +````` + +## Globals + +None + +## Result + +Should call `$` with: + - 1: ['uh', 'oh', 'no'] + - 2: 'string' + - eval returned: undefined + +Pre normalization calls: Same + +Normalized calls: Same + +Final output calls: Same diff --git a/tests/cases/let_aliases/parseint.md b/tests/cases/let_aliases/parseint.md new file mode 100644 index 00000000000..49423a0a020 --- /dev/null +++ b/tests/cases/let_aliases/parseint.md @@ -0,0 +1,149 @@ +# Preval test case + +# parseint.md + +> Let aliases > Parseint +> +> When two const bindings are assigned the same value, they are an alias + +## Input + +`````js filename=intro +const n = $('1'); +let the_let_binding = $(1); + +function f() { + the_let_binding = 2; + f = function() { + return the_let_binding + }; + return f(); +} + +// a and b are clearly an alias +const a = the_let_binding; +const m = parseInt(n); +const mm = m / 33; +const the_let_alias_to_eliminate = a; +$(a, the_let_alias_to_eliminate); +$(f); +$(mm); +````` + +## Pre Normal + + +`````js filename=intro +let f = function () { + debugger; + the_let_binding = 2; + f = function () { + debugger; + return the_let_binding; + }; + return f(); +}; +const n = $(`1`); +let the_let_binding = $(1); +const a = the_let_binding; +const m = parseInt(n); +const mm = m / 33; +const the_let_alias_to_eliminate = a; +$(a, the_let_alias_to_eliminate); +$(f); +$(mm); +````` + +## Normalized + + +`````js filename=intro +let f = function () { + debugger; + the_let_binding = 2; + f = function () { + debugger; + return the_let_binding; + }; + const tmpReturnArg = f(); + return tmpReturnArg; +}; +const n = $(`1`); +let the_let_binding = $(1); +const a = the_let_binding; +const m = parseInt(n); +const mm = m / 33; +const the_let_alias_to_eliminate = a; +$(a, the_let_alias_to_eliminate); +$(f); +$(mm); +````` + +## Output + + +`````js filename=intro +let f = function () { + debugger; + the_let_binding = 2; + f = function () { + debugger; + return the_let_binding; + }; + const tmpReturnArg = f(); + return tmpReturnArg; +}; +const n = $(`1`); +let the_let_binding = $(1); +const a = the_let_binding; +const m = parseInt(n); +$(a, a); +$(f); +const mm = m / 33; +$(mm); +````` + +## PST Output + +With rename=true + +`````js filename=intro +let a = function() { + debugger; + b = 2; + a = function() { + debugger; + return b; + }; + const c = a(); + return c; +}; +const d = $( "1" ); +let b = $( 1 ); +const e = b; +const f = parseInt( d ); +$( e, e ); +$( a ); +const g = f / 33; +$( g ); +````` + +## Globals + +None + +## Result + +Should call `$` with: + - 1: '1' + - 2: 1 + - 3: 1, 1 + - 4: '' + - 5: 0.030303030303030304 + - eval returned: undefined + +Pre normalization calls: Same + +Normalized calls: Same + +Final output calls: Same diff --git a/tests/cases/math_random/floor_trick/rng3.md b/tests/cases/math_random/floor_trick/rng3.md new file mode 100644 index 00000000000..53aab6d2b36 --- /dev/null +++ b/tests/cases/math_random/floor_trick/rng3.md @@ -0,0 +1,110 @@ +# Preval test case + +# rng3.md + +> Math random > Floor trick > Rng3 +> +> In this case the result is 1 2 or 3 and we can't really predict much more than that. + +## Input + +`````js filename=intro +const r = Math.random(); +const a = r * 3; +const is_012 = Math.floor(a); +const is_123 = is_012 + 1; +$(is_123 === 1 || is_123 === 2 || is_123 === 3); +````` + +## Pre Normal + + +`````js filename=intro +const r = Math.random(); +const a = r * 3; +const is_012 = Math.floor(a); +const is_123 = is_012 + 1; +$(is_123 === 1 || is_123 === 2 || is_123 === 3); +````` + +## Normalized + + +`````js filename=intro +const r = Math.random(); +const a = r * 3; +const is_012 = Math.floor(a); +const is_123 = is_012 + 1; +const tmpCallCallee = $; +let tmpCalleeParam = is_123 === 1; +if (tmpCalleeParam) { +} else { + tmpCalleeParam = is_123 === 2; + if (tmpCalleeParam) { + } else { + tmpCalleeParam = is_123 === 3; + } +} +tmpCallCallee(tmpCalleeParam); +````` + +## Output + + +`````js filename=intro +const r = Math.random(); +const a = r * 3; +const is_012 = Math.floor(a); +const is_123 = is_012 + 1; +let tmpCalleeParam = is_123 === 1; +if (tmpCalleeParam) { +} else { + tmpCalleeParam = is_123 === 2; + if (tmpCalleeParam) { + } else { + tmpCalleeParam = is_123 === 3; + } +} +$(tmpCalleeParam); +````` + +## PST Output + +With rename=true + +`````js filename=intro +const a = Math.random(); +const b = a * 3; +const c = Math.floor( b ); +const d = c + 1; +let e = d === 1; +if (e) { + +} +else { + e = d === 2; + if (e) { + + } + else { + e = d === 3; + } +} +$( e ); +````` + +## Globals + +None + +## Result + +Should call `$` with: + - 1: true + - eval returned: undefined + +Pre normalization calls: Same + +Normalized calls: Same + +Final output calls: Same diff --git a/tests/cases/normalize/arguments/param_default.md b/tests/cases/normalize/arguments/param_default.md index e0b14299791..2f60296c093 100644 --- a/tests/cases/normalize/arguments/param_default.md +++ b/tests/cases/normalize/arguments/param_default.md @@ -56,19 +56,7 @@ tmpCallCallee(tmpCalleeParam); `````js filename=intro -const f = function ($$0) { - const tmpPrevalAliasArgumentsAny = arguments; - const tmpParamBare = $$0; - debugger; - const tmpIfTest = tmpParamBare === undefined; - if (tmpIfTest) { - return tmpPrevalAliasArgumentsAny; - } else { - return tmpParamBare; - } -}; -const tmpCalleeParam = f(1, 2, 3); -$(tmpCalleeParam); +$(1); ````` ## PST Output @@ -76,20 +64,7 @@ $(tmpCalleeParam); With rename=true `````js filename=intro -const a = function($$0 ) { - const b = c; - const d = e; - debugger; - const f = d === undefined; - if (f) { - return b; - } - else { - return d; - } -}; -const g = a( 1, 2, 3 ); -$( g ); +$( 1 ); ````` ## Globals diff --git a/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_c-opt_simple_simple.md b/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_c-opt_simple_simple.md index 0316c7ff808..ddb7c7ab513 100644 --- a/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_c-opt_simple_simple.md +++ b/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_c-opt_simple_simple.md @@ -70,9 +70,9 @@ $(a); `````js filename=intro -const tmpForInGen = $forIn(1); +const tmpClusterSSA_tmpForInGen = $forIn(1); while ($LOOP_DONE_UNROLLING_ALWAYS_TRUE) { - const tmpForInNext = tmpForInGen.next(); + const tmpForInNext = tmpClusterSSA_tmpForInGen.next(); const tmpIfTest$1 = tmpForInNext.done; if (tmpIfTest$1) { break; diff --git a/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_extended.md b/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_extended.md index 566fa7d6182..6973ae12c2e 100644 --- a/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_extended.md +++ b/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_extended.md @@ -73,9 +73,9 @@ $(a); `````js filename=intro -const tmpForInGen = $forIn(100); +const tmpClusterSSA_tmpForInGen = $forIn(100); while ($LOOP_DONE_UNROLLING_ALWAYS_TRUE) { - const tmpForInNext = tmpForInGen.next(); + const tmpForInNext = tmpClusterSSA_tmpForInGen.next(); const tmpIfTest$1 = tmpForInNext.done; if (tmpIfTest$1) { break; diff --git a/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_method_call_extended.md b/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_method_call_extended.md index e9429f2554c..f3018c6f28f 100644 --- a/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_method_call_extended.md +++ b/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_method_call_extended.md @@ -75,9 +75,9 @@ $(a); `````js filename=intro const tmpObjLitVal$1 = { e: $ }; const tmpChainElementCall = tmpObjLitVal$1.e(1); -const tmpForInGen = $forIn(tmpChainElementCall); +const tmpClusterSSA_tmpForInGen = $forIn(tmpChainElementCall); while ($LOOP_DONE_UNROLLING_ALWAYS_TRUE) { - const tmpForInNext = tmpForInGen.next(); + const tmpForInNext = tmpClusterSSA_tmpForInGen.next(); const tmpIfTest$1 = tmpForInNext.done; if (tmpIfTest$1) { break; diff --git a/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_method_call_simple.md b/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_method_call_simple.md index f7390936a0a..aa01f380024 100644 --- a/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_method_call_simple.md +++ b/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_method_call_simple.md @@ -71,9 +71,9 @@ $(a); `````js filename=intro const b = { c: $ }; const tmpChainElementCall = b.c(1); -const tmpForInGen = $forIn(tmpChainElementCall); +const tmpClusterSSA_tmpForInGen = $forIn(tmpChainElementCall); while ($LOOP_DONE_UNROLLING_ALWAYS_TRUE) { - const tmpForInNext = tmpForInGen.next(); + const tmpForInNext = tmpClusterSSA_tmpForInGen.next(); const tmpIfTest$1 = tmpForInNext.done; if (tmpIfTest$1) { break; diff --git a/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_s-seq.md b/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_s-seq.md index e78ae8ccfc5..2bde77b6ab1 100644 --- a/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_s-seq.md +++ b/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_s-seq.md @@ -69,9 +69,9 @@ $(a); `````js filename=intro -const tmpForInGen = $forIn(1); +const tmpClusterSSA_tmpForInGen = $forIn(1); while ($LOOP_DONE_UNROLLING_ALWAYS_TRUE) { - const tmpForInNext = tmpForInGen.next(); + const tmpForInNext = tmpClusterSSA_tmpForInGen.next(); const tmpIfTest$1 = tmpForInNext.done; if (tmpIfTest$1) { break; diff --git a/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_simple.md b/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_simple.md index 63b7c2d61e6..6b254124ab5 100644 --- a/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_simple.md +++ b/tests/cases/normalize/expressions/assignments/for_in_right/auto_ident_opt_simple.md @@ -69,9 +69,9 @@ $(a); `````js filename=intro -const tmpForInGen = $forIn(1); +const tmpClusterSSA_tmpForInGen = $forIn(1); while ($LOOP_DONE_UNROLLING_ALWAYS_TRUE) { - const tmpForInNext = tmpForInGen.next(); + const tmpForInNext = tmpClusterSSA_tmpForInGen.next(); const tmpIfTest$1 = tmpForInNext.done; if (tmpIfTest$1) { break; diff --git a/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_c-opt_simple_simple.md b/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_c-opt_simple_simple.md index d8be90edc7a..7247f88e5da 100644 --- a/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_c-opt_simple_simple.md +++ b/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_c-opt_simple_simple.md @@ -70,9 +70,9 @@ $(a); `````js filename=intro -const tmpForOfGen = $forOf(1); +const tmpClusterSSA_tmpForOfGen = $forOf(1); while ($LOOP_DONE_UNROLLING_ALWAYS_TRUE) { - const tmpForOfNext = tmpForOfGen.next(); + const tmpForOfNext = tmpClusterSSA_tmpForOfGen.next(); const tmpIfTest$1 = tmpForOfNext.done; if (tmpIfTest$1) { break; diff --git a/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_extended.md b/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_extended.md index 52dcfb99780..4e9d834439d 100644 --- a/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_extended.md +++ b/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_extended.md @@ -73,9 +73,9 @@ $(a); `````js filename=intro -const tmpForOfGen = $forOf(100); +const tmpClusterSSA_tmpForOfGen = $forOf(100); while ($LOOP_DONE_UNROLLING_ALWAYS_TRUE) { - const tmpForOfNext = tmpForOfGen.next(); + const tmpForOfNext = tmpClusterSSA_tmpForOfGen.next(); const tmpIfTest$1 = tmpForOfNext.done; if (tmpIfTest$1) { break; diff --git a/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_method_call_extended.md b/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_method_call_extended.md index 268ca87662e..a3e41e231e4 100644 --- a/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_method_call_extended.md +++ b/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_method_call_extended.md @@ -75,9 +75,9 @@ $(a); `````js filename=intro const tmpObjLitVal$1 = { e: $ }; const tmpChainElementCall = tmpObjLitVal$1.e(1); -const tmpForOfGen = $forOf(tmpChainElementCall); +const tmpClusterSSA_tmpForOfGen = $forOf(tmpChainElementCall); while ($LOOP_DONE_UNROLLING_ALWAYS_TRUE) { - const tmpForOfNext = tmpForOfGen.next(); + const tmpForOfNext = tmpClusterSSA_tmpForOfGen.next(); const tmpIfTest$1 = tmpForOfNext.done; if (tmpIfTest$1) { break; diff --git a/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_method_call_simple.md b/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_method_call_simple.md index 422213fa9e6..9cfe04598c6 100644 --- a/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_method_call_simple.md +++ b/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_method_call_simple.md @@ -71,9 +71,9 @@ $(a); `````js filename=intro const b = { c: $ }; const tmpChainElementCall = b.c(1); -const tmpForOfGen = $forOf(tmpChainElementCall); +const tmpClusterSSA_tmpForOfGen = $forOf(tmpChainElementCall); while ($LOOP_DONE_UNROLLING_ALWAYS_TRUE) { - const tmpForOfNext = tmpForOfGen.next(); + const tmpForOfNext = tmpClusterSSA_tmpForOfGen.next(); const tmpIfTest$1 = tmpForOfNext.done; if (tmpIfTest$1) { break; diff --git a/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_s-seq.md b/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_s-seq.md index 3e2350bffb8..ed120680e97 100644 --- a/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_s-seq.md +++ b/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_s-seq.md @@ -69,9 +69,9 @@ $(a); `````js filename=intro -const tmpForOfGen = $forOf(1); +const tmpClusterSSA_tmpForOfGen = $forOf(1); while ($LOOP_DONE_UNROLLING_ALWAYS_TRUE) { - const tmpForOfNext = tmpForOfGen.next(); + const tmpForOfNext = tmpClusterSSA_tmpForOfGen.next(); const tmpIfTest$1 = tmpForOfNext.done; if (tmpIfTest$1) { break; diff --git a/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_simple.md b/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_simple.md index 7e25683139e..3cc22dea546 100644 --- a/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_simple.md +++ b/tests/cases/normalize/expressions/assignments/for_of_right/auto_ident_opt_simple.md @@ -69,9 +69,9 @@ $(a); `````js filename=intro -const tmpForOfGen = $forOf(1); +const tmpClusterSSA_tmpForOfGen = $forOf(1); while ($LOOP_DONE_UNROLLING_ALWAYS_TRUE) { - const tmpForOfNext = tmpForOfGen.next(); + const tmpForOfNext = tmpClusterSSA_tmpForOfGen.next(); const tmpIfTest$1 = tmpForOfNext.done; if (tmpIfTest$1) { break; diff --git a/tests/cases/normalize/expressions/assignments/logic_and_both/auto_base_assign_ident.md b/tests/cases/normalize/expressions/assignments/logic_and_both/auto_base_assign_ident.md index 50e6fc9be04..e461299db90 100644 --- a/tests/cases/normalize/expressions/assignments/logic_and_both/auto_base_assign_ident.md +++ b/tests/cases/normalize/expressions/assignments/logic_and_both/auto_base_assign_ident.md @@ -58,7 +58,7 @@ let a = tmpNestedComplexRhs; if (tmpNestedComplexRhs) { b = $(2); const tmpNestedComplexRhs$1 = b; - a = tmpNestedComplexRhs$1; + a = b; $(tmpNestedComplexRhs$1); } else { $(tmpNestedComplexRhs); @@ -77,7 +77,7 @@ let c = a; if (a) { b = $( 2 ); const d = b; - c = d; + c = b; $( d ); } else { diff --git a/tests/cases/normalize/expressions/assignments/logic_and_right/auto_base_assign_ident.md b/tests/cases/normalize/expressions/assignments/logic_and_right/auto_base_assign_ident.md index 04ee225ccd2..b2c2efa415e 100644 --- a/tests/cases/normalize/expressions/assignments/logic_and_right/auto_base_assign_ident.md +++ b/tests/cases/normalize/expressions/assignments/logic_and_right/auto_base_assign_ident.md @@ -55,7 +55,7 @@ const tmpCalleeParam = $(100); if (tmpCalleeParam) { b = $(2); const tmpNestedComplexRhs = b; - a = tmpNestedComplexRhs; + a = b; $(tmpNestedComplexRhs); } else { $(tmpCalleeParam); @@ -77,7 +77,7 @@ const c = $( 100 ); if (c) { a = $( 2 ); const d = a; - b = d; + b = a; $( d ); } else { diff --git a/tests/cases/normalize/expressions/assignments/logic_or_both/auto_base_assign_ident.md b/tests/cases/normalize/expressions/assignments/logic_or_both/auto_base_assign_ident.md index ada11aae3f9..385829fd678 100644 --- a/tests/cases/normalize/expressions/assignments/logic_or_both/auto_base_assign_ident.md +++ b/tests/cases/normalize/expressions/assignments/logic_or_both/auto_base_assign_ident.md @@ -60,7 +60,7 @@ if (tmpNestedComplexRhs) { } else { b = $(2); const tmpNestedComplexRhs$1 = b; - a = tmpNestedComplexRhs$1; + a = b; $(tmpNestedComplexRhs$1); } $(a, b); @@ -80,7 +80,7 @@ if (a) { else { b = $( 2 ); const d = b; - c = d; + c = b; $( d ); } $( c, b ); diff --git a/tests/cases/normalize/expressions/assignments/logic_or_right/auto_base_assign_ident.md b/tests/cases/normalize/expressions/assignments/logic_or_right/auto_base_assign_ident.md index 54e9119faba..c7e42b4d8e6 100644 --- a/tests/cases/normalize/expressions/assignments/logic_or_right/auto_base_assign_ident.md +++ b/tests/cases/normalize/expressions/assignments/logic_or_right/auto_base_assign_ident.md @@ -57,7 +57,7 @@ if (tmpCalleeParam) { } else { b = $(2); const tmpNestedComplexRhs = b; - a = tmpNestedComplexRhs; + a = b; $(tmpNestedComplexRhs); } $(a, b); @@ -80,7 +80,7 @@ if (c) { else { a = $( 2 ); const d = a; - b = d; + b = a; $( d ); } $( b, a ); diff --git a/tests/cases/normalize/expressions/assignments/template/auto_ident_opt_method_call_extended.md b/tests/cases/normalize/expressions/assignments/template/auto_ident_opt_method_call_extended.md index 180141983fb..012b36c13b4 100644 --- a/tests/cases/normalize/expressions/assignments/template/auto_ident_opt_method_call_extended.md +++ b/tests/cases/normalize/expressions/assignments/template/auto_ident_opt_method_call_extended.md @@ -61,8 +61,8 @@ $(a); `````js filename=intro const tmpObjLitVal$1 = { e: $ }; const tmpChainElementCall = tmpObjLitVal$1.e(1); -const tmpBinBothRhs = $coerce(tmpChainElementCall, `string`); -const tmpCalleeParam = `before ${tmpBinBothRhs} after`; +const tmpClusterSSA_tmpBinBothRhs = $coerce(tmpChainElementCall, `string`); +const tmpCalleeParam = `before ${tmpClusterSSA_tmpBinBothRhs} after`; $(tmpCalleeParam); $(tmpChainElementCall); ````` @@ -75,7 +75,7 @@ With rename=true const a = { e: $ }; const b = a.e( 1 ); const c = $coerce( b, "string" ); -const d = `before ${tmpBinBothRhs} after`; +const d = `before ${tmpClusterSSA_tmpBinBothRhs} after`; $( d ); $( b ); ````` diff --git a/tests/cases/normalize/expressions/assignments/template/auto_ident_opt_method_call_simple.md b/tests/cases/normalize/expressions/assignments/template/auto_ident_opt_method_call_simple.md index 7245e7b6e23..083569bbd1a 100644 --- a/tests/cases/normalize/expressions/assignments/template/auto_ident_opt_method_call_simple.md +++ b/tests/cases/normalize/expressions/assignments/template/auto_ident_opt_method_call_simple.md @@ -57,8 +57,8 @@ $(a); `````js filename=intro const b = { c: $ }; const tmpChainElementCall = b.c(1); -const tmpBinBothRhs = $coerce(tmpChainElementCall, `string`); -const tmpCalleeParam = `before ${tmpBinBothRhs} after`; +const tmpClusterSSA_tmpBinBothRhs = $coerce(tmpChainElementCall, `string`); +const tmpCalleeParam = `before ${tmpClusterSSA_tmpBinBothRhs} after`; $(tmpCalleeParam); $(tmpChainElementCall); ````` @@ -71,7 +71,7 @@ With rename=true const a = { c: $ }; const b = a.c( 1 ); const c = $coerce( b, "string" ); -const d = `before ${tmpBinBothRhs} after`; +const d = `before ${tmpClusterSSA_tmpBinBothRhs} after`; $( d ); $( b ); ````` diff --git a/tests/cases/normalize/expressions/assignments/ternary_b/auto_base_assign_ident.md b/tests/cases/normalize/expressions/assignments/ternary_b/auto_base_assign_ident.md index a6b1ef4b869..34045e4e6a6 100644 --- a/tests/cases/normalize/expressions/assignments/ternary_b/auto_base_assign_ident.md +++ b/tests/cases/normalize/expressions/assignments/ternary_b/auto_base_assign_ident.md @@ -57,7 +57,7 @@ const tmpIfTest = $(1); if (tmpIfTest) { b = $(2); const tmpNestedComplexRhs = b; - a = tmpNestedComplexRhs; + a = b; $(tmpNestedComplexRhs); } else { const tmpClusterSSA_tmpCalleeParam = $(200); @@ -80,7 +80,7 @@ const c = $( 1 ); if (c) { a = $( 2 ); const d = a; - b = d; + b = a; $( d ); } else { diff --git a/tests/cases/normalize/expressions/assignments/ternary_c/auto_base_assign_ident.md b/tests/cases/normalize/expressions/assignments/ternary_c/auto_base_assign_ident.md index 12cbed2c06c..835f2682f81 100644 --- a/tests/cases/normalize/expressions/assignments/ternary_c/auto_base_assign_ident.md +++ b/tests/cases/normalize/expressions/assignments/ternary_c/auto_base_assign_ident.md @@ -60,7 +60,7 @@ if (tmpIfTest) { } else { b = $(2); const tmpNestedComplexRhs = b; - a = tmpNestedComplexRhs; + a = b; $(tmpNestedComplexRhs); } $(a, b); @@ -84,7 +84,7 @@ if (c) { else { a = $( 2 ); const e = a; - b = e; + b = a; $( e ); } $( b, a ); diff --git a/tests/cases/static_arg_ops/coerce/assign/closure/param.md b/tests/cases/static_arg_ops/coerce/assign/closure/param.md index d595e3e3e50..464a0a285e8 100644 --- a/tests/cases/static_arg_ops/coerce/assign/closure/param.md +++ b/tests/cases/static_arg_ops/coerce/assign/closure/param.md @@ -60,11 +60,10 @@ $(x); `````js filename=intro -let x = $(`50`); +$(`50`); const f = function ($$0) { const c = $$0; debugger; - x = $coerce(c, `number`); $(1); $(2); $(c); @@ -72,7 +71,7 @@ const f = function ($$0) { }; f(3); f(4); -$(x); +$(4); ````` ## PST Output @@ -80,19 +79,18 @@ $(x); With rename=true `````js filename=intro -let a = $( "50" ); -const b = function($$0 ) { - const c = d; +$( "50" ); +const a = function($$0 ) { + const b = c; debugger; - a = $coerce( c, "number" ); $( 1 ); $( 2 ); - $( c ); + $( b ); return undefined; }; -b( 3 ); -b( 4 ); -$( a ); +a( 3 ); +a( 4 ); +$( 4 ); ````` ## Globals diff --git a/tests/cases/static_arg_ops/coerce/assign/closure/param2.md b/tests/cases/static_arg_ops/coerce/assign/closure/param2.md new file mode 100644 index 00000000000..7edb9c3c835 --- /dev/null +++ b/tests/cases/static_arg_ops/coerce/assign/closure/param2.md @@ -0,0 +1,150 @@ +# Preval test case + +# param2.md + +> Static arg ops > Coerce > Assign > Closure > Param2 + +## Input + +`````js filename=intro +let x = $({ + valueOf:function(){ $('PASS'); } +}); +Number(x); // This should trigger a pass +const f = function (c) { + // This $coerce gets eliminated because f is only called with numbers as first arg, so this coerce is a noop + x = $coerce(c, 'number'); + $(1); + $(2); + $(c); +}; +f(3); +f(4); +$(x); // This is $, not f +````` + +## Pre Normal + + +`````js filename=intro +let x = $({ + valueOf: function () { + debugger; + $(`PASS`); + }, +}); +Number(x); +const f = function ($$0) { + let c = $$0; + debugger; + x = $coerce(c, `number`); + $(1); + $(2); + $(c); +}; +f(3); +f(4); +$(x); +````` + +## Normalized + + +`````js filename=intro +const tmpCallCallee = $; +const tmpObjLitVal = function () { + debugger; + $(`PASS`); + return undefined; +}; +const tmpCalleeParam = { valueOf: tmpObjLitVal }; +let x = tmpCallCallee(tmpCalleeParam); +$coerce(x, `number`); +const f = function ($$0) { + let c = $$0; + debugger; + x = $coerce(c, `number`); + $(1); + $(2); + $(c); + return undefined; +}; +f(3); +f(4); +$(x); +````` + +## Output + + +`````js filename=intro +const tmpObjLitVal = function () { + debugger; + $(`PASS`); + return undefined; +}; +const tmpCalleeParam = { valueOf: tmpObjLitVal }; +const x = $(tmpCalleeParam); +$coerce(x, `number`); +const f = function ($$0) { + const c = $$0; + debugger; + $(1); + $(2); + $(c); + return undefined; +}; +f(3); +f(4); +$(4); +````` + +## PST Output + +With rename=true + +`````js filename=intro +const a = function() { + debugger; + $( "PASS" ); + return undefined; +}; +const b = { valueOf: a }; +const c = $( b ); +$coerce( c, "number" ); +const d = function($$0 ) { + const e = f; + debugger; + $( 1 ); + $( 2 ); + $( e ); + return undefined; +}; +d( 3 ); +d( 4 ); +$( 4 ); +````` + +## Globals + +None + +## Result + +Should call `$` with: + - 1: { valueOf: '""' } + - 2: 'PASS' + - 3: 1 + - 4: 2 + - 5: 3 + - 6: 1 + - 7: 2 + - 8: 4 + - 9: 4 + - eval returned: undefined + +Pre normalization calls: Same + +Normalized calls: Same + +Final output calls: Same diff --git a/tests/cases/static_arg_ops/coerce/assign/closure/param3.md b/tests/cases/static_arg_ops/coerce/assign/closure/param3.md new file mode 100644 index 00000000000..7358e783af6 --- /dev/null +++ b/tests/cases/static_arg_ops/coerce/assign/closure/param3.md @@ -0,0 +1,153 @@ +# Preval test case + +# param3.md + +> Static arg ops > Coerce > Assign > Closure > Param3 + +## Input + +`````js filename=intro +let x = $({ + valueOf:function(){ $('PASS'); } +}); +Number(x); // This should trigger a pass +const f = function (c) { + x = $coerce(c, 'number'); // This should trigger a pass if c===x. But c will never be x here. + $(1); + $(2); + $(c); +}; +f(3); +f(4); +f(x); // At this point, x is set to 3, the first call to f() above, so no PASS output +````` + +## Pre Normal + + +`````js filename=intro +let x = $({ + valueOf: function () { + debugger; + $(`PASS`); + }, +}); +Number(x); +const f = function ($$0) { + let c = $$0; + debugger; + x = $coerce(c, `number`); + $(1); + $(2); + $(c); +}; +f(3); +f(4); +f(x); +````` + +## Normalized + + +`````js filename=intro +const tmpCallCallee = $; +const tmpObjLitVal = function () { + debugger; + $(`PASS`); + return undefined; +}; +const tmpCalleeParam = { valueOf: tmpObjLitVal }; +let x = tmpCallCallee(tmpCalleeParam); +$coerce(x, `number`); +const f = function ($$0) { + let c = $$0; + debugger; + x = $coerce(c, `number`); + $(1); + $(2); + $(c); + return undefined; +}; +f(3); +f(4); +f(x); +````` + +## Output + + +`````js filename=intro +const tmpObjLitVal = function () { + debugger; + $(`PASS`); + return undefined; +}; +const tmpCalleeParam = { valueOf: tmpObjLitVal }; +let x = $(tmpCalleeParam); +$coerce(x, `number`); +const f = function ($$0) { + const c = $$0; + debugger; + x = $coerce(c, `number`); + $(1); + $(2); + $(c); + return undefined; +}; +f(3); +f(4); +f(x); +````` + +## PST Output + +With rename=true + +`````js filename=intro +const a = function() { + debugger; + $( "PASS" ); + return undefined; +}; +const b = { valueOf: a }; +let c = $( b ); +$coerce( c, "number" ); +const d = function($$0 ) { + const e = f; + debugger; + c = $coerce( e, "number" ); + $( 1 ); + $( 2 ); + $( e ); + return undefined; +}; +d( 3 ); +d( 4 ); +d( c ); +````` + +## Globals + +None + +## Result + +Should call `$` with: + - 1: { valueOf: '""' } + - 2: 'PASS' + - 3: 1 + - 4: 2 + - 5: 3 + - 6: 1 + - 7: 2 + - 8: 4 + - 9: 1 + - 10: 2 + - 11: 4 + - eval returned: undefined + +Pre normalization calls: Same + +Normalized calls: Same + +Final output calls: Same diff --git a/tests/cases/tofix/sub_alias.md b/tests/cases/tofix/sub_alias.md new file mode 100644 index 00000000000..3a5b601aa8b --- /dev/null +++ b/tests/cases/tofix/sub_alias.md @@ -0,0 +1,120 @@ +# Preval test case + +# sub_alias.md + +> Tofix > Sub alias +> +> Normalization of assignments should work the same everywhere they are + +## Input + +`````js filename=intro +let b = 1; +let a = { a: 999, b: 1000 }; +const tmp = $(100); +if (tmp) { + b = $(2); + const alias = b; // This is a redundant alias that we can eliminate + a = b; + $(alias); +} else { + $(tmp); +} +$(a, b); +````` + +## Pre Normal + + +`````js filename=intro +let b = 1; +let a = { a: 999, b: 1000 }; +const tmp = $(100); +if (tmp) { + b = $(2); + const alias = b; + a = b; + $(alias); +} else { + $(tmp); +} +$(a, b); +````` + +## Normalized + + +`````js filename=intro +let b = 1; +let a = { a: 999, b: 1000 }; +const tmp = $(100); +if (tmp) { + b = $(2); + const alias = b; + a = b; + $(alias); +} else { + $(tmp); +} +$(a, b); +````` + +## Output + + +`````js filename=intro +let b = 1; +let a = { a: 999, b: 1000 }; +const tmp = $(100); +if (tmp) { + b = $(2); + const alias = b; + a = b; + $(alias); +} else { + $(tmp); +} +$(a, b); +````` + +## PST Output + +With rename=true + +`````js filename=intro +let a = 1; +let b = { + a: 999, + b: 1000, +}; +const c = $( 100 ); +if (c) { + a = $( 2 ); + const d = a; + b = a; + $( d ); +} +else { + $( c ); +} +$( b, a ); +````` + +## Globals + +None + +## Result + +Should call `$` with: + - 1: 100 + - 2: 2 + - 3: 2 + - 4: 2, 2 + - eval returned: undefined + +Pre normalization calls: Same + +Normalized calls: Same + +Final output calls: Same diff --git a/tests/cases/type_tracked/invert/else_branch_closure2.md b/tests/cases/type_tracked/invert/else_branch_closure2.md index 4d8346cfb73..263cf5902d9 100644 --- a/tests/cases/type_tracked/invert/else_branch_closure2.md +++ b/tests/cases/type_tracked/invert/else_branch_closure2.md @@ -95,11 +95,10 @@ const f = function ($$0) { const z = $$0; debugger; $(`keepme`); - const tmpReturnArg = [x, z]; + const tmpReturnArg = [y, z]; return tmpReturnArg; }; - const x = $coerce(y, `plustr`); - if (x) { + if (y) { return undefined; } else { const tmpCalleeParam = g(10); @@ -125,18 +124,17 @@ const a = function($$0 ) { const e = c; debugger; $( "keepme" ); - const f = [ g, e ]; + const f = [ b, e ]; return f; }; - const g = $coerce( b, "plustr" ); - if (g) { + if (b) { return undefined; } else { - const h = d( 10 ); + const g = d( 10 ); + $( g, "pass" ); + const h = d( 20 ); $( h, "pass" ); - const i = d( 20 ); - $( i, "pass" ); return undefined; } };