emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master 7c8e32c 062/271: Restore microooptimized, add more benchma


From: Jackson Ray Hamilton
Subject: [elpa] master 7c8e32c 062/271: Restore microooptimized, add more benchmarks, add tests.
Date: Thu, 05 Feb 2015 18:29:47 +0000

branch: master
commit 7c8e32c2ab1da09dbd82a9a7f395c829022766dc
Author: Jackson Ray Hamilton <address@hidden>
Commit: Jackson Ray Hamilton <address@hidden>

    Restore microooptimized, add more benchmarks, add tests.
---
 benchmark/scenarios.js      |   35 +++---
 scopifier-microoptimized.js |  123 ++++++++++++++++++++
 test/.jslintrc              |    4 +
 test/fixtures/monad.js      |   12 --
 test/fixtures/vow.js        |  270 +++++++++++++++++++++++++++++++++++++++++++
 test/fixtures/vow.json      |    1 +
 test/scopifier.js           |   11 --
 test/specs.js               |   53 +++++++++
 8 files changed, 469 insertions(+), 40 deletions(-)

diff --git a/benchmark/scenarios.js b/benchmark/scenarios.js
index f2fa386..879da5c 100644
--- a/benchmark/scenarios.js
+++ b/benchmark/scenarios.js
@@ -3,6 +3,7 @@
 var fs = require('fs'),
     path = require('path'),
     scopifier = require('../scopifier'),
+    scopifierMicrooptimized = require('../scopifier-microoptimized'),
 
     jqueryPath = path.join(__dirname, 'fixtures', 'jquery-2.1.1.js'),
     lodashPath = path.join(__dirname, 'fixtures', 'lodash-2.4.1.js'),
@@ -19,21 +20,18 @@ suite('scopifier', function () {
             next(error);
         });
     });
-
     before(function (next) {
         fs.readFile(lodashPath, 'utf8', function (error, contents) {
             lodash = contents;
             next(error);
         });
     });
-
     before(function (next) {
         fs.readFile(asyncPath, 'utf8', function (error, contents) {
             async = contents;
             next(error);
         });
     });
