All files / src/compiler/phases/3-transform/client/visitors UpdateExpression.js

94.44% Statements 68/72
89.28% Branches 25/28
100% Functions 1/1
94.2% Lines 65/69

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 702x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 191x 191x 191x 191x 191x 191x 1x 191x 1x 1x 1x 1x 1x 1x     1x 1x 1x 190x 191x     190x 190x 191x 188x 191x 142x 142x 142x 142x 125x 125x 142x 63x 63x 63x 63x 191x 191x 191x 191x 191x 191x 191x 191x 191x 191x 191x 191x 191x 191x 191x 191x 191x 191x 191x  
/** @import { Expression, Node, Pattern, Statement, UpdateExpression } from 'estree' */
/** @import { Context } from '../types' */
import { is_ignored } from '../../../../state.js';
import { object } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js';
 
/**
 * @param {UpdateExpression} node
 * @param {Context} context
 */
export function UpdateExpression(node, context) {
	const argument = node.argument;
 
	if (
		argument.type === 'MemberExpression' &&
		argument.object.type === 'ThisExpression' &&
		argument.property.type === 'PrivateIdentifier' &&
		context.state.private_state.has(argument.property.name)
	) {
		let fn = '$.update';
		if (node.prefix) fn += '_pre';
 
		/** @type {Expression[]} */
		const args = [argument];
		if (node.operator === '--') {
			args.push(b.literal(-1));
		}
 
		return b.call(fn, ...args);
	}
 
	if (argument.type !== 'Identifier' && argument.type !== 'MemberExpression') {
		throw new Error('An impossible state was reached');
	}
 
	const left = object(argument);
	if (left === null) return context.next();
 
	if (left === argument) {
		const transform = context.state.transform;
		const update = transform[left.name]?.update;
 
		if (update && Object.hasOwn(transform, left.name)) {
			return update(node);
		}
	}
 
	const assignment = /** @type {Expression} */ (
		context.visit(
			b.assignment(
				node.operator === '++' ? '+=' : '-=',
				/** @type {Pattern} */ (argument),
				b.literal(1)
			)
		)
	);
 
	const parent = /** @type {Node} */ (context.path.at(-1));
	const is_standalone = parent.type === 'ExpressionStatement'; // TODO and possibly others, but not e.g. the `test` of a WhileStatement
 
	const update =
		node.prefix || is_standalone
			? assignment
			: b.binary(node.operator === '++' ? '-' : '+', assignment, b.literal(1));
 
	return is_ignored(node, 'ownership_invalid_mutation')
		? b.call('$.skip_ownership_validation', b.thunk(update))
		: update;
}