Skip to content

Commit

Permalink
feat: enhance the treehshaking for unary expression (#5775)
Browse files Browse the repository at this point in the history
* enhance the treehshaking for unary expression

* simplify the number string

* without considering BigInt

* retain boolean and undefined && slightly improve performance
  • Loading branch information
TrickyPi authored Jan 6, 2025
1 parent 27216d8 commit d3e2bf7
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 47 deletions.
78 changes: 76 additions & 2 deletions src/ast/nodes/UnaryExpression.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import type MagicString from 'magic-string';
import type { RenderOptions } from '../../utils/renderHelpers';
import type { DeoptimizableEntity } from '../DeoptimizableEntity';
import type { HasEffectsContext } from '../ExecutionContext';
import type { HasEffectsContext, InclusionContext } from '../ExecutionContext';
import type { NodeInteraction } from '../NodeInteractions';
import { INTERACTION_ACCESSED, NODE_INTERACTION_UNKNOWN_ASSIGNMENT } from '../NodeInteractions';
import { EMPTY_PATH, type ObjectPath, type PathTracker } from '../utils/PathTracker';
import {
EMPTY_PATH,
type ObjectPath,
type PathTracker,
SHARED_RECURSION_TRACKER
} from '../utils/PathTracker';
import Identifier from './Identifier';
import type { LiteralValue } from './Literal';
import type * as NodeType from './NodeType';
import { Flag, isFlagSet, setFlag } from './shared/BitFlags';
import type { InclusionOptions } from './shared/Expression';
import { type LiteralValueOrUnknown, UnknownValue } from './shared/Expression';
import type { IncludeChildren } from './shared/Node';
import { type ExpressionNode, NodeBase } from './shared/Node';

const unaryOperators: Record<string, (value: LiteralValue) => LiteralValueOrUnknown> = {
Expand All @@ -20,10 +29,13 @@ const unaryOperators: Record<string, (value: LiteralValue) => LiteralValueOrUnkn
'~': value => ~(value as NonNullable<LiteralValue>)
};

const UNASSIGNED = Symbol('Unassigned');

export default class UnaryExpression extends NodeBase {
declare argument: ExpressionNode;
declare operator: '!' | '+' | '-' | 'delete' | 'typeof' | 'void' | '~';
declare type: NodeType.tUnaryExpression;
renderedLiteralValue: string | typeof UnknownValue | typeof UNASSIGNED = UNASSIGNED;

get prefix(): boolean {
return isFlagSet(this.flags, Flag.prefix);
Expand All @@ -32,6 +44,10 @@ export default class UnaryExpression extends NodeBase {
this.flags = setFlag(this.flags, Flag.prefix, value);
}

deoptimizeCache(): void {
this.renderedLiteralValue = UnknownValue;
}

getLiteralValueAtPath(
path: ObjectPath,
recursionTracker: PathTracker,
Expand Down Expand Up @@ -69,4 +85,62 @@ export default class UnaryExpression extends NodeBase {
this.scope.context.requestTreeshakingPass();
}
}

getRenderedLiteralValue(includeChildrenRecursively: IncludeChildren) {
if (this.renderedLiteralValue !== UNASSIGNED) return this.renderedLiteralValue;
return (this.renderedLiteralValue = includeChildrenRecursively
? UnknownValue
: getSimplifiedLiterals(
this.getLiteralValueAtPath(EMPTY_PATH, SHARED_RECURSION_TRACKER, this)
));
}

include(
context: InclusionContext,
includeChildrenRecursively: IncludeChildren,
_options?: InclusionOptions
): void {
if (!this.deoptimized) this.applyDeoptimizations();
this.included = true;
if (
typeof this.getRenderedLiteralValue(includeChildrenRecursively) === 'symbol' ||
this.argument.shouldBeIncluded(context)
) {
this.argument.include(context, includeChildrenRecursively);
this.renderedLiteralValue = UnknownValue;
}
}

render(code: MagicString, options: RenderOptions) {
if (typeof this.renderedLiteralValue === 'symbol') {
super.render(code, options);
} else {
code.overwrite(this.start, this.end, this.renderedLiteralValue);
}
}
}

function getSimplifiedLiterals(value: unknown) {
if (value === undefined || typeof value === 'boolean') {
return String(value);
}
if (typeof value === 'string') {
return JSON.stringify(value);
}
if (typeof value === 'number') {
return getSimplifiedNumber(value);
}
return UnknownValue;
}

function getSimplifiedNumber(value: number) {
if (Object.is(-0, value)) {
return '-0';
}
const exp = value.toExponential();
const [base, exponent] = exp.split('e');
const floatLength = base.split('.')[1]?.length || 0;
const finalizedExp = `${base.replace('.', '')}e${parseInt(exponent) - floatLength}`;
const stringifiedValue = String(value).replace('+', '');
return finalizedExp.length < stringifiedValue.length ? finalizedExp : stringifiedValue;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ define(['exports'], (function (exports) { 'use strict';

console.log('This is the output when a missing export is reexported');

var _missingExportShim$1 = void 0;
var _missingExportShim$1 = undefined;

console.log(_missingExportShim$1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var _missingExportShim = void 0;

console.log('This is the output when a missing export is reexported');

var _missingExportShim$1 = void 0;
var _missingExportShim$1 = undefined;

console.log(_missingExportShim$1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ var _missingExportShim = void 0;

console.log('This is the output when a missing export is reexported');

var _missingExportShim$1 = void 0;
var _missingExportShim$1 = undefined;

console.log(_missingExportShim$1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ System.register([], (function (exports) {

console.log('This is the output when a missing export is reexported');

var _missingExportShim$1 = exports("previousShimmedExport", void 0);
var _missingExportShim$1 = exports("previousShimmedExport", undefined);

console.log(_missingExportShim$1);

Expand Down
4 changes: 2 additions & 2 deletions test/form/samples/supports-core-js/_expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -9133,7 +9133,7 @@ function requireEs_number_minSafeInteger () {
// `Number.MIN_SAFE_INTEGER` constant
// https://tc39.es/ecma262/#sec-number.min_safe_integer
$({ target: 'Number', stat: true, nonConfigurable: true, nonWritable: true }, {
MIN_SAFE_INTEGER: -0x1FFFFFFFFFFFFF
MIN_SAFE_INTEGER: -9007199254740991
});
return es_number_minSafeInteger;
}
Expand Down Expand Up @@ -9265,7 +9265,7 @@ function requireEs_number_toExponential () {
var stringSlice = uncurryThis(''.slice);

// Edge 17-
var ROUNDS_PROPERLY = nativeToExponential(-6.9e-11, 4) === '-6.9000e-11'
var ROUNDS_PROPERLY = nativeToExponential(-69e-12, 4) === '-6.9000e-11'
// IE11- && Edge 14-
&& nativeToExponential(1.255, 2) === '1.25e+0'
// FF86-, V8 ~ Chrome 49-50
Expand Down
10 changes: 5 additions & 5 deletions test/form/samples/supports-es5-shim/_expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ function requireEs5Shim () {
var n = +num;
if (isActualNaN(n)) {
n = 0;
} else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
} else if (n !== 0 && n !== (1 / 0) && n !== -Infinity) {
n = (n > 0 || -1) * floor(abs(n));
}
return n;
Expand Down Expand Up @@ -1384,7 +1384,7 @@ function requireEs5Shim () {
// string format defined in 15.9.1.15. All fields are present in the String.
// The time zone is always UTC, denoted by the suffix Z. If the time value of
// this object is not a finite Number a RangeError exception is thrown.
var negativeDate = -62198755200000;
var negativeDate = -621987552e5;
var negativeYearString = '-000001';
var hasNegativeDateBug = Date.prototype.toISOString && new Date(negativeDate).toISOString().indexOf(negativeYearString) === -1; // eslint-disable-line max-len
var hasSafari51DateBug = Date.prototype.toISOString && new Date(-1).toISOString() !== '1969-12-31T23:59:59.999Z';
Expand Down Expand Up @@ -1642,7 +1642,7 @@ function requireEs5Shim () {
if (isLocalTime) {
result = toUTC(result);
}
if (-8.64e15 <= result && result <= 8.64e15) {
if (-864e13 <= result && result <= 8.64e15) {
return result;
}
}
Expand Down Expand Up @@ -2043,7 +2043,7 @@ function requireEs5Shim () {
match[0].replace(separator2, function () {
for (var i = 1; i < arguments.length - 2; i++) {
if (typeof arguments[i] === 'undefined') {
match[i] = void 0;
match[i] = undefined;
}
}
});
Expand Down Expand Up @@ -2080,7 +2080,7 @@ function requireEs5Shim () {
// then the output array is truncated so that it contains no more than limit
// elements.
// "0".split(undefined, 0) -> []
} else if ('0'.split(void 0, 0).length) {
} else if ('0'.split(undefined, 0).length) {
StringPrototype.split = function split(separator, limit) {
if (typeof separator === 'undefined' && limit === 0) {
return [];
Expand Down
Loading

0 comments on commit d3e2bf7

Please sign in to comment.