Skip to content

Commit

Permalink
Add known globals (#5072)
Browse files Browse the repository at this point in the history
* Add known globals

* change Object.values/Object.entries to O from PF

* tree shake Object.entries/values
  • Loading branch information
sapphi-red committed Jul 28, 2023
1 parent 1f2f0ce commit 80747e3
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 5 deletions.
44 changes: 39 additions & 5 deletions src/ast/nodes/shared/knownGlobals.ts
Expand Up @@ -3,7 +3,10 @@
import { doNothing } from '../../../utils/doNothing';
import type { HasEffectsContext } from '../../ExecutionContext';
import type { NodeInteractionCalled } from '../../NodeInteractions';
import { NODE_INTERACTION_UNKNOWN_ASSIGNMENT } from '../../NodeInteractions';
import {
NODE_INTERACTION_UNKNOWN_ACCESS,
NODE_INTERACTION_UNKNOWN_ASSIGNMENT
} from '../../NodeInteractions';
import type { ObjectPath } from '../../utils/PathTracker';
import {
SymbolToStringTag,
Expand All @@ -12,7 +15,7 @@ import {
} from '../../utils/PathTracker';
import ArrayExpression from '../ArrayExpression';
import type { LiteralValueOrUnknown } from './Expression';
import { UnknownTruthyValue } from './Expression';
import { ExpressionEntity, UnknownTruthyValue } from './Expression';

const ValueProperties = Symbol('Value Properties');

Expand Down Expand Up @@ -52,6 +55,22 @@ const PURE_WITH_ARRAY: ValueDescription = {
}
};

const GETTER_ACCESS: ValueDescription = {
deoptimizeArgumentsOnCall: doNothing,
getLiteralValue: getTruthyLiteralValue,
hasEffectsWhenCalled({ args }, context) {
const [_thisArgument, firstArgument] = args;
return (
!(firstArgument instanceof ExpressionEntity) ||
firstArgument.hasEffectsOnInteractionAtPath(
UNKNOWN_PATH,
NODE_INTERACTION_UNKNOWN_ACCESS,
context
)
);
}
};

// We use shortened variables to reduce file size here
/* OBJECT */
const O: GlobalDescription = {
Expand All @@ -65,6 +84,12 @@ const PF: GlobalDescription = {
[ValueProperties]: PURE
};

/* PURE FUNCTION IF FIRST ARG DOES NOT CONTAIN A GETTER */
const PF_NO_GETTER: GlobalDescription = {
__proto__: null,
[ValueProperties]: GETTER_ACCESS
};

/* FUNCTION THAT MUTATES FIRST ARG WITHOUT TRIGGERING ACCESSORS */
const MUTATES_ARG_WITHOUT_ACCESSOR: GlobalDescription = {
__proto__: null,
Expand Down Expand Up @@ -253,7 +278,8 @@ const knownGlobals: GlobalDescription = {
isSealed: PF,
keys: PF,
fromEntries: O,
entries: PF,
entries: PF_NO_GETTER,
values: PF_NO_GETTER,
prototype: O
},
parseFloat: PF,
Expand Down Expand Up @@ -350,16 +376,24 @@ const knownGlobals: GlobalDescription = {
[ValueProperties]: IMPURE,
Collator: INTL_MEMBER,
DateTimeFormat: INTL_MEMBER,
DisplayNames: INTL_MEMBER,
ListFormat: INTL_MEMBER,
Locale: INTL_MEMBER,
NumberFormat: INTL_MEMBER,
PluralRules: INTL_MEMBER,
RelativeTimeFormat: INTL_MEMBER
RelativeTimeFormat: INTL_MEMBER,
Segmenter: INTL_MEMBER
},
setInterval: C,
setTimeout: C,
TextDecoder: C,
TextEncoder: C,
URL: C,
URL: {
__proto__: null,
[ValueProperties]: IMPURE,
prototype: O,
canParse: PF
},
URLSearchParams: C,

// Browser specific globals
Expand Down
@@ -0,0 +1,3 @@
module.exports = {
description: 'retain functions that accept a object with a getter that has side effects'
};
@@ -0,0 +1,13 @@
let effects = 0;

const objWithEffect = {
get foo() {
effects++;
return 'foo';
}
};

Object.values(objWithEffect);
Object.entries(objWithEffect);

assert.equal(effects, 2);
@@ -0,0 +1,27 @@
let effects = 0;

const objWithEffect = {
get foo() {
effects++;
return 'foo';
}
};

const objWithoutEffect1 = {
get foo() {
return 'foo';
}
};
const objWithoutEffect2 = {
foo: 'foo'
};

Object.values(objWithEffect);
Object.entries(objWithEffect);

Object.values(objWithoutEffect1);
Object.entries(objWithoutEffect1);
Object.values(objWithoutEffect2);
Object.entries(objWithoutEffect2);

assert.equal(effects, 2);

0 comments on commit 80747e3

Please sign in to comment.