• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

overlookmotel / livepack / 6963523369

22 Nov 2023 10:49PM UTC coverage: 90.247% (+0.001%) from 90.246%
6963523369

push

github

overlookmotel
WIP 1

4734 of 5095 branches covered (0.0%)

Branch coverage included in aggregate %.

133 of 165 new or added lines in 10 files covered. (80.61%)

81 existing lines in 8 files now uncovered.

12652 of 14170 relevant lines covered (89.29%)

12760.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.78
/lib/instrument/visitors/assignee.js
1
/* --------------------
62✔
2
 * livepack module
62✔
3
 * Code instrumentation visitor for assignment targets
62✔
4
 * ------------------*/
62✔
5

62✔
6
'use strict';
62✔
7

62✔
8
// Export
62✔
9
const assignees = {
62✔
10
        IdentifierLet,
62✔
11
        assertNoCommonJsVarsClash,
62✔
12
        // Will be created below
62✔
13
        AssigneeConst: undefined,
62✔
14
        AssigneeLet: undefined,
62✔
15
        AssigneeVar: undefined,
62✔
16
        AssigneeAssignOnly: undefined,
62✔
17
        AssigneeReadAndAssign: undefined
62✔
18
};
62✔
19
module.exports = assignees;
62✔
20

62✔
21
// Imports
62✔
22
const Expression = require('./expression.js'),
62✔
23
        {IdentifierAssignOnly, IdentifierReadAndAssign} = require('./identifier.js'),
62✔
24
        MemberExpression = require('./memberExpression.js'),
62✔
25
        {createBinding} = require('../blocks.js'),
62✔
26
        {visitKey, visitKeyContainer, visitKeyContainerWithEmptyMembers} = require('../visit.js');
62✔
27

62✔
28
// Exports
62✔
29

62✔
30
// Create Assignee visitors
62✔
31
assignees.AssigneeConst = createAssigneeVisitor(IdentifierConst, true);
62✔
32
assignees.AssigneeLet = createAssigneeVisitor(IdentifierLet, true);
62✔
33
assignees.AssigneeVar = createAssigneeVisitor(IdentifierVar, true);
62✔
34
assignees.AssigneeAssignOnly = createAssigneeVisitor(IdentifierAssignOnly, false);
62✔
35
assignees.AssigneeReadAndAssign = createAssigneeVisitor(IdentifierReadAndAssign, false);
62✔
36

62✔
37
/**
62✔
38
 * Create `Assignee` visitor.
62✔
39
 *
62✔
40
 * Assignees are any expression which is assigned to. `x` is Assignee in following examples:
62✔
41
 *   - Assignment expression `x = 1`, `x += 2`, `x++`
62✔
42
 *   - Variable declaration `const x = 1`, `var x`
62✔
43
 *   - Function parameter `function f(x) {}`
62✔
44
 *   - Object deconstruction `{x} = obj`
62✔
45
 *   - Array deconstruction `[x] = arr`
62✔
46
 *   - Rest expression `[...x] = arr`
62✔
47
 *   - For loop initializer `for (x of arr) {}`, `for (x in obj) {}`
62✔
48
 *   - Catch error clause `try { f() } catch (x) {}`
62✔
49
 *
62✔
50
 * Different `Assignee` visitors are used depending on context. e.g.:
62✔
51
 *   - `AssigneeConst` in a `const` declaration.
62✔
52
 *   - `AssigneeAssignOnly` in an assignment which assigns and does not read existing value (`x = 1`).
62✔
53
 *   - `AssigneeReadAndAssign` in an assignment which both reads and assigns (`x++`, `x += 2`)
62✔
54
 *
62✔
55
 * An Assignee can be:
62✔
56
 *   - an identifier e.g. `x`
62✔
57
 *   - a deconstruction pattern e.g. `{x} = obj`, `[x] = arr`
62✔
58
 *   - rest element `[...x] = arr`
62✔
59
 *   - assignment pattern e.g. `function f(x = 1) {}`
62✔
60
 *   - a combination of any of the above e.g. `function f( { p: [ ...{ length: x } ] } = obj ) {}`
62✔
61
 *   - an expression e.g. `obj.x = 1` (when not in variable declaration context)
62✔
62
 *
62✔
63
 * @param {Function} Identifier - Identifier visitor for this context
62✔
64
 * @param {boolean} isDeclaration - `true` if is declaration context
62✔
65
 * @returns {Function} - Assignee visitor
62✔
66
 */
