/*
							 | 
						|
								  Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
							 | 
						|
								
							 | 
						|
								  Redistribution and use in source and binary forms, with or without
							 | 
						|
								  modification, are permitted provided that the following conditions are met:
							 | 
						|
								
							 | 
						|
								    * Redistributions of source code must retain the above copyright
							 | 
						|
								      notice, this list of conditions and the following disclaimer.
							 | 
						|
								    * Redistributions in binary form must reproduce the above copyright
							 | 
						|
								      notice, this list of conditions and the following disclaimer in the
							 | 
						|
								      documentation and/or other materials provided with the distribution.
							 | 
						|
								
							 | 
						|
								  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
							 | 
						|
								  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
							 | 
						|
								  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
							 | 
						|
								  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
							 | 
						|
								  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
							 | 
						|
								  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
							 | 
						|
								  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
							 | 
						|
								  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
							 | 
						|
								  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
							 | 
						|
								  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
							 | 
						|
								*/
							 | 
						|
								
							 | 
						|
								/* eslint-disable no-underscore-dangle */
							 | 
						|
								/* eslint-disable no-undefined */
							 | 
						|
								
							 | 
						|
								import estraverse from "estraverse";
							 | 
						|
								import esrecurse from "esrecurse";
							 | 
						|
								import Reference from "./reference.js";
							 | 
						|
								import Variable from "./variable.js";
							 | 
						|
								import PatternVisitor from "./pattern-visitor.js";
							 | 
						|
								import { Definition, ParameterDefinition } from "./definition.js";
							 | 
						|
								import assert from "assert";
							 | 
						|
								
							 | 
						|
								const { Syntax } = estraverse;
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Traverse identifier in pattern
							 | 
						|
								 * @param {Object} options options
							 | 
						|
								 * @param {pattern} rootPattern root pattern
							 | 
						|
								 * @param {Refencer} referencer referencer
							 | 
						|
								 * @param {callback} callback callback
							 | 
						|
								 * @returns {void}
							 | 
						|
								 */
							 | 
						|
								function traverseIdentifierInPattern(options, rootPattern, referencer, callback) {
							 | 
						|
								
							 | 
						|
								    // Call the callback at left hand identifier nodes, and Collect right hand nodes.
							 | 
						|
								    const visitor = new PatternVisitor(options, rootPattern, callback);
							 | 
						|
								
							 | 
						|
								    visitor.visit(rootPattern);
							 | 
						|
								
							 | 
						|
								    // Process the right hand nodes recursively.
							 | 
						|
								    if (referencer !== null && referencer !== undefined) {
							 | 
						|
								        visitor.rightHandNodes.forEach(referencer.visit, referencer);
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Importing ImportDeclaration.
							 | 
						|
								// http://people.mozilla.org/~jorendorff/es6-draft.html#sec-moduledeclarationinstantiation
							 | 
						|
								// https://github.com/estree/estree/blob/master/es6.md#importdeclaration
							 | 
						|
								// FIXME: Now, we don't create module environment, because the context is
							 | 
						|
								// implementation dependent.
							 | 
						|
								
							 | 
						|
								class Importer extends esrecurse.Visitor {
							 | 
						|
								    constructor(declaration, referencer) {
							 | 
						|
								        super(null, referencer.options);
							 | 
						|
								        this.declaration = declaration;
							 | 
						|
								        this.referencer = referencer;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    visitImport(id, specifier) {
							 | 
						|
								        this.referencer.visitPattern(id, pattern => {
							 | 
						|
								            this.referencer.currentScope().__define(pattern,
							 | 
						|
								                new Definition(
							 | 
						|
								                    Variable.ImportBinding,
							 | 
						|
								                    pattern,
							 | 
						|
								                    specifier,
							 | 
						|
								                    this.declaration,
							 | 
						|
								                    null,
							 | 
						|
								                    null
							 | 
						|
								                ));
							 | 
						|
								        });
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ImportNamespaceSpecifier(node) {
							 | 
						|
								        const local = (node.local || node.id);
							 | 
						|
								
							 | 
						|
								        if (local) {
							 | 
						|
								            this.visitImport(local, node);
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ImportDefaultSpecifier(node) {
							 | 
						|
								        const local = (node.local || node.id);
							 | 
						|
								
							 | 
						|
								        this.visitImport(local, node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ImportSpecifier(node) {
							 | 
						|
								        const local = (node.local || node.id);
							 | 
						|
								
							 | 
						|
								        if (node.name) {
							 | 
						|
								            this.visitImport(node.name, node);
							 | 
						|
								        } else {
							 | 
						|
								            this.visitImport(local, node);
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Referencing variables and creating bindings.
							 | 
						|
								class Referencer extends esrecurse.Visitor {
							 | 
						|
								    constructor(options, scopeManager) {
							 | 
						|
								        super(null, options);
							 | 
						|
								        this.options = options;
							 | 
						|
								        this.scopeManager = scopeManager;
							 | 
						|
								        this.parent = null;
							 | 
						|
								        this.isInnerMethodDefinition = false;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    currentScope() {
							 | 
						|
								        return this.scopeManager.__currentScope;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    close(node) {
							 | 
						|
								        while (this.currentScope() && node === this.currentScope().block) {
							 | 
						|
								            this.scopeManager.__currentScope = this.currentScope().__close(this.scopeManager);
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    pushInnerMethodDefinition(isInnerMethodDefinition) {
							 | 
						|
								        const previous = this.isInnerMethodDefinition;
							 | 
						|
								
							 | 
						|
								        this.isInnerMethodDefinition = isInnerMethodDefinition;
							 | 
						|
								        return previous;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    popInnerMethodDefinition(isInnerMethodDefinition) {
							 | 
						|
								        this.isInnerMethodDefinition = isInnerMethodDefinition;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    referencingDefaultValue(pattern, assignments, maybeImplicitGlobal, init) {
							 | 
						|
								        const scope = this.currentScope();
							 | 
						|
								
							 | 
						|
								        assignments.forEach(assignment => {
							 | 
						|
								            scope.__referencing(
							 | 
						|
								                pattern,
							 | 
						|
								                Reference.WRITE,
							 | 
						|
								                assignment.right,
							 | 
						|
								                maybeImplicitGlobal,
							 | 
						|
								                pattern !== assignment.left,
							 | 
						|
								                init
							 | 
						|
								            );
							 | 
						|
								        });
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    visitPattern(node, options, callback) {
							 | 
						|
								        let visitPatternOptions = options;
							 | 
						|
								        let visitPatternCallback = callback;
							 | 
						|
								
							 | 
						|
								        if (typeof options === "function") {
							 | 
						|
								            visitPatternCallback = options;
							 | 
						|
								            visitPatternOptions = { processRightHandNodes: false };
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        traverseIdentifierInPattern(
							 | 
						|
								            this.options,
							 | 
						|
								            node,
							 | 
						|
								            visitPatternOptions.processRightHandNodes ? this : null,
							 | 
						|
								            visitPatternCallback
							 | 
						|
								        );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    visitFunction(node) {
							 | 
						|
								        let i, iz;
							 | 
						|
								
							 | 
						|
								        // FunctionDeclaration name is defined in upper scope
							 | 
						|
								        // NOTE: Not referring variableScope. It is intended.
							 | 
						|
								        // Since
							 | 
						|
								        //  in ES5, FunctionDeclaration should be in FunctionBody.
							 | 
						|
								        //  in ES6, FunctionDeclaration should be block scoped.
							 | 
						|
								
							 | 
						|
								        if (node.type === Syntax.FunctionDeclaration) {
							 | 
						|
								
							 | 
						|
								            // id is defined in upper scope
							 | 
						|
								            this.currentScope().__define(node.id,
							 | 
						|
								                new Definition(
							 | 
						|
								                    Variable.FunctionName,
							 | 
						|
								                    node.id,
							 | 
						|
								                    node,
							 | 
						|
								                    null,
							 | 
						|
								                    null,
							 | 
						|
								                    null
							 | 
						|
								                ));
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // FunctionExpression with name creates its special scope;
							 | 
						|
								        // FunctionExpressionNameScope.
							 | 
						|
								        if (node.type === Syntax.FunctionExpression && node.id) {
							 | 
						|
								            this.scopeManager.__nestFunctionExpressionNameScope(node);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Consider this function is in the MethodDefinition.
							 | 
						|
								        this.scopeManager.__nestFunctionScope(node, this.isInnerMethodDefinition);
							 | 
						|
								
							 | 
						|
								        const that = this;
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Visit pattern callback
							 | 
						|
								         * @param {pattern} pattern pattern
							 | 
						|
								         * @param {Object} info info
							 | 
						|
								         * @returns {void}
							 | 
						|
								         */
							 | 
						|
								        function visitPatternCallback(pattern, info) {
							 | 
						|
								            that.currentScope().__define(pattern,
							 | 
						|
								                new ParameterDefinition(
							 | 
						|
								                    pattern,
							 | 
						|
								                    node,
							 | 
						|
								                    i,
							 | 
						|
								                    info.rest
							 | 
						|
								                ));
							 | 
						|
								
							 | 
						|
								            that.referencingDefaultValue(pattern, info.assignments, null, true);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Process parameter declarations.
							 | 
						|
								        for (i = 0, iz = node.params.length; i < iz; ++i) {
							 | 
						|
								            this.visitPattern(node.params[i], { processRightHandNodes: true }, visitPatternCallback);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // if there's a rest argument, add that
							 | 
						|
								        if (node.rest) {
							 | 
						|
								            this.visitPattern({
							 | 
						|
								                type: "RestElement",
							 | 
						|
								                argument: node.rest
							 | 
						|
								            }, pattern => {
							 | 
						|
								                this.currentScope().__define(pattern,
							 | 
						|
								                    new ParameterDefinition(
							 | 
						|
								                        pattern,
							 | 
						|
								                        node,
							 | 
						|
								                        node.params.length,
							 | 
						|
								                        true
							 | 
						|
								                    ));
							 | 
						|
								            });
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // In TypeScript there are a number of function-like constructs which have no body,
							 | 
						|
								        // so check it exists before traversing
							 | 
						|
								        if (node.body) {
							 | 
						|
								
							 | 
						|
								            // Skip BlockStatement to prevent creating BlockStatement scope.
							 | 
						|
								            if (node.body.type === Syntax.BlockStatement) {
							 | 
						|
								                this.visitChildren(node.body);
							 | 
						|
								            } else {
							 | 
						|
								                this.visit(node.body);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        this.close(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    visitClass(node) {
							 | 
						|
								        if (node.type === Syntax.ClassDeclaration) {
							 | 
						|
								            this.currentScope().__define(node.id,
							 | 
						|
								                new Definition(
							 | 
						|
								                    Variable.ClassName,
							 | 
						|
								                    node.id,
							 | 
						|
								                    node,
							 | 
						|
								                    null,
							 | 
						|
								                    null,
							 | 
						|
								                    null
							 | 
						|
								                ));
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        this.visit(node.superClass);
							 | 
						|
								
							 | 
						|
								        this.scopeManager.__nestClassScope(node);
							 | 
						|
								
							 | 
						|
								        if (node.id) {
							 | 
						|
								            this.currentScope().__define(node.id,
							 | 
						|
								                new Definition(
							 | 
						|
								                    Variable.ClassName,
							 | 
						|
								                    node.id,
							 | 
						|
								                    node
							 | 
						|
								                ));
							 | 
						|
								        }
							 | 
						|
								        this.visit(node.body);
							 | 
						|
								
							 | 
						|
								        this.close(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    visitProperty(node) {
							 | 
						|
								        let previous;
							 | 
						|
								
							 | 
						|
								        if (node.computed) {
							 | 
						|
								            this.visit(node.key);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        const isMethodDefinition = node.type === Syntax.MethodDefinition;
							 | 
						|
								
							 | 
						|
								        if (isMethodDefinition) {
							 | 
						|
								            previous = this.pushInnerMethodDefinition(true);
							 | 
						|
								        }
							 | 
						|
								        this.visit(node.value);
							 | 
						|
								        if (isMethodDefinition) {
							 | 
						|
								            this.popInnerMethodDefinition(previous);
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    visitForIn(node) {
							 | 
						|
								        if (node.left.type === Syntax.VariableDeclaration && node.left.kind !== "var") {
							 | 
						|
								            this.scopeManager.__nestForScope(node);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        if (node.left.type === Syntax.VariableDeclaration) {
							 | 
						|
								            this.visit(node.left);
							 | 
						|
								            this.visitPattern(node.left.declarations[0].id, pattern => {
							 | 
						|
								                this.currentScope().__referencing(pattern, Reference.WRITE, node.right, null, true, true);
							 | 
						|
								            });
							 | 
						|
								        } else {
							 | 
						|
								            this.visitPattern(node.left, { processRightHandNodes: true }, (pattern, info) => {
							 | 
						|
								                let maybeImplicitGlobal = null;
							 | 
						|
								
							 | 
						|
								                if (!this.currentScope().isStrict) {
							 | 
						|
								                    maybeImplicitGlobal = {
							 | 
						|
								                        pattern,
							 | 
						|
								                        node
							 | 
						|
								                    };
							 | 
						|
								                }
							 | 
						|
								                this.referencingDefaultValue(pattern, info.assignments, maybeImplicitGlobal, false);
							 | 
						|
								                this.currentScope().__referencing(pattern, Reference.WRITE, node.right, maybeImplicitGlobal, true, false);
							 | 
						|
								            });
							 | 
						|
								        }
							 | 
						|
								        this.visit(node.right);
							 | 
						|
								        this.visit(node.body);
							 | 
						|
								
							 | 
						|
								        this.close(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    visitVariableDeclaration(variableTargetScope, type, node, index) {
							 | 
						|
								
							 | 
						|
								        const decl = node.declarations[index];
							 | 
						|
								        const init = decl.init;
							 | 
						|
								
							 | 
						|
								        this.visitPattern(decl.id, { processRightHandNodes: true }, (pattern, info) => {
							 | 
						|
								            variableTargetScope.__define(
							 | 
						|
								                pattern,
							 | 
						|
								                new Definition(
							 | 
						|
								                    type,
							 | 
						|
								                    pattern,
							 | 
						|
								                    decl,
							 | 
						|
								                    node,
							 | 
						|
								                    index,
							 | 
						|
								                    node.kind
							 | 
						|
								                )
							 | 
						|
								            );
							 | 
						|
								
							 | 
						|
								            this.referencingDefaultValue(pattern, info.assignments, null, true);
							 | 
						|
								            if (init) {
							 | 
						|
								                this.currentScope().__referencing(pattern, Reference.WRITE, init, null, !info.topLevel, true);
							 | 
						|
								            }
							 | 
						|
								        });
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    AssignmentExpression(node) {
							 | 
						|
								        if (PatternVisitor.isPattern(node.left)) {
							 | 
						|
								            if (node.operator === "=") {
							 | 
						|
								                this.visitPattern(node.left, { processRightHandNodes: true }, (pattern, info) => {
							 | 
						|
								                    let maybeImplicitGlobal = null;
							 | 
						|
								
							 | 
						|
								                    if (!this.currentScope().isStrict) {
							 | 
						|
								                        maybeImplicitGlobal = {
							 | 
						|
								                            pattern,
							 | 
						|
								                            node
							 | 
						|
								                        };
							 | 
						|
								                    }
							 | 
						|
								                    this.referencingDefaultValue(pattern, info.assignments, maybeImplicitGlobal, false);
							 | 
						|
								                    this.currentScope().__referencing(pattern, Reference.WRITE, node.right, maybeImplicitGlobal, !info.topLevel, false);
							 | 
						|
								                });
							 | 
						|
								            } else {
							 | 
						|
								                this.currentScope().__referencing(node.left, Reference.RW, node.right);
							 | 
						|
								            }
							 | 
						|
								        } else {
							 | 
						|
								            this.visit(node.left);
							 | 
						|
								        }
							 | 
						|
								        this.visit(node.right);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    CatchClause(node) {
							 | 
						|
								        this.scopeManager.__nestCatchScope(node);
							 | 
						|
								
							 | 
						|
								        this.visitPattern(node.param, { processRightHandNodes: true }, (pattern, info) => {
							 | 
						|
								            this.currentScope().__define(pattern,
							 | 
						|
								                new Definition(
							 | 
						|
								                    Variable.CatchClause,
							 | 
						|
								                    node.param,
							 | 
						|
								                    node,
							 | 
						|
								                    null,
							 | 
						|
								                    null,
							 | 
						|
								                    null
							 | 
						|
								                ));
							 | 
						|
								            this.referencingDefaultValue(pattern, info.assignments, null, true);
							 | 
						|
								        });
							 | 
						|
								        this.visit(node.body);
							 | 
						|
								
							 | 
						|
								        this.close(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    Program(node) {
							 | 
						|
								        this.scopeManager.__nestGlobalScope(node);
							 | 
						|
								
							 | 
						|
								        if (this.scopeManager.isGlobalReturn()) {
							 | 
						|
								
							 | 
						|
								            // Force strictness of GlobalScope to false when using node.js scope.
							 | 
						|
								            this.currentScope().isStrict = false;
							 | 
						|
								            this.scopeManager.__nestFunctionScope(node, false);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        if (this.scopeManager.__isES6() && this.scopeManager.isModule()) {
							 | 
						|
								            this.scopeManager.__nestModuleScope(node);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        if (this.scopeManager.isStrictModeSupported() && this.scopeManager.isImpliedStrict()) {
							 | 
						|
								            this.currentScope().isStrict = true;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        this.visitChildren(node);
							 | 
						|
								        this.close(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    Identifier(node) {
							 | 
						|
								        this.currentScope().__referencing(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    // eslint-disable-next-line class-methods-use-this
							 | 
						|
								    PrivateIdentifier() {
							 | 
						|
								
							 | 
						|
								        // Do nothing.
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    UpdateExpression(node) {
							 | 
						|
								        if (PatternVisitor.isPattern(node.argument)) {
							 | 
						|
								            this.currentScope().__referencing(node.argument, Reference.RW, null);
							 | 
						|
								        } else {
							 | 
						|
								            this.visitChildren(node);
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    MemberExpression(node) {
							 | 
						|
								        this.visit(node.object);
							 | 
						|
								        if (node.computed) {
							 | 
						|
								            this.visit(node.property);
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    Property(node) {
							 | 
						|
								        this.visitProperty(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    PropertyDefinition(node) {
							 | 
						|
								        const { computed, key, value } = node;
							 | 
						|
								
							 | 
						|
								        if (computed) {
							 | 
						|
								            this.visit(key);
							 | 
						|
								        }
							 | 
						|
								        if (value) {
							 | 
						|
								            this.scopeManager.__nestClassFieldInitializerScope(value);
							 | 
						|
								            this.visit(value);
							 | 
						|
								            this.close(value);
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    StaticBlock(node) {
							 | 
						|
								        this.scopeManager.__nestClassStaticBlockScope(node);
							 | 
						|
								
							 | 
						|
								        this.visitChildren(node);
							 | 
						|
								
							 | 
						|
								        this.close(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    MethodDefinition(node) {
							 | 
						|
								        this.visitProperty(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    BreakStatement() {} // eslint-disable-line class-methods-use-this
							 | 
						|
								
							 | 
						|
								    ContinueStatement() {} // eslint-disable-line class-methods-use-this
							 | 
						|
								
							 | 
						|
								    LabeledStatement(node) {
							 | 
						|
								        this.visit(node.body);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ForStatement(node) {
							 | 
						|
								
							 | 
						|
								        // Create ForStatement declaration.
							 | 
						|
								        // NOTE: In ES6, ForStatement dynamically generates
							 | 
						|
								        // per iteration environment. However, escope is
							 | 
						|
								        // a static analyzer, we only generate one scope for ForStatement.
							 | 
						|
								        if (node.init && node.init.type === Syntax.VariableDeclaration && node.init.kind !== "var") {
							 | 
						|
								            this.scopeManager.__nestForScope(node);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        this.visitChildren(node);
							 | 
						|
								
							 | 
						|
								        this.close(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ClassExpression(node) {
							 | 
						|
								        this.visitClass(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ClassDeclaration(node) {
							 | 
						|
								        this.visitClass(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    CallExpression(node) {
							 | 
						|
								
							 | 
						|
								        // Check this is direct call to eval
							 | 
						|
								        if (!this.scopeManager.__ignoreEval() && node.callee.type === Syntax.Identifier && node.callee.name === "eval") {
							 | 
						|
								
							 | 
						|
								            // NOTE: This should be `variableScope`. Since direct eval call always creates Lexical environment and
							 | 
						|
								            // let / const should be enclosed into it. Only VariableDeclaration affects on the caller's environment.
							 | 
						|
								            this.currentScope().variableScope.__detectEval();
							 | 
						|
								        }
							 | 
						|
								        this.visitChildren(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    BlockStatement(node) {
							 | 
						|
								        if (this.scopeManager.__isES6()) {
							 | 
						|
								            this.scopeManager.__nestBlockScope(node);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        this.visitChildren(node);
							 | 
						|
								
							 | 
						|
								        this.close(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ThisExpression() {
							 | 
						|
								        this.currentScope().variableScope.__detectThis();
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    WithStatement(node) {
							 | 
						|
								        this.visit(node.object);
							 | 
						|
								
							 | 
						|
								        // Then nest scope for WithStatement.
							 | 
						|
								        this.scopeManager.__nestWithScope(node);
							 | 
						|
								
							 | 
						|
								        this.visit(node.body);
							 | 
						|
								
							 | 
						|
								        this.close(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    VariableDeclaration(node) {
							 | 
						|
								        const variableTargetScope = (node.kind === "var") ? this.currentScope().variableScope : this.currentScope();
							 | 
						|
								
							 | 
						|
								        for (let i = 0, iz = node.declarations.length; i < iz; ++i) {
							 | 
						|
								            const decl = node.declarations[i];
							 | 
						|
								
							 | 
						|
								            this.visitVariableDeclaration(variableTargetScope, Variable.Variable, node, i);
							 | 
						|
								            if (decl.init) {
							 | 
						|
								                this.visit(decl.init);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    // sec 13.11.8
							 | 
						|
								    SwitchStatement(node) {
							 | 
						|
								        this.visit(node.discriminant);
							 | 
						|
								
							 | 
						|
								        if (this.scopeManager.__isES6()) {
							 | 
						|
								            this.scopeManager.__nestSwitchScope(node);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        for (let i = 0, iz = node.cases.length; i < iz; ++i) {
							 | 
						|
								            this.visit(node.cases[i]);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        this.close(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    FunctionDeclaration(node) {
							 | 
						|
								        this.visitFunction(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    FunctionExpression(node) {
							 | 
						|
								        this.visitFunction(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ForOfStatement(node) {
							 | 
						|
								        this.visitForIn(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ForInStatement(node) {
							 | 
						|
								        this.visitForIn(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ArrowFunctionExpression(node) {
							 | 
						|
								        this.visitFunction(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ImportDeclaration(node) {
							 | 
						|
								        assert(this.scopeManager.__isES6() && this.scopeManager.isModule(), "ImportDeclaration should appear when the mode is ES6 and in the module context.");
							 | 
						|
								
							 | 
						|
								        const importer = new Importer(node, this);
							 | 
						|
								
							 | 
						|
								        importer.visit(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    visitExportDeclaration(node) {
							 | 
						|
								        if (node.source) {
							 | 
						|
								            return;
							 | 
						|
								        }
							 | 
						|
								        if (node.declaration) {
							 | 
						|
								            this.visit(node.declaration);
							 | 
						|
								            return;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        this.visitChildren(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    // TODO: ExportDeclaration doesn't exist. for bc?
							 | 
						|
								    ExportDeclaration(node) {
							 | 
						|
								        this.visitExportDeclaration(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ExportAllDeclaration(node) {
							 | 
						|
								        this.visitExportDeclaration(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ExportDefaultDeclaration(node) {
							 | 
						|
								        this.visitExportDeclaration(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ExportNamedDeclaration(node) {
							 | 
						|
								        this.visitExportDeclaration(node);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    ExportSpecifier(node) {
							 | 
						|
								
							 | 
						|
								        // TODO: `node.id` doesn't exist. for bc?
							 | 
						|
								        const local = (node.id || node.local);
							 | 
						|
								
							 | 
						|
								        this.visit(local);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    MetaProperty() { // eslint-disable-line class-methods-use-this
							 | 
						|
								
							 | 
						|
								        // do nothing.
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								export default Referencer;
							 | 
						|
								
							 | 
						|
								/* vim: set sw=4 ts=4 et tw=80 : */
							 |