-
     before(function (next) {
         fs.readFile(mkdirpPath, 'utf8', function (error, contents) {
             mkdirp = contents;
@@ -41,20 +39,23 @@ suite('scopifier', function () {
         });
     });
 
-    bench('jquery', function () {
-        scopifier(jquery);
-    });
-
-    bench('lodash', function () {
-        scopifier(lodash);
-    });
-
-    bench('async', function () {
-        scopifier(async);
-    });
-
-    bench('mkdirp', function () {
-        scopifier(mkdirp);
+    [scopifier, scopifierMicrooptimized].forEach(function (scopifier, index) {
+        var message = '';
+        if (index === 1) {
+            message = ' (microoptimized)';
+        }
+        bench('jquery' + message, function () {
+            scopifier(jquery);
+        });
+        bench('lodash' + message, function () {
+            scopifier(lodash);
+        });
+        bench('async' + message, function () {
+            scopifier(async);
+        });
+        bench('mkdirp' + message, function () {
+            scopifier(mkdirp);
+        });
     });
 
 });
diff --git a/scopifier-microoptimized.js b/scopifier-microoptimized.js
new file mode 100644
index 0000000..1ef067d
--- /dev/null
+++ b/scopifier-microoptimized.js
@@ -0,0 +1,123 @@
+'use strict';
+
+var escope = require('escope'),
+    esprima = require('esprima');
+
+// Given code, returns an array of `[level, start, end]' tokens for
+// context-coloring.
+module.exports = function (code) {
+    var analyzedScopes,
+        ast,
+        comment,
+        comments,
+        definition,
+        definitions,
+        i,
+        isDefined,
+        j,
+        k,
+        mappedDefinitions,
+        range,
+        reference,
+        references,
+        scope,
+        scopes = [],
+        symbols = [],
+        variable;
+
+    // Gracefully handle parse errors by doing nothing.
+    try {
+        ast = esprima.parse(code, {
+            comment: true,
+            range: true
+        });
+        analyzedScopes = escope.analyze(ast).scopes;
+    } catch (error) {
+        process.exit(1);
+    }
+
+    for (i = 0; i < analyzedScopes.length; i += 1) {
+        scope = analyzedScopes[i];
+        // Having its level set implies it was already annotated.
+        if (scope.level === undefined) {
+            if (scope.upper) {
+                if (scope.upper.functionExpressionScope) {
+                    // Pretend function expression scope doesn't exist.
+                    scope.level = scope.upper.level;
+                    scope.variables = 
scope.upper.variables.concat(scope.variables);
+                } else {
+                    scope.level = scope.upper.level + 1;
+                }
+            } else {
+                // Base case.
+                scope.level = 0;
+            }
+            // We've only given the scope a level for posterity's sake. We're
+            // done now.
+            if (!scope.functionExpressionScope) {
+                range = scope.block.range;
+                scopes.push([
+                    scope.level,
+                    range[0] + 1,
+                    range[1] + 1
+                ]);
+                definitions = [];
+                for (j = 0; j < scope.variables.length; j += 1) {
+                    variable = scope.variables[j];
+                    mappedDefinitions = [];
+                    for (k = 0; k < variable.defs.length; k += 1) {
+                        definition = variable.defs[k];
+                        range = definition.name.range;
+                        mappedDefinitions.push([
+                            scope.level,
+                            range[0] + 1,
+                            range[1] + 1
+                        ]);
+                    }
+                    Array.prototype.push.apply(definitions, mappedDefinitions);
+                }
+                references = [];
+                for (j = 0; j < scope.references.length; j += 1) {
+                    reference = scope.references[j];
+                    range = reference.identifier.range;
+                    isDefined = false;
+                    // Determine if a definition already exists for the
+                    // range. (escope detects variables twice if they are
+                    // declared and initialized simultaneously; this filters
+                    // them.)
+                    for (k = 0; k < definitions.length; k += 1) {
+                        definition = definitions[k];
+                        if (definition[1] === range[0] + 1 &&
+                                definition[2] === range[1] + 1) {
+                            isDefined = true;
+                            break;
+                        }
+                    }
+                    if (!isDefined) {
+                        references.push([
+                            // Handle global references too.
+                            reference.resolved ? 
reference.resolved.scope.level : 0,
+                            range[0] + 1,
+                            range[1] + 1
+                        ]);
+                    }
+                }
+                Array.prototype.push.apply(symbols, definitions);
+                Array.prototype.push.apply(symbols, references);
+            }
+        }
+    }
+
+    comments = [];
+    for (i = 0; i < ast.comments.length; i += 1) {
+        comment = ast.comments[i];
+        range = comment.range;
+        comments.push([
+            -1,
+            range[0] + 1,
+            range[1] + 1
+        ]);
+    }
+
+    return scopes.concat(symbols).concat(comments);
+};
diff --git a/test/.jslintrc b/test/.jslintrc
index 1202114..539f357 100644
--- a/test/.jslintrc
+++ b/test/.jslintrc
@@ -1,5 +1,9 @@
 {
   "predef": [
+    "after",
+    "afterEach",
+    "before",
+    "beforeEach",
     "describe",
     "it"
   ]
diff --git a/test/fixtures/monad.js b/test/fixtures/monad.js
deleted file mode 100644
index 6366fad..0000000
--- a/test/fixtures/monad.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/* A monad. */
-function MONAD() {
-    abc = 3;
-    return function unit(value) {
-        // Some details.
-        var monad = Object.create(null);
-        monad.bind = function (func) {
-            return func(value);
-        };
-        return monad;
-    };
-}
diff --git a/test/fixtures/vow.js b/test/fixtures/vow.js
new file mode 100644
index 0000000..374853e
--- /dev/null
+++ b/test/fixtures/vow.js
@@ -0,0 +1,270 @@
+// vow.js
+// Douglas Crockford
+// 2013-09-20
+
+// Public Domain
+
+/*global setImmediate */
+
+
+var VOW = (function () {
+    'use strict';
+
+// The VOW object contains a .make function that is used to make vows.
+// It may also contain other useful functions.
+// In some mythologies, 'VOW' is called 'deferrer'.
+
+    function enlighten(queue, fate) {
+
+// enlighten is a helper function of herald and .when. It schedules the
+// processing of all of the resolution functions in either the keepers queue
+// or the breakers queue in later turns with the promise's fate.
+
+        queue.forEach(function (func) {
+            setImmediate(func, fate);
+        });
+    }
+
+    return {
+        make: function make() {
+
+// The make function makes new vows. A vow contains a promise object and the
+// two resolution functions (break and keep) that determine the fate of the
+// promise.
+
+            var breakers = [],          // .when's broken queue
+                fate,                   // The promise's ultimate value
+                keepers = [],           // .when's kept queue
+                status = 'pending';     // 'broken', 'kept', or 'pending'
+
+            function enqueue(
+                resolution, // 'keep' or 'break'
+                func,       // A function that was registered with .when
+                vow         // A vow that provides the resolution functions
+            ) {
+
+// enqueue is a helper function used by .when. It will append a function to
+// either the keepers queue or the breakers queue.
+
+                var queue = resolution === 'keep' ? keepers : breakers;
+                queue[queue.length] = typeof func !== 'function'
+
+// If func is not a function, push the resolver so that the value passes to
+// the next cascaded .when.
+
+                    ? vow[resolution]
+
+// If the func is a function, push a function that calls func with a value.
+// The result can be a promise, or not a promise, or an exception.
+
+                    : function (value) {
+                        try {
+                            var result = func(value);
+
+// If the result is a promise, then register our resolver with that promise.
+
+                            if (result && result.is_promise === true) {
+                                result.when(vow.keep, vow.break);
+
+// But if it is not a promise, then use the result to resolve our promise.
+
+                            } else {
+                                vow.keep(result);
+                            }
+
+// But if func throws an exception, then break our promise.
+
+                        } catch (e) {
+                            vow.break(e);
+                        }
+                    };
+            }
+
+            function herald(state, value, queue) {
+
+// The herald function is a helper function of break and keep.
+// It seals the promise's fate, updates its status, enlightens
+// one of the queues, and empties both queues.
+
+                if (status !== 'pending') {
+                    throw "overpromise";
+                }
+                fate = value;
+                status = state;
+                enlighten(queue, fate);
+                keepers.length = 0;
+                breakers.length = 0;
+            }
+
+// Construct and return the vow object.
+
+            return {
+                'break': function (value) {
+
+// The break method breaks the promise.
+
+                    herald('broken', value, breakers);
+                },
+                keep: function keep(value) {
+
+// The keep method keeps the promise.
+
+                    herald('kept', value, keepers);
+                },
+                promise: {
+
+// The promise is an object with a .when method.
+
+                    is_promise: true,
+
+// The .when method is the promise monad's bind. The .when method can take two
+// optional functions. One of those functions may be called, depending on the
+// promise's resolution. Both could be called if the the kept function throws.
+
+                    when: function (kept, broken) {
+
+// Make a new vow. Return the new promise.
+
+                        var vow = make();
+                        switch (status) {
+
+// If this promise is still pending, then enqueue both kept and broken.
+
+                        case 'pending':
+                            enqueue('keep',  kept,   vow);
+                            enqueue('break', broken, vow);
+                            break;
+
+// If the promise has already been kept, then enqueue only the kept function,
+// and enlighten it.
+
+                        case 'kept':
+                            enqueue('keep', kept, vow);
+                            enlighten(keepers, fate);
+                            break;
+
+// If the promise has already been broken, then enqueue only the broken
+// function, and enlighten it.
+
+                        case 'broken':
+                            enqueue('break', broken, vow);
+                            enlighten(breakers, fate);
+                            break;
+                        }
+                        return vow.promise;
+                    }
+                }
+            };
+        },
+        every: function every(array) {
+
+// The every function takes an array of promises and returns a promise that
+// will deliver an array of results only if every promise is kept.
+
+            var remaining = array.length, results = [], vow = VOW.make();
+
+            if (!remaining) {
+                vow.break(array);
+            } else {
+                array.forEach(function (promise, i) {
+                    promise.when(function (value) {
+                        results[i] = value;
+                        remaining -= 1;
+                        if (remaining === 0) {
+                            vow.keep(results);
+                        }
+                    }, function (reason) {
+                        remaining = NaN;
+                        vow.break(reason);
+                    });
+                });
+            }
+            return vow.promise;
+        },
+        first: function first(array) {
+
+// The first function takes an array of promises and returns a promise to
+// deliver the first observed kept promise, or a broken promise if all of
+// the promises are broken.
+
+            var found = false, remaining = array.length, vow = VOW.make();
+
+            function check() {
+                remaining -= 1;
+                if (remaining === 0 && !found) {
+                    vow.break();
+                }
+            }
+
+            if (remaining === 0) {
+                vow.break(array);
+            } else {
+                array.forEach(function (promise) {
+                    promise.when(function (value) {
+                        if (!found) {
+                            found = true;
+                            vow.keep(value);
+                        }
+                        check();
+                    }, check);
+                });
+            }
+            return vow.promise;
+        },
+        any: function any(array) {
+
+// The any function takes an array of promises and returns a promise that
+// will deliver a possibly sparse array of results of any kept promises.
+// The result will contain an undefined element for each broken promise.
+
+            var remaining = array.length, results = [], vow = VOW.make();
+
+            function check() {
+                remaining -= 1;
+                if (remaining === 0) {
+                    vow.keep(results);
+                }
+            }
+
+            if (!remaining) {
+                vow.keep(results);
+            } else {
+                array.forEach(function (promise, i) {
+                    promise.when(function (value) {
+                        results[i] = value;
+                        check();
+                    }, check);
+                });
+            }
+            return vow.promise;
+        },
+        kept: function (value) {
+
+// Returns a new kept promise.
+
+            var vow = VOW.make();
+            vow.keep(value);
+            return vow.promise;
+        },
+        broken: function (reason) {
+
+// Returns a new broken promise.
+
+            var vow = VOW.make();
+            vow.break(reason);
+            return vow.promise;
+        }
+    };
+}());
+
+
+// If your system does not have setImmediate, then simulate it with setTimeout.
+
+if (typeof setImmediate !== 'function') {
+    setImmediate = function setImmediate(func, param) {
+        'use strict';
+        return setTimeout(function () {
+            func(param);
+        }, 0);
+    };
+}
diff --git a/test/fixtures/vow.json b/test/fixtures/vow.json
new file mode 100644
index 0000000..8eb2d6d
--- /dev/null
+++ b/test/fixtures/vow.json
@@ -0,0 +1 @@
+[[0,92,8470],[1,103,8174],[2,311,656],[3,583,648],[2,685,5093],[3,1155,2682],[4,1964,2667],[5,2566,2645],[3,2696,3201],[3,3290,3423],[3,3447,3579],[3,3961,5050],[2,5110,5975],[3,5469,5917],[4,5526,5770],[4,5772,5897],[2,5992,6931],[3,6282,6446],[3,6568,6873],[4,6622,6846],[2,6946,7814],[3,7277,7437],[3,7555,7756],[4,7612,7729],[2,7830,7986],[2,8004,8165],[1,8323,8467],[2,8408,8456],[0,96,99],[0,8273,8285],[0,8308,8320],[1,320,329],[2,330,335],[2,337,341],[2,569,574],[3,593,597],[0,613,62
 [...]
diff --git a/test/scopifier.js b/test/scopifier.js
deleted file mode 100644
index 3f87ef4..0000000
--- a/test/scopifier.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-var assert = require('assert');
-
-describe('scopifier', function () {
-
-    it('should work', function () {
-        assert.strictEqual(true, true);
-    });
-
-});
diff --git a/test/specs.js b/test/specs.js
new file mode 100644
index 0000000..c015ddb
--- /dev/null
+++ b/test/specs.js
@@ -0,0 +1,53 @@
+'use strict';
+
+var assert = require('assert'),
+    fs = require('fs'),
+    path = require('path'),
+
+    scopifier = require('../scopifier'),
+    scopifierMicrooptimized = require('../scopifier-microoptimized'),
+
+    inputPath = path.join(__dirname, 'fixtures', 'vow.js'),
+    outputPath = path.join(__dirname, 'fixtures', 'vow.json');
+
+describe('scopifier', function () {
+
+    var input, output;
+
+    before(function (done) {
+        fs.readFile(inputPath, 'utf8', function (error, contents) {
+            if (error) {
+                done(error);
+                return;
+            }
+            input = contents;
+            done();
+        });
+    });
+
+    before(function (done) {
+        fs.readFile(outputPath, 'utf8', function (readError, contents) {
+            if (readError) {
+                done(readError);
+                return;
+            }
+            try {
+                output = JSON.parse(contents);
+            } catch (parseError) {
+                done(parseError);
+            }
+            done();
+        });
+    });
+
+    [scopifier, scopifierMicrooptimized].forEach(function (scopifier, index) {
+        var message = '';
+        if (index === 1) {
+            message = ' (microoptimized)';
+        }
+        it('should work' + message, function () {
+            assert.deepEqual(scopifier(input), output);
+        });
+    });
+
+});



reply via email to

[Prev in Thread] Current Thread [Next in Thread]