vm-shim v0.0.6
vm-shim
This began as a wan attempt to reproduce/polyfill/infill the Node.JS
vm#runIn<Some?>Context() methods in browsers. It has transformed
into the present tan muscular self-assured and smiling project before you.
I'd wanted to show that shimming vm in the browser really could be done
directly, partly to avoid iframes (which
vm-browserify uses) to create and
clone globals and contexts, and partly to side-step Node.js's
contra-normative implementations of runInContext methods.
It's actually a "why didn't I think of that?" solution to problems such as -
- why don't
vmmethods accept functions as arguments, not just strings? - why don't
eval()andFunction()accept functions as arguments? - why do
eval()andFunction()leak un-var'd symbols to the global scope, in browser & node.js environments? - how can we inspect items defined in closures?
- how can we override (or mock) them?
methods provided (so far)
vm#runInContext(code, context)vm#runInNewContext(code, context)vm#runInThisContext(code)
not provided (yet)
vm#.createContextvm#.createScriptscript.runInThisContext()script.runInNewContext([sandbox])
install
npm install vm-shim
git clone https://github.com/dfkaye/vm-shim.git
implementation
Starting with vm.runInContext(code, context), the Function() constructor is
at the core. The code param may be either a string or a function. The
context param is a simple object with key-value mappings. For any key on the
context, a new var for that key is prefixed to the code. The code is passed
in to Function() so that the keynames can't leak outside the new function's
scope.
Refactored 8 Nov 2013: a lot of little things involved - biggest is that
runInThisContext now uses eval() internally, and the other two use with()
inside of Function(). Who says you can't use with()?
10 Nov Having discovered that eval() leaks globals (!?!) if symbols are not
var'd, all methods rely on a helper method to scrape EVERY global added by its
internal eval() (or Function()) call.
10 Dec: removed use of with.
example tests
The unit tests demonstrate how runInContext and runInNewContext methods work
by passing a context param containing a reference to the test's expectation
object or function.
Example runInContext test passes the expect function via context argument:
it("overrides external scope vars with context attrs", function() {
var attr = "shouldn't see this";
var context = {
attr: 'ok',
expect: expect // <-- pass expect here
};
vm.runInContext(function(){
expect(attr).toBe('ok');
expect(attr).not.toBe('should not see this');
}, context);
});Example runInNewContext test to verify context is returned:
it('should return context object', function () {
var context = { name: 'test' };
var result = vm.runInNewContext('', context);
expect(result).toBe(context);
expect(result.name).toBe('test');
});Example runInThisContext test to verify accidental is not placed on global scope:
it("should not leak accidental (un-var'd) globals", function() {
vm.runInThisContext(function(){
accidental = 'defined';
});
expect(global.accidental).not.toBeDefined();
});node tests
Using Misko Hevery's jasmine-node to run command line tests on node (even though this project initially aimed at a browser shim).
The package.json file defines three test script commands to run the tests via
jasmine-node without the browsers:
npm test
# => jasmine-node --verbose ./test/suite.spec.js
npm run test-vm
# => jasmine-node --verbose ./test/vm-shim.spec.jsbrowser tests
Using @pivotallabs' jasmine-2.0.0 for the browser suite.
The jasmine2 browser test page is viewable on rawgit.
Using Toby Ho's MAGNIFICENT testemjs to
drive tests in multiple browsers for jasmine-2.0.0 (see how to
hack testem for jasmine 2), as well
as jasmine-node. The testem.json file uses the standalone test page above,
and also uses a custom launcher for jasmine-node (v 1.3.1).
View both test types at the console by running:
testem -l jhistory
Just noting for the record:
- Original idea emerged late at night 17 SEPT 2013
- First implemented with rawgit approach 18 SEPT,
- Full success including objects as properties of the context argument 19 SEPT.
- Breaking the usual TDD procedure:
- Started with console statements and prayer ~ removed both for real unit tests
- Tape tests added 20 SEPT.
- Jasmine tests/page added 20 SEPT.
- Error, and leakage tests added 21 SEPT.
- runInNewContext, runInThisContext methods added; runInContext refactored 4 OCT 2013
- CoffeeScript test with jasmine-node added 6 OCT
- tape test written in CoffeeScript test added 7 OCT
- scope injection tests started 21 OCT
- scope injection: spec started, tests updated, testem.json added 6 NOV 2013
- massive refactoring 8 NOV 2013
- certain cases were just wrong (needed 'eval()' for 'runInThisContext()', e.g.)
- new/completed bdd specs for both vm-shim and scope mocking (temp name is 'mockScope')
- last global leakage fixed 10 NOV
- deleted CoffeeScript and tape tests (fun but extra work for now) 11 NOV
- rawgit-viewable test page that also works with testem 12 NOV
- moved mock scope stuff to metafunction project 18 NOV
