source: trip-planner-front/node_modules/eslint-scope/lib/scope.js@ 6c1585f

Last change on this file since 6c1585f was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 21.1 KB
Line 
1/*
2 Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
6
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12
13 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
17 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23*/
24"use strict";
25
26/* eslint-disable no-underscore-dangle */
27/* eslint-disable no-undefined */
28
29const Syntax = require("estraverse").Syntax;
30
31const Reference = require("./reference");
32const Variable = require("./variable");
33const Definition = require("./definition").Definition;
34const assert = require("assert");
35
36/**
37 * Test if scope is struct
38 * @param {Scope} scope - scope
39 * @param {Block} block - block
40 * @param {boolean} isMethodDefinition - is method definition
41 * @param {boolean} useDirective - use directive
42 * @returns {boolean} is strict scope
43 */
44function isStrictScope(scope, block, isMethodDefinition, useDirective) {
45 let body;
46
47 // When upper scope is exists and strict, inner scope is also strict.
48 if (scope.upper && scope.upper.isStrict) {
49 return true;
50 }
51
52 if (isMethodDefinition) {
53 return true;
54 }
55
56 if (scope.type === "class" || scope.type === "module") {
57 return true;
58 }
59
60 if (scope.type === "block" || scope.type === "switch") {
61 return false;
62 }
63
64 if (scope.type === "function") {
65 if (block.type === Syntax.ArrowFunctionExpression && block.body.type !== Syntax.BlockStatement) {
66 return false;
67 }
68
69 if (block.type === Syntax.Program) {
70 body = block;
71 } else {
72 body = block.body;
73 }
74
75 if (!body) {
76 return false;
77 }
78 } else if (scope.type === "global") {
79 body = block;
80 } else {
81 return false;
82 }
83
84 // Search 'use strict' directive.
85 if (useDirective) {
86 for (let i = 0, iz = body.body.length; i < iz; ++i) {
87 const stmt = body.body[i];
88
89 if (stmt.type !== Syntax.DirectiveStatement) {
90 break;
91 }
92 if (stmt.raw === "\"use strict\"" || stmt.raw === "'use strict'") {
93 return true;
94 }
95 }
96 } else {
97 for (let i = 0, iz = body.body.length; i < iz; ++i) {
98 const stmt = body.body[i];
99
100 if (stmt.type !== Syntax.ExpressionStatement) {
101 break;
102 }
103 const expr = stmt.expression;
104
105 if (expr.type !== Syntax.Literal || typeof expr.value !== "string") {
106 break;
107 }
108 if (expr.raw !== null && expr.raw !== undefined) {
109 if (expr.raw === "\"use strict\"" || expr.raw === "'use strict'") {
110 return true;
111 }
112 } else {
113 if (expr.value === "use strict") {
114 return true;
115 }
116 }
117 }
118 }
119 return false;
120}
121
122/**
123 * Register scope
124 * @param {ScopeManager} scopeManager - scope manager
125 * @param {Scope} scope - scope
126 * @returns {void}
127 */
128function registerScope(scopeManager, scope) {
129 scopeManager.scopes.push(scope);
130
131 const scopes = scopeManager.__nodeToScope.get(scope.block);
132
133 if (scopes) {
134 scopes.push(scope);
135 } else {
136 scopeManager.__nodeToScope.set(scope.block, [scope]);
137 }
138}
139
140/**
141 * Should be statically
142 * @param {Object} def - def
143 * @returns {boolean} should be statically
144 */
145function shouldBeStatically(def) {
146 return (
147 (def.type === Variable.ClassName) ||
148 (def.type === Variable.Variable && def.parent.kind !== "var")
149 );
150}
151
152/**
153 * @class Scope
154 */
155class Scope {
156 constructor(scopeManager, type, upperScope, block, isMethodDefinition) {
157
158 /**
159 * One of 'module', 'block', 'switch', 'function', 'catch', 'with', 'function', 'class', 'global'.
160 * @member {String} Scope#type
161 */
162 this.type = type;
163
164 /**
165 * The scoped {@link Variable}s of this scope, as <code>{ Variable.name
166 * : Variable }</code>.
167 * @member {Map} Scope#set
168 */
169 this.set = new Map();
170
171 /**
172 * The tainted variables of this scope, as <code>{ Variable.name :
173 * boolean }</code>.
174 * @member {Map} Scope#taints */
175 this.taints = new Map();
176
177 /**
178 * Generally, through the lexical scoping of JS you can always know
179 * which variable an identifier in the source code refers to. There are
180 * a few exceptions to this rule. With 'global' and 'with' scopes you
181 * can only decide at runtime which variable a reference refers to.
182 * Moreover, if 'eval()' is used in a scope, it might introduce new
183 * bindings in this or its parent scopes.
184 * All those scopes are considered 'dynamic'.
185 * @member {boolean} Scope#dynamic
186 */
187 this.dynamic = this.type === "global" || this.type === "with";
188
189 /**
190 * A reference to the scope-defining syntax node.
191 * @member {espree.Node} Scope#block
192 */
193 this.block = block;
194
195 /**
196 * The {@link Reference|references} that are not resolved with this scope.
197 * @member {Reference[]} Scope#through
198 */
199 this.through = [];
200
201 /**
202 * The scoped {@link Variable}s of this scope. In the case of a
203 * 'function' scope this includes the automatic argument <em>arguments</em> as
204 * its first element, as well as all further formal arguments.
205 * @member {Variable[]} Scope#variables
206 */
207 this.variables = [];
208
209 /**
210 * Any variable {@link Reference|reference} found in this scope. This
211 * includes occurrences of local variables as well as variables from
212 * parent scopes (including the global scope). For local variables
213 * this also includes defining occurrences (like in a 'var' statement).
214 * In a 'function' scope this does not include the occurrences of the
215 * formal parameter in the parameter list.
216 * @member {Reference[]} Scope#references
217 */
218 this.references = [];
219
220 /**
221 * For 'global' and 'function' scopes, this is a self-reference. For
222 * other scope types this is the <em>variableScope</em> value of the
223 * parent scope.
224 * @member {Scope} Scope#variableScope
225 */
226 this.variableScope =
227 (this.type === "global" || this.type === "function" || this.type === "module") ? this : upperScope.variableScope;
228
229 /**
230 * Whether this scope is created by a FunctionExpression.
231 * @member {boolean} Scope#functionExpressionScope
232 */
233 this.functionExpressionScope = false;
234
235 /**
236 * Whether this is a scope that contains an 'eval()' invocation.
237 * @member {boolean} Scope#directCallToEvalScope
238 */
239 this.directCallToEvalScope = false;
240
241 /**
242 * @member {boolean} Scope#thisFound
243 */
244 this.thisFound = false;
245
246 this.__left = [];
247
248 /**
249 * Reference to the parent {@link Scope|scope}.
250 * @member {Scope} Scope#upper
251 */
252 this.upper = upperScope;
253
254 /**
255 * Whether 'use strict' is in effect in this scope.
256 * @member {boolean} Scope#isStrict
257 */
258 this.isStrict = isStrictScope(this, block, isMethodDefinition, scopeManager.__useDirective());
259
260 /**
261 * List of nested {@link Scope}s.
262 * @member {Scope[]} Scope#childScopes
263 */
264 this.childScopes = [];
265 if (this.upper) {
266 this.upper.childScopes.push(this);
267 }
268
269 this.__declaredVariables = scopeManager.__declaredVariables;
270
271 registerScope(scopeManager, this);
272 }
273
274 __shouldStaticallyClose(scopeManager) {
275 return (!this.dynamic || scopeManager.__isOptimistic());
276 }
277
278 __shouldStaticallyCloseForGlobal(ref) {
279
280 // On global scope, let/const/class declarations should be resolved statically.
281 const name = ref.identifier.name;
282
283 if (!this.set.has(name)) {
284 return false;
285 }
286
287 const variable = this.set.get(name);
288 const defs = variable.defs;
289
290 return defs.length > 0 && defs.every(shouldBeStatically);
291 }
292
293 __staticCloseRef(ref) {
294 if (!this.__resolve(ref)) {
295 this.__delegateToUpperScope(ref);
296 }
297 }
298
299 __dynamicCloseRef(ref) {
300
301 // notify all names are through to global
302 let current = this;
303
304 do {
305 current.through.push(ref);
306 current = current.upper;
307 } while (current);
308 }
309
310 __globalCloseRef(ref) {
311
312 // let/const/class declarations should be resolved statically.
313 // others should be resolved dynamically.
314 if (this.__shouldStaticallyCloseForGlobal(ref)) {
315 this.__staticCloseRef(ref);
316 } else {
317 this.__dynamicCloseRef(ref);
318 }
319 }
320
321 __close(scopeManager) {
322 let closeRef;
323
324 if (this.__shouldStaticallyClose(scopeManager)) {
325 closeRef = this.__staticCloseRef;
326 } else if (this.type !== "global") {
327 closeRef = this.__dynamicCloseRef;
328 } else {
329 closeRef = this.__globalCloseRef;
330 }
331
332 // Try Resolving all references in this scope.
333 for (let i = 0, iz = this.__left.length; i < iz; ++i) {
334 const ref = this.__left[i];
335
336 closeRef.call(this, ref);
337 }
338 this.__left = null;
339
340 return this.upper;
341 }
342
343 // To override by function scopes.
344 // References in default parameters isn't resolved to variables which are in their function body.
345 __isValidResolution(ref, variable) { // eslint-disable-line class-methods-use-this, no-unused-vars
346 return true;
347 }
348
349 __resolve(ref) {
350 const name = ref.identifier.name;
351
352 if (!this.set.has(name)) {
353 return false;
354 }
355 const variable = this.set.get(name);
356
357 if (!this.__isValidResolution(ref, variable)) {
358 return false;
359 }
360 variable.references.push(ref);
361 variable.stack = variable.stack && ref.from.variableScope === this.variableScope;
362 if (ref.tainted) {
363 variable.tainted = true;
364 this.taints.set(variable.name, true);
365 }
366 ref.resolved = variable;
367
368 return true;
369 }
370
371 __delegateToUpperScope(ref) {
372 if (this.upper) {
373 this.upper.__left.push(ref);
374 }
375 this.through.push(ref);
376 }
377
378 __addDeclaredVariablesOfNode(variable, node) {
379 if (node === null || node === undefined) {
380 return;
381 }
382
383 let variables = this.__declaredVariables.get(node);
384
385 if (variables === null || variables === undefined) {
386 variables = [];
387 this.__declaredVariables.set(node, variables);
388 }
389 if (variables.indexOf(variable) === -1) {
390 variables.push(variable);
391 }
392 }
393
394 __defineGeneric(name, set, variables, node, def) {
395 let variable;
396
397 variable = set.get(name);
398 if (!variable) {
399 variable = new Variable(name, this);
400 set.set(name, variable);
401 variables.push(variable);
402 }
403
404 if (def) {
405 variable.defs.push(def);
406 this.__addDeclaredVariablesOfNode(variable, def.node);
407 this.__addDeclaredVariablesOfNode(variable, def.parent);
408 }
409 if (node) {
410 variable.identifiers.push(node);
411 }
412 }
413
414 __define(node, def) {
415 if (node && node.type === Syntax.Identifier) {
416 this.__defineGeneric(
417 node.name,
418 this.set,
419 this.variables,
420 node,
421 def
422 );
423 }
424 }
425
426 __referencing(node, assign, writeExpr, maybeImplicitGlobal, partial, init) {
427
428 // because Array element may be null
429 if (!node || node.type !== Syntax.Identifier) {
430 return;
431 }
432
433 // Specially handle like `this`.
434 if (node.name === "super") {
435 return;
436 }
437
438 const ref = new Reference(node, this, assign || Reference.READ, writeExpr, maybeImplicitGlobal, !!partial, !!init);
439
440 this.references.push(ref);
441 this.__left.push(ref);
442 }
443
444 __detectEval() {
445 let current = this;
446
447 this.directCallToEvalScope = true;
448 do {
449 current.dynamic = true;
450 current = current.upper;
451 } while (current);
452 }
453
454 __detectThis() {
455 this.thisFound = true;
456 }
457
458 __isClosed() {
459 return this.__left === null;
460 }
461
462 /**
463 * returns resolved {Reference}
464 * @method Scope#resolve
465 * @param {Espree.Identifier} ident - identifier to be resolved.
466 * @returns {Reference} reference
467 */
468 resolve(ident) {
469 let ref, i, iz;
470
471 assert(this.__isClosed(), "Scope should be closed.");
472 assert(ident.type === Syntax.Identifier, "Target should be identifier.");
473 for (i = 0, iz = this.references.length; i < iz; ++i) {
474 ref = this.references[i];
475 if (ref.identifier === ident) {
476 return ref;
477 }
478 }
479 return null;
480 }
481
482 /**
483 * returns this scope is static
484 * @method Scope#isStatic
485 * @returns {boolean} static
486 */
487 isStatic() {
488 return !this.dynamic;
489 }
490
491 /**
492 * returns this scope has materialized arguments
493 * @method Scope#isArgumentsMaterialized
494 * @returns {boolean} arguemnts materialized
495 */
496 isArgumentsMaterialized() { // eslint-disable-line class-methods-use-this
497 return true;
498 }
499
500 /**
501 * returns this scope has materialized `this` reference
502 * @method Scope#isThisMaterialized
503 * @returns {boolean} this materialized
504 */
505 isThisMaterialized() { // eslint-disable-line class-methods-use-this
506 return true;
507 }
508
509 isUsedName(name) {
510 if (this.set.has(name)) {
511 return true;
512 }
513 for (let i = 0, iz = this.through.length; i < iz; ++i) {
514 if (this.through[i].identifier.name === name) {
515 return true;
516 }
517 }
518 return false;
519 }
520}
521
522class GlobalScope extends Scope {
523 constructor(scopeManager, block) {
524 super(scopeManager, "global", null, block, false);
525 this.implicit = {
526 set: new Map(),
527 variables: [],
528
529 /**
530 * List of {@link Reference}s that are left to be resolved (i.e. which
531 * need to be linked to the variable they refer to).
532 * @member {Reference[]} Scope#implicit#left
533 */
534 left: []
535 };
536 }
537
538 __close(scopeManager) {
539 const implicit = [];
540
541 for (let i = 0, iz = this.__left.length; i < iz; ++i) {
542 const ref = this.__left[i];
543
544 if (ref.__maybeImplicitGlobal && !this.set.has(ref.identifier.name)) {
545 implicit.push(ref.__maybeImplicitGlobal);
546 }
547 }
548
549 // create an implicit global variable from assignment expression
550 for (let i = 0, iz = implicit.length; i < iz; ++i) {
551 const info = implicit[i];
552
553 this.__defineImplicit(info.pattern,
554 new Definition(
555 Variable.ImplicitGlobalVariable,
556 info.pattern,
557 info.node,
558 null,
559 null,
560 null
561 ));
562
563 }
564
565 this.implicit.left = this.__left;
566
567 return super.__close(scopeManager);
568 }
569
570 __defineImplicit(node, def) {
571 if (node && node.type === Syntax.Identifier) {
572 this.__defineGeneric(
573 node.name,
574 this.implicit.set,
575 this.implicit.variables,
576 node,
577 def
578 );
579 }
580 }
581}
582
583class ModuleScope extends Scope {
584 constructor(scopeManager, upperScope, block) {
585 super(scopeManager, "module", upperScope, block, false);
586 }
587}
588
589class FunctionExpressionNameScope extends Scope {
590 constructor(scopeManager, upperScope, block) {
591 super(scopeManager, "function-expression-name", upperScope, block, false);
592 this.__define(block.id,
593 new Definition(
594 Variable.FunctionName,
595 block.id,
596 block,
597 null,
598 null,
599 null
600 ));
601 this.functionExpressionScope = true;
602 }
603}
604
605class CatchScope extends Scope {
606 constructor(scopeManager, upperScope, block) {
607 super(scopeManager, "catch", upperScope, block, false);
608 }
609}
610
611class WithScope extends Scope {
612 constructor(scopeManager, upperScope, block) {
613 super(scopeManager, "with", upperScope, block, false);
614 }
615
616 __close(scopeManager) {
617 if (this.__shouldStaticallyClose(scopeManager)) {
618 return super.__close(scopeManager);
619 }
620
621 for (let i = 0, iz = this.__left.length; i < iz; ++i) {
622 const ref = this.__left[i];
623
624 ref.tainted = true;
625 this.__delegateToUpperScope(ref);
626 }
627 this.__left = null;
628
629 return this.upper;
630 }
631}
632
633class BlockScope extends Scope {
634 constructor(scopeManager, upperScope, block) {
635 super(scopeManager, "block", upperScope, block, false);
636 }
637}
638
639class SwitchScope extends Scope {
640 constructor(scopeManager, upperScope, block) {
641 super(scopeManager, "switch", upperScope, block, false);
642 }
643}
644
645class FunctionScope extends Scope {
646 constructor(scopeManager, upperScope, block, isMethodDefinition) {
647 super(scopeManager, "function", upperScope, block, isMethodDefinition);
648
649 // section 9.2.13, FunctionDeclarationInstantiation.
650 // NOTE Arrow functions never have an arguments objects.
651 if (this.block.type !== Syntax.ArrowFunctionExpression) {
652 this.__defineArguments();
653 }
654 }
655
656 isArgumentsMaterialized() {
657
658 // TODO(Constellation)
659 // We can more aggressive on this condition like this.
660 //
661 // function t() {
662 // // arguments of t is always hidden.
663 // function arguments() {
664 // }
665 // }
666 if (this.block.type === Syntax.ArrowFunctionExpression) {
667 return false;
668 }
669
670 if (!this.isStatic()) {
671 return true;
672 }
673
674 const variable = this.set.get("arguments");
675
676 assert(variable, "Always have arguments variable.");
677 return variable.tainted || variable.references.length !== 0;
678 }
679
680 isThisMaterialized() {
681 if (!this.isStatic()) {
682 return true;
683 }
684 return this.thisFound;
685 }
686
687 __defineArguments() {
688 this.__defineGeneric(
689 "arguments",
690 this.set,
691 this.variables,
692 null,
693 null
694 );
695 this.taints.set("arguments", true);
696 }
697
698 // References in default parameters isn't resolved to variables which are in their function body.
699 // const x = 1
700 // function f(a = x) { // This `x` is resolved to the `x` in the outer scope.
701 // const x = 2
702 // console.log(a)
703 // }
704 __isValidResolution(ref, variable) {
705
706 // If `options.nodejsScope` is true, `this.block` becomes a Program node.
707 if (this.block.type === "Program") {
708 return true;
709 }
710
711 const bodyStart = this.block.body.range[0];
712
713 // It's invalid resolution in the following case:
714 return !(
715 variable.scope === this &&
716 ref.identifier.range[0] < bodyStart && // the reference is in the parameter part.
717 variable.defs.every(d => d.name.range[0] >= bodyStart) // the variable is in the body.
718 );
719 }
720}
721
722class ForScope extends Scope {
723 constructor(scopeManager, upperScope, block) {
724 super(scopeManager, "for", upperScope, block, false);
725 }
726}
727
728class ClassScope extends Scope {
729 constructor(scopeManager, upperScope, block) {
730 super(scopeManager, "class", upperScope, block, false);
731 }
732}
733
734module.exports = {
735 Scope,
736 GlobalScope,
737 ModuleScope,
738 FunctionExpressionNameScope,
739 CatchScope,
740 WithScope,
741 BlockScope,
742 SwitchScope,
743 FunctionScope,
744 ForScope,
745 ClassScope
746};
747
748/* vim: set sw=4 ts=4 et tw=80 : */
Note: See TracBrowser for help on using the repository browser.