Skip to content
This repository has been archived by the owner on Jan 13, 2022. It is now read-only.

Latest commit

 

History

History
140 lines (107 loc) · 4.65 KB

README.mdown

File metadata and controls

140 lines (107 loc) · 4.65 KB

jsgrep: a syntactically-aware grep for JavaScript

Jsgrep is program that searches for a particular JavaScript pattern using the abstract syntax tree (AST) of the program. This enables matching expressions based on their JavaScript meaning, rather than based on simple strings.

Examples

Find calls to window.setTimeout with a 0 timeout

$ jsgrep 'setTimeout(A, 0)' tests/*.js
tests/jquery.js:        setTimeout( function() {
tests/jquery.js:    setTimeout( clearFxNow, 0 );

Jsgrep uses metavariables as wildcards. Metavariables match any valid JavaScript chunk, so in this case, the first match was an entire inline function. In both cases, the second parameter to setTimeout was a literal value of 0.

Find value defaulting

$ jsgrep "A = A || B;" tests/*.js
tests/jquery.js:            args = args || [];
tests/jquery.js:    dataType = dataType || options.dataTypes[ 0 ];

When the pattern references the same metavariable multiple times, jsgrep ensures that the value of the metavariable is the same throughout the match.

Find the names of all invoked events

$ ./jsgrep -p B "A.fire(B, ...)" tests/*.js
tests/connect.js: 'auth.logout'
tests/connect.js: 'auth.login'

The ... operator matches 0 or more expressions in function calls and array initializers. This example also uses the -p flag to print only a particular matched variable.

Find classes that have a 'path' property

$ jsgrep -p C "JX.install(C, { properties: { path: X } })" tests/*.js
tests/javelin.js: 'Event'
tests/javelin.js: 'URI'

Jsgrep allows you to search object initializations partially, which enables easily drilling into the structure of JavaScript classes.

jspatch

Jsgrep allows you to identify source code that resembles a certain pattern. Jspatch is a tool to take that information and programmatically modify the original source code.

Replace setTimeout with window.setTimeout

$ jspatch -e 's/setTimeout( A, B )/window.setTimeout( A, B )/' tests/jquery.js
--- tests/jquery.js 2012-01-10 14:51:36.000000000 -0800
+++ /tmp/jspatch.jquery.js  2012-01-11 17:13:56.000000000 -0800
@@ -420,7 +420,7 @@
        if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
            // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
            if ( !document.body ) {
-               return setTimeout( jQuery.ready, 1 );
+               return window.setTimeout( jQuery.ready, 1 );
            }

            // Remember that the DOM is ready
@@ -1987,7 +1987,7 @@
        ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
        // Give room for hard-coded callbacks to fire first
        // and eventually mark/queue something else on the element
-       setTimeout( function() {
+       window.setTimeout( function() {
            if ( !jQuery._data( elem, queueDataKey ) &&
                !jQuery._data( elem, markDataKey ) ) {
                jQuery.removeData( elem, deferDataKey, true );

Sed mode allows you to quickly perform simple substitutions on the source code.

Refactor properties/members into just properties

$ cat tests/transform.spatch
 JX.install(N, {
   ...,
-  properties : {
+  members : {
     ...
+    ,
-  },
-  members : {
     ...
   },
   ...
 })

$ jspatch -f tests/transform.spatch tests/javelin.js
--- tests/javelin.js    2012-01-08 10:55:04.000000000 -0800
+++ /tmp/jspatch.javelin.js 2012-01-12 03:06:45.000000000 -0800
@@ -3615,16 +3615,14 @@
     }
   },

-  properties : {
+  members : {
     protocol: undefined,
     port: undefined,
     path: undefined,
     queryParams: undefined,
     fragment: undefined,
-    querySerializer: undefined
-  },
-
-  members : {
+    querySerializer: undefined,
+
     _domain: undefined,

     /**

This complex spatch file leverages the fact that properties and members are always adjacent to one another in the source. This could be verified using jsgrep to ensure that no cases were missed.

TODO

  • Consider a metavar that matches but doesn't save, to prevent ambiguities.
  • Rewrite forEachNode to be iterative rather than recursive (to fix ...)
  • Consider support for --where/--eval and/or not-patterns
  • Support for most statements in patterns
  • Formally testing the pattern matchers

jspatch:

  • Testing

Contributors

Jsgrep was written by Ryan Patterson at Facebook. Inspiration for this project draws heavily from pfff, a source code analyzer written at Facebook.