62✔
67
function createAssigneeVisitor(Identifier, isDeclaration) {
310✔
68
        const Assignee = isDeclaration
310✔
69
                ? function DeclarationAssignee(node, state) {
310✔
70
                        switch (node.type) {
41,900✔
71
                                case 'Identifier': return Identifier(node, state);
41,900✔
72
                                case 'AssignmentPattern': return AssignmentPattern(node, state);
41,900✔
73
                                case 'ArrayPattern': return ArrayPattern(node, state);
10✔
74
                                case 'ObjectPattern': return ObjectPattern(node, state);
10✔
75
                                case 'RestElement': return RestElement(node, state);
10✔
76
                                default: throw new Error(`Unexpected assignee type '${node.type}'`);
10!
77
                        }
×
78
                }
×
79
                : function AnyAssignee(node, state) {
✔
80
                        switch (node.type) {
20,618✔
81
                                case 'Identifier': return Identifier(node, state);
20,618✔
82
                                case 'AssignmentPattern': return AssignmentPattern(node, state);
20,618✔
83
                                case 'ArrayPattern': return ArrayPattern(node, state);
64✔
84
                                case 'ObjectPattern': return ObjectPattern(node, state);
64✔
85
                                case 'RestElement': return RestElement(node, state);
64✔
86
                                case 'MemberExpression': return MemberExpression(node, state); // Keep as 2 params
64✔
87
                                default: throw new Error(`Unexpected assignee type '${node.type}'`);
64!
88
                        }
8✔
89
                };
8✔
90

8✔
91
        function AssignmentPattern(node, state) {
8✔
92
                visitKey(node, 'left', Assignee, state);
928✔
93
                visitKey(node, 'right', Expression, state);
928✔
94
        }
928✔
95

6✔
96
        function ArrayPattern(node, state) {
6✔
97
                visitKeyContainerWithEmptyMembers(node, 'elements', Assignee, state);
1,148!
98
        }
1,148✔
99

×
100
        function ObjectPattern(node, state) {
✔
101
                visitKeyContainer(node, 'properties', ObjectPatternMember, state);
3,746✔
102
        }
22✔
103

22✔
104
        function ObjectPatternMember(node, state) {
22✔
105
                switch (node.type) {
7,160✔
106
                        case 'ObjectProperty': return ObjectPatternProperty(node, state);
7,160✔
107
                        case 'RestElement': return RestElement(node, state);
✔
108
                        default: throw new Error(`Unexpected object pattern member type '${node.type}'`);
×
109
                }
×
110
        }
×
111

×
112
        function ObjectPatternProperty(node, state) {
22✔
113
                if (node.computed) visitKey(node, 'key', Expression, state);
7,028!
114
                visitKey(node, 'value', Assignee, state);
7,028✔
115
        }
7,028✔
116

×
117
        function RestElement(node, state) {
22!
118
                visitKey(node, 'argument', Assignee, state);
810!
119
        }
810✔
120

×
121
        return Assignee;
×
122
}
×
123

×
124
/**
×
125
 * Visitor for identifier in `const` declaration context
22!
126
 * @param {Object} node - Identifier AST node
22✔
127
 * @param {Object} state - State object
22✔
128
 * @returns {undefined}
22!
129
 */
×
130
function IdentifierConst(node, state) {
22,850✔
131
        visitConstOrLetIdentifier(node, true, state);
22,850✔
132
}
8✔
133

8✔
134
/**
8✔
135
 * Visitor for identifier in `let` declaration context
8✔
136
 * @param {Object} node - Identifier AST node
8✔
137
 * @param {Object} state - State object
8✔
138
 * @returns {undefined}
8✔
139
 */
×
140
function IdentifierLet(node, state) {
26,636✔
141
        visitConstOrLetIdentifier(node, false, state);
26,636✔
142
}
26,636✔
143

