Apply module bundling
[platform/framework/web/wrtjs.git] / node_modules / eslint-scope / lib / scope.js
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
29 const Syntax = require("estraverse").Syntax;
30
31 const Reference = require("./reference");
32 const Variable = require("./variable");
33 const Definition = require("./definition").Definition;
34 const 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  */
44 function 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  */
128 function 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  */
145 function 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  */
155 class 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
522 class 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
583 class ModuleScope extends Scope {
584     constructor(scopeManager, upperScope, block) {
585         super(scopeManager, "module", upperScope, block, false);
586     }
587 }
588
589 class 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
605 class CatchScope extends Scope {
606     constructor(scopeManager, upperScope, block) {
607         super(scopeManager, "catch", upperScope, block, false);
608     }
609 }
610
611 class 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
633 class BlockScope extends Scope {
634     constructor(scopeManager, upperScope, block) {
635         super(scopeManager, "block", upperScope, block, false);
636     }
637 }
638
639 class SwitchScope extends Scope {
640     constructor(scopeManager, upperScope, block) {
641         super(scopeManager, "switch", upperScope, block, false);
642     }
643 }
644
645 class 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
722 class ForScope extends Scope {
723     constructor(scopeManager, upperScope, block) {
724         super(scopeManager, "for", upperScope, block, false);
725     }
726 }
727
728 class ClassScope extends Scope {
729     constructor(scopeManager, upperScope, block) {
730         super(scopeManager, "class", upperScope, block, false);
731     }
732 }
733
734 module.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 : */