pax_global_header00006660000000000000000000000064121557564170014527gustar00rootroot0000000000000052 comment=4bd13e0b95aa0e3e025cc05010f1c216bd77c277 contextify-0.1.6/000077500000000000000000000000001215575641700137275ustar00rootroot00000000000000contextify-0.1.6/.gitignore000066400000000000000000000001561215575641700157210ustar00rootroot00000000000000.lock-wscript node_modules/ build/ *.swp *.swo TODO Makefile.gyp *.Makefile *.target.gyp.mk gyp-mac-tool out/ contextify-0.1.6/.npmignore000066400000000000000000000001641215575641700157270ustar00rootroot00000000000000Makefile .lock-wscript node_modules build *.swp *.swo TODO Makefile.gyp *.Makefile *.target.gyp.mk gyp-mac-tool out contextify-0.1.6/LICENSE.txt000066400000000000000000000020421215575641700155500ustar00rootroot00000000000000Copyright (c) 2011 Brian McDaniel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. contextify-0.1.6/Makefile000066400000000000000000000002261215575641700153670ustar00rootroot00000000000000all: build .PHONY: test clean clean: node-waf distclean build: src/contextify.cc node-waf distclean && node-waf configure build test: npm test contextify-0.1.6/README.md000066400000000000000000000104131215575641700152050ustar00rootroot00000000000000# Contextify For Windows issues, see here: https://github.com/brianmcd/contextify/wiki/Windows-Installation-Guide Please add to the wiki if you find new issues/solutions. Turn an object into a V8 execution context. A contextified object acts as the global 'this' when executing scripts in its context. Contextify adds 3 methods to the contextified object: run(code, filename), getGlobal(), and dispose(). The main difference between Contextify and Node's vm methods is that Contextify allows asynchronous functions to continue executing in the Contextified object's context. See vm vs. Contextify below for more discussion. ## Examples ```javascript var Contextify = require('contextify'); var sandbox = { console : console, prop1 : 'prop1'}; Contextify(sandbox); sandbox.run('console.log(prop1);'); sandbox.dispose(); // free the resources allocated for the context. ``` ```javascript var sandbox = Contextify(); // returns an empty contextified object. sandbox.run('var x = 3;'); console.log(sandbox.x); // prints 3 sandbox.dispose(); ``` ```javascript var sandbox = Contextify({setTimeout : setTimeout}); sandbox.run("setTimeout(function () { x = 3; }, 5);"); console.log(sandbox.x); // prints undefined setTimeout(function () { console.log(sandbox.x); // prints 3 sandbox.dispose(); }, 10); ``` ## Details **Contextify([sandbox])** sandbox - The object to contextify, which will be modified as described below If no sandbox is specified, an empty object will be allocated and used instead. Returns the contextified object. It doesn't make a copy, so if you already have a reference to the sandbox, you don't need to catch the return value. A Contextified object has 2 methods added to it: **run(code, [filename])** code - string containing JavaScript to execute filename - an optional filename for debugging. Runs the code in the Contextified object's context. **getGlobal()** Returns the actual global object for the V8 context. The global object is initialized with interceptors (discussed below) which forward accesses on it to the contextified object. This means the contextified object acts like the global object in most cases. Sometimes, though, you need to make a reference to the actual global object. For example: ```javascript var window = Contextify({console : console}); window.window = window; window.run("console.log(window === this);"); // prints false. ``` ```javascript var window = Contextify({console : console}); window.window = window.getGlobal(); window.run("console.log(window === this);"); // prints true ``` The global object returned by getGlobal() can be treated like the contextified sandbox object, except that defining getters/setters will not work on it. Define getters and setters on the actual sandbox object instead. **dispose()** Frees the memory allocated for the underlying V8 context. If you don't call this when you're done, the V8 context memory will leak, as will the sandbox memory, since the context's global stores a strong reference to the sandbox object. You can still use your sandbox object after calling dispose(), but it's unsafe to use a global previously returned from getGlobal(). run, getGlobal, and dispose will be removed from the sandbox object. ## Install npm install contextify ## require('vm') vs. Contextify Node's vm functions (runInContext etc) work by copying the values from the sandbox object onto a context's global object, executing the passed in script, then copying the results back. This means that scripts that create asynchronous functions (using mechanisms like setTimeout) won't have see the results of executing those functions, since the copying in/out only occurs during an explicit call to runInContext and friends. Contextify creates a V8 context, and uses interceptors (see: http://code.google.com/apis/v8/embed.html#interceptors) to forward global object accesses to the sandbox object. This means there is no copying in or out, so asynchronous functions have the expected effect on the sandbox object. ## Tests Testing is done with nodeunit. Run the tests with nodeunit test/ Output: OK: 92 assertions (27ms) ## Building node-waf configure build ## Acknowledgments Inspiration taken from Assaf's Zombie.js context solution: https://github.com/assaf/zombie contextify-0.1.6/binding.gyp000066400000000000000000000001571215575641700160650ustar00rootroot00000000000000{ 'targets': [ { 'target_name': 'contextify', 'sources': [ 'src/contextify.cc' ] } ] } contextify-0.1.6/changelog000066400000000000000000000027211215575641700156030ustar00rootroot000000000000000.1.6 * Fix broken build with node >= 0.11.0 (Jamie Kirkpatrick - @jpk) 0.1.5 * Fix broken builds on 0.9.11 and above (tomgco - #61) 0.1.4 * Fix segfault on Node >= 0.9.6. (pyokagan) * Allow pre-compiling scripts. (mroch) 0.1.3 * Remove PrintException. 0.1.2 * Renamed bindings.gyp to binding.gyp (Rex Morgan) * More fixes for OS X build. 0.1.1 * Add node-gyp build support (Nathan Rajlich) * Fix build for OS X (stolen from einaros's work on ws :)). * Better exception reporting (print error message and stack trace). 0.1.0 * Fix: #13 - Can't use global.eval as function. * Added [Named|Indexed]SecurityCallbacks to global ObjectTemplate. * No longer detaching the global proxy. * Refactored to use node::ObjectWrap. 0.0.7 * Fix: #11 - Declared global variables treated as undefined 0.0.6 * Fix: potential segfault when looking up properties on sandbox. 0.0.5 * Better error feedback when the module isn't built on the current node version. * All builds now build to the Release directory, regardless of Node version. 0.0.4 * Defend against calling Contextify methods on the global after dispose() has been called. * Fix: npm install fails on Node 0.5.x. 0.0.3 * Fix: segfault due to premature garbage collection of sandbox. * Added dispose() method to clean up context. 0.0.2 * Fix: memory leak due to creating unnecessary function instances. 0.0.1 * Initial release contextify-0.1.6/lib/000077500000000000000000000000001215575641700144755ustar00rootroot00000000000000contextify-0.1.6/lib/contextify.js000066400000000000000000000024061215575641700172310ustar00rootroot00000000000000var binding = require('bindings')('contextify'); var ContextifyContext = binding.ContextifyContext; var ContextifyScript = binding.ContextifyScript; function Contextify (sandbox) { if (typeof sandbox != 'object') { sandbox = {}; } var ctx = new ContextifyContext(sandbox); sandbox.run = function () { return ctx.run.apply(ctx, arguments); }; sandbox.getGlobal = function () { return ctx.getGlobal(); } sandbox.dispose = function () { sandbox.run = function () { throw new Error("Called run() after dispose()."); }; sandbox.getGlobal = function () { throw new Error("Called getGlobal() after dispose()."); }; sandbox.dispose = function () { throw new Error("Called dispose() after dispose()."); }; ctx = null; } return sandbox; } Contextify.createContext = function (sandbox) { if (typeof sandbox != 'object') { sandbox = {}; } return new ContextifyContext(sandbox); }; Contextify.createScript = function (code, filename) { if (typeof code != 'string') { throw new TypeError('Code argument is required'); } return new ContextifyScript(code, filename); }; module.exports = Contextify; contextify-0.1.6/package.json000066400000000000000000000015061215575641700162170ustar00rootroot00000000000000{ "name": "contextify", "version": "0.1.6", "description": "Turn an object into a persistent execution context.", "author": "Brian McDaniel ", "contributors": [ "Assaf Arkin (http://labnotes.org/)" ], "keywords": ["context","vm"], "repository": { "type" : "git", "url": "https://github.com/brianmcd/contextify.git" }, "main": "./lib/contextify", "scripts": { "test": "nodeunit test/" }, "engines": { "node": ">=0.4.0" }, "licenses": [ { "type" : "MIT", "url" : "http://github.com/brianmcd/contextify/blob/master/LICENSE.txt" } ], "dependencies": { "bindings" : "*" }, "devDependencies": { "nodeunit" : ">=0.5.x" } } contextify-0.1.6/src/000077500000000000000000000000001215575641700145165ustar00rootroot00000000000000contextify-0.1.6/src/contextify.cc000066400000000000000000000275131215575641700172310ustar00rootroot00000000000000#include "node.h" #include "node_version.h" #include using namespace v8; using namespace node; // For some reason this needs to be out of the object or node won't load the // library. static Persistent dataWrapperTmpl; static Persistent dataWrapperCtor; class ContextifyContext : ObjectWrap { public: Persistent context; Persistent sandbox; Persistent proxyGlobal; static Persistent jsTmpl; ContextifyContext(Local sbox) { HandleScope scope; sandbox = Persistent::New(sbox); } ~ContextifyContext() { context.Dispose(); context.Clear(); proxyGlobal.Dispose(); proxyGlobal.Clear(); sandbox.Dispose(); sandbox.Clear(); } // We override ObjectWrap::Wrap so that we can create our context after // we have a reference to our "host" JavaScript object. If we try to use // handle_ in the ContextifyContext constructor, it will be empty since it's // set in ObjectWrap::Wrap. inline void Wrap(Handle handle) { ObjectWrap::Wrap(handle); context = createV8Context(); proxyGlobal = Persistent::New(context->Global()); } // This is an object that just keeps an internal pointer to this // ContextifyContext. It's passed to the NamedPropertyHandler. If we // pass the main JavaScript context object we're embedded in, then the // NamedPropertyHandler will store a reference to it forever and keep it // from getting gc'd. Local createDataWrapper () { HandleScope scope; Local wrapper = dataWrapperCtor->NewInstance(); #if NODE_MAJOR_VERSION > 0 || (NODE_MINOR_VERSION == 9 && (NODE_PATCH_VERSION >= 6 && NODE_PATCH_VERSION <= 10)) || NODE_MINOR_VERSION >= 11 wrapper->SetAlignedPointerInInternalField(0, this); #else wrapper->SetPointerInInternalField(0, this); #endif return scope.Close(wrapper); } Persistent createV8Context() { HandleScope scope; Local ftmpl = FunctionTemplate::New(); ftmpl->SetHiddenPrototype(true); ftmpl->SetClassName(sandbox->GetConstructorName()); Local otmpl = ftmpl->InstanceTemplate(); otmpl->SetNamedPropertyHandler(GlobalPropertyGetter, GlobalPropertySetter, GlobalPropertyQuery, GlobalPropertyDeleter, GlobalPropertyEnumerator, createDataWrapper()); otmpl->SetAccessCheckCallbacks(GlobalPropertyNamedAccessCheck, GlobalPropertyIndexedAccessCheck); return Context::New(NULL, otmpl); } static void Init(Handle target) { HandleScope scope; dataWrapperTmpl = Persistent::New(FunctionTemplate::New()); dataWrapperTmpl->InstanceTemplate()->SetInternalFieldCount(1); dataWrapperCtor = Persistent::New(dataWrapperTmpl->GetFunction()); jsTmpl = Persistent::New(FunctionTemplate::New(New)); jsTmpl->InstanceTemplate()->SetInternalFieldCount(1); jsTmpl->SetClassName(String::NewSymbol("ContextifyContext")); NODE_SET_PROTOTYPE_METHOD(jsTmpl, "run", ContextifyContext::Run); NODE_SET_PROTOTYPE_METHOD(jsTmpl, "getGlobal", ContextifyContext::GetGlobal); target->Set(String::NewSymbol("ContextifyContext"), jsTmpl->GetFunction()); } // args[0] = the sandbox object static Handle New(const Arguments& args) { HandleScope scope; if (args.Length() < 1) { Local msg = String::New("Wrong number of arguments passed to ContextifyContext constructor"); return ThrowException(Exception::Error(msg)); } if (!args[0]->IsObject()) { Local msg = String::New("Argument to ContextifyContext constructor must be an object."); return ThrowException(Exception::Error(msg)); } ContextifyContext* ctx = new ContextifyContext(args[0]->ToObject()); ctx->Wrap(args.This()); return args.This(); } static Handle Run(const Arguments& args) { HandleScope scope; if (args.Length() == 0) { Local msg = String::New("Must supply at least 1 argument to run"); return ThrowException(Exception::Error(msg)); } if (!args[0]->IsString()) { Local msg = String::New("First argument to run must be a String."); return ThrowException(Exception::Error(msg)); } ContextifyContext* ctx = ObjectWrap::Unwrap(args.This()); Persistent context = ctx->context; context->Enter(); Local code = args[0]->ToString(); TryCatch trycatch; Handle