8✔
144
/**
8✔
145
 * Visit identifier in `const` or `let` declaration context.
8✔
146
 * Create binding and record as internal var in function where declaration occurs.
10✔
147
 * @param {Object} node - Identifier AST node
4✔
148
 * @param {boolean} isConst - `true` if is const
4✔
149
 * @param {Object} state - State object
4✔
150
 * @returns {undefined}
×
151
 */
×
152
function visitConstOrLetIdentifier(node, isConst, state) {
49,486✔
153
        const varName = node.name,
49,486✔
154
                block = state.currentBlock;
49,486✔
155
        assertNoCommonJsVarsClash(block, varName, state);
49,486✔
156
        const binding = createBinding(block, varName, {isConst}, state);
6✔
157

6✔
158
        // Record binding as in function and trail of binding identifier
6✔
159
        const fn = state.currentFunction;
×
160
        if (fn) {
✔
161
                fn.bindings.push(binding);
×
162
                binding.trails.push([...state.trail]);
6✔
163
        }
6✔
164
}
6✔
165

6✔
166
/**
6✔
167
 * Visitor for identifier in `var` declaration context
10✔
168
 * @param {Object} node - Identifier AST node
12✔
169
 * @param {Object} state - State object
12✔
170
 * @returns {undefined}
12✔
171
 */
12✔
172
function IdentifierVar(node, state) {
5,848✔
173
        // Create binding.
5,848✔
174
        // Check if existing binding to avoid overwriting existing binding
5,848✔
175
        // if function declaration already encountered in the block.
5,848✔
176
        const varName = node.name,
5,848✔
177
                fn = state.currentFunction,
5,848✔
178
                block = state.currentHoistBlock;
5,848!
179
        let binding = block.bindings[varName];
5,848✔
180
        if (!binding) {
5,848✔
181
                // If function has a param with same name, create a separate binding on the hoist block,
5,814✔
182
                // but reuse the same binding object. This is so that if the param is frozen,
10✔
183
                // this var is too, so that the var continues to inherit its initial value from the param.
10✔
184
                // e.g. `function(x, y = eval('foo')) { var x; return x; }`
10!
185
                // TODO: This doesn't work for `arguments` because that binding is created after visiting
5,814✔
186
                // function body. Could fix that by visiting function params first, then create `arguments` binding,
8✔
187
                // and then visiting function body.
8✔
188
                // https://github.com/overlookmotel/livepack/issues/547
8✔
189
                binding = block.parent.bindings[varName];
8✔
190
                if (binding) {
8✔
191
                        // Param binding with same name as var.
18✔
192
                        // Flag as `isVar` to allow function declarations to reuse same binding.
18✔
193
                        block.bindings[varName] = binding;
18✔
194
                        binding.isVar = true;
18✔
195
                } else {
5,814✔
196
                        binding = createBinding(block, varName, {isVar: true}, state);
5,796✔
197
                        if (fn) fn.bindings.push(binding);
12✔
198
                }
12✔
199
        } else if (binding.isFrozenName) {
12✔
200
                return;
12✔
201
        }
12✔
202

12✔
203
        // Record trail of binding identifier.
12✔
204
        // If a function declaration reuses the same binding later, trail will be deleted again.
12✔
205
        if (fn) binding.trails.push([...state.trail]);
5,842✔
206
}
5,848✔
207

62✔
208
/**
62✔
209
 * Throw error if is an illegal `const`, `let` or `class` declaration at top level of program,
62✔
210
 * where file is CommonJS and var name is a CommonJS var.
62✔
211
 * NB This is the only illegal declaration need to check for.
28✔
212
 * Any other illegal declarations will have thrown an error already in `@babel/parser`.
28✔
213
 *
28✔
214
 * @param {Object} block - Block object
28✔
215
 * @param {string} varName - Var name
28✔
216
 * @param {Object} state - State object
28✔
217
 * @returns {undefined}
62✔
218
 * @throws {Error} - If is a clashing CommonJS var declaration
62✔
219
 */
62✔
220
function assertNoCommonJsVarsClash(block, varName, state) {
50,204✔
221
        if (block === state.programBlock && varName !== 'arguments' && state.fileBlock.bindings[varName]) {
50,204!
UNCOV
222
                throw new Error(`Clashing binding for var '${varName}'`);
×
UNCOV
223
        }
×
224
}
50,204✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2024 Coveralls, Inc