GithubHelp home page GithubHelp logo

estools / escope Goto Github PK

View Code? Open in Web Editor NEW
539.0 18.0 77.0 426 KB

Escope: ECMAScript scope analyzer

License: BSD 2-Clause "Simplified" License

JavaScript 100.00%
ast estree javascript ecmascript

escope's People

Contributors

alex-seville avatar btmills avatar constellation avatar eventualbuddha avatar fczuardi avatar glasser avatar kevinbarabash avatar kumavis avatar mazurov avatar michaelficarra avatar mysticatea avatar nkzawa avatar nre avatar nzakas avatar pdehaan avatar realityking avatar robcolburn avatar shz avatar stuk avatar thron7 avatar trysound avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

escope's Issues

Named function expression causes duplicate scopes in ScopeManager

Please take a look at the illustration

Right: the sample code
Left (pseudocode): each(escope.analyze(code).scopes, function(scope) { return scope.block }

function Bear() {
    this.size = 'large'
}
Bear.prototype.say = function () {
    return 'roar'
}
Bear.prototype.move = function move() {
    return 'run'
}
Bear.prototype.attack = function attack() {
    function rage() {
    }
    return 'claw'
}
var crazyBear = new Bear();

Block-level functions and web extensions

Context: http://stackoverflow.com/questions/31419897/what-are-the-precise-semantics-of-block-level-functions-in-es6

I'm not well versed with the ES6 spec details, but from my understanding, there is some optional behavior for block-level function declarations that escope does not take into account.

In the following code:

// ES6

function foo() {

    {
        function bar() {};
    }

    bar(1);
}

the call to bar would not throw a ReferenceError if the web compatibility semantics extension is in effect.

The escope output for this code with {ecmaVersion: 6} does not link the bar reference with the bar declaration at all. Maybe an additional webExtensions flag for escope.analyze is needed.

Upgrade major version of escope

Now estraverse, esrecurse used in escope are upgraded and there's added/removed AST nodes,
so there're breaking changes. So I think it's time to upgrade major version.

@nzakas
Is it possible to upgrade the major version of escope on whcih eslint depends?
If it's too difficult, we need to consider another way.

ES6 module import tests relies on a version of esprima that doesn't follow estree spec

If you look at https://github.com/estools/escope/blob/master/test/es6-import.coffee#L25 you see that the tests for es6 uses an esprima version that does not generate https://github.com/estree/estree spec compliant ast

For example:

ast.body[0].specifiers for code 'import v from "mod"' is expected to output:

 [ { type: 'ImportDefaultSpecifier', local: { type: 'Identifier', name: 'v' } } ]

and not

 [ { type: 'ImportDefaultSpecifier', id: { type: 'Identifier', name: 'v' } } ]

adding eval inside a function puts all variables into global through

The following code:

function evilEval(stuffToEval)
{
    var ultimateAnswer;

    ultimateAnswer = 42;

    alert(stuffToEval);
}

Will put one variable into through array - alert. However, if you change alert to eval like so:

function evilEval(stuffToEval)
{
    var ultimateAnswer;

    ultimateAnswer = 42;

    eval(stuffToEval);
}

There are now 3 variables in through. eval, ultimateAnswer and stuffToEval.

This was found and reported here: eslint/eslint#376

Support Node.js scoping as an option?

Would you consider supporting Node.js scoping? Node.js does a weird thing where the module code is actually run inside a function, so this:

return false;

is legal even though it's not in a function because it's actually interpreted as:

(function(require, module, process) {
return false;
}());

What I'm proposing is a new flag nodejsScope that can be set to true to artificially add a function scope immediately following the global scope.

That way, tools like ESLint can properly maintain scope even when evaluating Node.js files.

Try blocks not identified as scope level

As far as I can tell after peaking at the spec, try blocks should also be included in the scopes while catch and finally shouldn't. However escope only creates adds a scope for catch blocks

import {parse} from 'acorn';
import escope from 'escope';

let ast = parse(`
const x = 2;
try {
  const x = 1;
  [1, 2, 3].map(x => x);
} catch(o_O) {
  a();
} finally {
  console.log(2);
}
`, { ecmaVersion: 6});

escope.analyze(ast);
// [{global scope}, {catch scope}]

Scope tree documentation

Is the scope tree that is output based on a standard or is it custom to this project?

I'm trying to identify the variables that are exported, by parsed code, to the global object.

On a trivial example reading the variables array of the global scope object works.

However, when I parse a file that passes in the context I see an empty variables array in the global object, and I'm not sure how to determine what would be available in the global scope if that code was executed.

i.e.

(function(){
...
}).call(this);

Any thoughts?

ObjectPattern/ObjectExpression incorrectly assume all children are Property

Both ObjectPattern and ObjectExpression assume that all nodes in properties will be of type Property, and that causes an error if there's ever a different node type present (such as with spread properties or rest properties).

Around here:
https://github.com/estools/escope/blob/master/src/referencer.js#L50

And here:
https://github.com/estools/escope/blob/master/src/referencer.js#L112

It should instead visit() each property so it can handle any node type that is present.

Use ES6

Use ES6 to write escope.

One technical note is that, current jsdoc doesn't support it...

Missing super identifier

In class methods, there should be a super identifier prepopulated in the scope (similar to this and arguments).

Use native Map and WeakMap when possible

Current implementation relies on using es6-map and es6-weak-map unconditionally which sometimes conflicts with other already injected polyfills or native implementations.

Please make those imports conditional (depending on whether Map and WeakMap already exist in global scope).

Broken links in docs after moving to estools org

I sent PR #38 to take care of the JSDoc link in the README, but there are broken links in the docs themselves still referencing constellation/escope. GitHub is smart enough to redirect some of these, but external links to travis for example won't work until they're updated.

Scope for ObjectExpression does not conform to Parser API spec

From https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API#Expressions

interface ObjectExpression <: Expression {
    type: "ObjectExpression";
    properties: [ { key: Literal | Identifier,
                    value: Expression,
                    kind: "init" | "get" | "set" } ];
}

esprima adds a non-standard type: "Property" field to this declaration, which escope relies on.

This is pretty pedantic, I know, but I'm reporting it because it causes esmangle to give bad output using the acorn parser. Specifically:

var acorn = require('acorn');
var esmangle = require('esmangle');
var escodegen = require('escodegen');

var ast = acorn.parse('(function() {var foo = {a: bar}; function bar() {};})();');
console.log(escodegen.generate(esmangle.mangle(ast)));

Results in

(function() {
  var a = {a:bar};
  function b() {};
})();

Rest arguments are ignored

In the latest escope with ecmaVersion: 6, rest arguments are ignored. For example:

function f(...b) {
}

In this code, the b argument isn't found by escope.

I believe I have a fix for this.

Related: eslint/eslint#1543

ES6 default parameters cause two write references

(@michaelficarra)

See for example this test:

expect(scope.references[1].identifier.name).to.equal 'c'
expect(scope.references[1].isWrite()).to.be.true
expect(scope.references[1].writeExpr.name).to.equal 'e'
expect(scope.references[1].partial).to.be.true
expect(scope.references[1].resolved).to.equal scope.variables[2]
expect(scope.references[2].identifier.name).to.equal 'c'
expect(scope.references[2].isWrite()).to.be.true

Note that there are two references with name c and isWrite() true: that is, two write references to c. This despite the fact that c will be written precisely once by the sample code. This is especially confusing if you're looking at const-correctness.

Is this intentional? If so, what's the reasoning behind it?

Add optimistic option

Currently Escope analysis is pessimistic.
For example, with statement / direct call to eval makes references unreachable.

(function () {
  var ref;
  eval(str);  // direct call to eval
  print(ref);
}());

This is because originally Escope is extracted from Esmangle project. Esmangle takes conservative approach on minification, that is Esmangle must not break any JS code semantics.

But in the other fields such as syntax coloring, optimistic scope analysis is useful.

So I'm planning to add optimistic option to Escope. This ignores eval and with effect.

Incorrect number of scopes reported for a function declaration case

Hi Yusuke!

First, let me thank you for all projects you have published on github, I am finding them quite interesting and useful. Also, let me know if this is not the best channel for communication in regards to these libraries.

While running escope.analize on the following code:

function getAdd1toX(x) {
return function add1toX() {
x = x + 1;
return x;
}
};

Although I would expect to get 3 scopes, 4 Scope objects are returned. The last two scopes, point to the same block (The add1ToX function), with the only difference being that in the "variables" array of their Scope object, one holds a variable with the name of the function (add1ToX) and the other holds a variable with the "arguments" (To be expected).

The interesting part is that the following representations of the same function, both return 3 scopes as expected:

function getAdd1toX2(x) {
return function () {
x = x + 1;
return x;
};
}

function getAdd1toX3(x) {
function add1toX() {
x = x + 1;
return x;
}
return add1toX;
}

When I compare the 3 functions execution context within chrome developer tools, I see only 3 scopes for the 3 versions of the function (global, getAdd1toX and add1toX), so I am assuming 3 scopes is the right behaviour.

Can you point me to the right direction on how to fix this bug?

Thanks!

Best regards,

Jorge C.

Destructuring introduces object properties as references

Example code:

var obj = {};
[obj.prop] = [1];

The output includes prop as a reference:

// globalScope.references[2]

{ identifier: { type: 'Identifier', name: 'prop' },
  from: 
   { type: 'global',
// ...

I'm not sure whether this is the intended behaviour.

For customizing referencer logic.

I want to customize escope's referencer logic in order to use ES7 features.
However, PatternVisitor is not exposed, so I cannot do.

Could escope export that?

Variable erroneously tagged as ImplicitGlobalVariable

This is probably a regression bug for the fix introduced in #21

Test script:

var escope = require("escope"),
    esprima = require("esprima");

var src = require("fs").readFileSync("source.js");

var ast = esprima.parse(src),
    scopes = escope.analyze(ast).scopes;

console.dir(scopes[0].variables[0].defs.map(function (def) {
    return def.type
}));

Input source:

var a; a = 42;

Output with escope 0.14.0:
[ 'Variable' ]

Output with escope 0.15.0 and 0.16.0:
[ 'Variable', 'ImplicitGlobalVariable' ]

References not counting vars without init expressions

Perhaps I'm just not understanding the documentation on references and through, but the following:

var y = 2; var u; var z;
  1. Correctly produces y, u, and z for the variables.
  2. (Incorrectly?) only counts y in the references
  3. (Incorrectly?) adds y to the "through".

If I change "var u" to "var u = 2", all of a sudden u shows up both in references and through.

Variable leaking into global scope

Consider the following source:

function a() {
    var x;
    function b() {
        x = 42;
    }
}

When I run escope via this script:

var escope = require("escope"),
    esprima = require("esprima");

var src = require("fs").readFileSync("source.js");

var ast = esprima.parse(src),
    scopes = escope.analyze(ast).scopes;

// dump names of variables in the global scope
console.dir(scopes[0].variables.map(function (variable) {
    return variable.name
}));

I get the following outpout: ["a", "x"]

Interestingly, if I change x = 42 to x() or simply x, it does not appear in the output.

Am I using escope incorrectly or why is x in the global scope?

add function to Scope.prototype that tests whether a fresh name is safe to use

Something like this:

Scope.prototype.isUnused = function(name){
    if(this.set.has(name)) return false;
    for (var i = 0, iz = this.through.length; i < iz; ++i) {
        if (this.through[i].identifier.name === name) {
            return false;
        }
    }
    return true;
};

I needed to implement something like this in michaelficarra/brushtail@532d09f2dde8d7b1686c739154c226f0d0217dd1

I figured it was more appropriate to pull this into escope. An alternative would be a function that takes a name-generating function (which takes a seed) and runs it until the name is safe to use.

safeName = Scope.helpMeGenerateASafeName(function iWillGenerateNames(attemptNumber){
  return "somePrefixThatILike" + Array(attemptNumber + 1).join("$");
});

edit: Updated first code sample, boolean logic was backwards.

Incremental scope generation

For ESLint, it would be awesome if we could have incremental scope generation. What I mean is that we are already doing a traversal, but right now we have to do at least two: one for escope and then one for what ESLint is doing. If there was a way to hook escope into using the traversal that ESLint is doing, it would dramatically decrease the running time.

Is this possible?

SwitchStatement scope not being returned with acquire()

I'm not sure if this is a bug or not, but this behavior is unexpected. If you can let me know if it's a bug, I can try to submit a patch.

If I call this in 3.0.0:

var scope = scopeManager.acquire(node);

And the node is a SwitchStatement, scope is undefined. However, there actually is a scope of type "switch" that I expected to be returned by this.

Is this behavior expected or a bug?

Add support for property references

For my task, I need to collect & pass minimum of data referenced by the given function from outer scope i.e. if it references globalObj.prop1 and globalObj.prop2 I'd like to get list of those props and not just globalObj.

Do you think it would be possible / easy to extend escope to support collection of such property references?

Rename global scope variable to currentScope

I think this would help with readability.
It's easy to mistake it with the local variable in certain prototype functions such as ScopeManager.prototype.__get or ScopeManager.prototype.release

Misses class references

The 2.0.5 version misses references to classes, such as:

class Shoe {
  constructor() {
    //Shoe.x = true;
  }
}
let s = new Shoe();

escope fails to identify new Shoe() as a reference.

Add ES6 Support

Supporting ES6 block scopes and some special scopes (such as arraw function and method)

[email protected] issues

I've just upgraded to EScope 3.0.0 for ESLint. Here are some issues I've encountered:

  • npm i escope@latest still pulls in 1.0.3 instead of 3.0.0, making it hard to know the latest available version
  • Unknown node type Super - The ESTree spec defined Super instead of SuperExpression

Otherwise, very smooth upgrade!

Escope incorrectly moves variables into global scope, from nested functions.

While parsing the following example:

function a() {
  var test = 0;
  (function() {
    test = test + 1;
  })();
}

Escope incorrectly moves "test" variables from anonymous function into global scope. Since anonymous function is called from within function a, and a has "test" variables declared, variable will not be in the global scope.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.