mirror of https://github.com/lukechilds/node.git
Browse Source
As documented in #3042 and in [1], the existing vm implementation has
many problems. All of these are solved by @brianmcd's [contextify][2]
package. This commit uses contextify as a conceptual base and its code
core to overhaul the vm module and fix its many edge cases and caveats.
Functionally, this fixes #3042. In particular:
- A context is now indistinguishable from the object it is based on
(the "sandbox"). A context is simply a sandbox that has been marked
by the vm module, via `vm.createContext`, with special internal
information that allows scripts to be run inside of it.
- Consequently, items added to the context from anywhere are
immediately visible to all code that can access that context, both
inside and outside the virtual machine.
This commit also smooths over the API very slightly:
- Parameter defaults are now uniformly triggered via `undefined`, per
ES6 semantics and previous discussion at [3].
- Several undocumented and problematic features have been removed, e.g.
the conflation of `vm.Script` with `vm` itself, and the fact that
`Script` instances also had all static `vm` methods. The API is now
exactly as documented (although arguably the existence of the
`vm.Script` export is not yet documented, just the `Script` class
itself).
In terms of implementation, this replaces node_script.cc with
node_contextify.cc, which is derived originally from [4] (see [5]) but
has since undergone extensive modifications and iterations to expose
the most useful C++ API and use the coding conventions and utilities of
Node core.
The bindings exposed by `process.binding('contextify')`
(node_contextify.cc) replace those formerly exposed by
`process.binding('evals')` (node_script.cc). They are:
- ContextifyScript(code, [filename]), with methods:
- runInThisContext()
- runInContext(sandbox, [timeout])
- makeContext(sandbox)
From this, the vm.js file builds the entire documented vm module API.
node.js and module.js were modified to use this new native binding, or
the vm module itself where possible. This introduces an extra line or
two into the stack traces of module compilation (and thus into most
stack traces), explaining the changed tests.
The tests were also updated slightly, with all vm-related simple tests
consolidated as test/simple/test-vm-* (some of them were formerly
test/simple/test-script-*). At the same time they switched from
`common.debug` to `console.error` and were updated to use
`assert.throws` instead of rolling their own error-testing methods.
New tests were also added, of course, demonstrating the new
capabilities and fixes.
[1]: http://nodejs.org/docs/v0.10.16/api/vm.html#vm_caveats
[2]: https://github.com/brianmcd/contextify
[3]: https://github.com/joyent/node/issues/5323#issuecomment-20250726
[4]: bf123f3ef9/src/contextify.cc
[5]: https://gist.github.com/domenic/6068120
v0.11.7-release
Domenic Denicola
12 years ago
committed by
isaacs
26 changed files with 799 additions and 588 deletions
@ -0,0 +1,484 @@ |
|||
// Copyright Joyent, Inc. and other Node contributors.
|
|||
//
|
|||
// 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.
|
|||
|
|||
#include "node.h" |
|||
#include "node_internals.h" |
|||
#include "node_watchdog.h" |
|||
|
|||
namespace node { |
|||
|
|||
using v8::AccessType; |
|||
using v8::Array; |
|||
using v8::Boolean; |
|||
using v8::Context; |
|||
using v8::Function; |
|||
using v8::FunctionCallbackInfo; |
|||
using v8::FunctionTemplate; |
|||
using v8::HandleScope; |
|||
using v8::Integer; |
|||
using v8::Local; |
|||
using v8::None; |
|||
using v8::Object; |
|||
using v8::ObjectTemplate; |
|||
using v8::Persistent; |
|||
using v8::PropertyCallbackInfo; |
|||
using v8::Script; |
|||
using v8::String; |
|||
using v8::TryCatch; |
|||
using v8::Value; |
|||
using v8::V8; |
|||
|
|||
|
|||
class ContextifyContext : ObjectWrap { |
|||
private: |
|||
Persistent<Object> sandbox_; |
|||
Persistent<Object> proxy_global_; |
|||
static Persistent<FunctionTemplate> data_wrapper_tmpl; |
|||
static Persistent<Function> data_wrapper_ctor; |
|||
|
|||
public: |
|||
Persistent<Context> context_; |
|||
static Persistent<FunctionTemplate> js_tmpl; |
|||
|
|||
explicit ContextifyContext(Local<Object> sandbox) : |
|||
sandbox_(node_isolate, sandbox) { |
|||
} |
|||
|
|||
|
|||
~ContextifyContext() { |
|||
context_.Dispose(); |
|||
proxy_global_.Dispose(); |
|||
sandbox_.Dispose(); |
|||
} |
|||
|
|||
|
|||
// 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(Local<Object> handle) { |
|||
HandleScope scope(node_isolate); |
|||
ObjectWrap::Wrap(handle); |
|||
Local<Context> v8_context = CreateV8Context(); |
|||
context_.Reset(node_isolate, v8_context); |
|||
proxy_global_.Reset(node_isolate, v8_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<Value> CreateDataWrapper() { |
|||
HandleScope scope(node_isolate); |
|||
Local<Function> ctor = PersistentToLocal(node_isolate, data_wrapper_ctor); |
|||
Local<Object> wrapper = ctor->NewInstance(); |
|||
NODE_WRAP(wrapper, this); |
|||
return scope.Close(wrapper); |
|||
} |
|||
|
|||
|
|||
Local<Context> CreateV8Context() { |
|||
HandleScope scope(node_isolate); |
|||
Local<FunctionTemplate> function_template = FunctionTemplate::New(); |
|||
function_template->SetHiddenPrototype(true); |
|||
|
|||
Local<Object> sandbox = PersistentToLocal(node_isolate, sandbox_); |
|||
function_template->SetClassName(sandbox->GetConstructorName()); |
|||
|
|||
Local<ObjectTemplate> object_template = |
|||
function_template->InstanceTemplate(); |
|||
object_template->SetNamedPropertyHandler(GlobalPropertyGetterCallback, |
|||
GlobalPropertySetterCallback, |
|||
GlobalPropertyQueryCallback, |
|||
GlobalPropertyDeleterCallback, |
|||
GlobalPropertyEnumeratorCallback, |
|||
CreateDataWrapper()); |
|||
object_template->SetAccessCheckCallbacks(GlobalPropertyNamedAccessCheck, |
|||
GlobalPropertyIndexedAccessCheck); |
|||
return scope.Close(Context::New(node_isolate, NULL, object_template)); |
|||
} |
|||
|
|||
|
|||
static void Init(Local<Object> target) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
Local<FunctionTemplate> function_template = FunctionTemplate::New(); |
|||
function_template->InstanceTemplate()->SetInternalFieldCount(1); |
|||
data_wrapper_tmpl.Reset(node_isolate, function_template); |
|||
|
|||
Local<FunctionTemplate> lwrapper_tmpl = |
|||
PersistentToLocal(node_isolate, data_wrapper_tmpl); |
|||
data_wrapper_ctor.Reset(node_isolate, lwrapper_tmpl->GetFunction()); |
|||
|
|||
js_tmpl.Reset(node_isolate, FunctionTemplate::New(New)); |
|||
Local<FunctionTemplate> ljs_tmpl = PersistentToLocal(node_isolate, js_tmpl); |
|||
ljs_tmpl->InstanceTemplate()->SetInternalFieldCount(1); |
|||
|
|||
Local<String> class_name |
|||
= FIXED_ONE_BYTE_STRING(node_isolate, "ContextifyContext"); |
|||
ljs_tmpl->SetClassName(class_name); |
|||
target->Set(class_name, ljs_tmpl->GetFunction()); |
|||
|
|||
NODE_SET_METHOD(target, "makeContext", MakeContext); |
|||
} |
|||
|
|||
|
|||
// args[0] = the sandbox object
|
|||
static void New(const FunctionCallbackInfo<Value>& args) { |
|||
HandleScope scope(node_isolate); |
|||
if (!args[0]->IsObject()) { |
|||
return ThrowTypeError("sandbox argument must be an object."); |
|||
} |
|||
ContextifyContext* ctx = new ContextifyContext(args[0].As<Object>()); |
|||
ctx->Wrap(args.This()); |
|||
} |
|||
|
|||
|
|||
static void MakeContext(const FunctionCallbackInfo<Value>& args) { |
|||
Local<Object> sandbox = args[0].As<Object>(); |
|||
|
|||
Local<FunctionTemplate> ljs_tmpl = PersistentToLocal(node_isolate, js_tmpl); |
|||
Local<Value> constructor_args[] = { sandbox }; |
|||
Local<Object> contextify_context_object = |
|||
ljs_tmpl->GetFunction()->NewInstance(1, constructor_args); |
|||
|
|||
Local<String> hidden_name = |
|||
FIXED_ONE_BYTE_STRING(node_isolate, "_contextifyHidden"); |
|||
sandbox->SetHiddenValue(hidden_name, contextify_context_object); |
|||
} |
|||
|
|||
|
|||
static const Local<Context> ContextFromContextifiedSandbox( |
|||
const Local<Object>& sandbox) { |
|||
Local<String> hidden_name = |
|||
FIXED_ONE_BYTE_STRING(node_isolate, "_contextifyHidden"); |
|||
Local<Object> hidden_context = |
|||
sandbox->GetHiddenValue(hidden_name).As<Object>(); |
|||
|
|||
if (hidden_context.IsEmpty()) { |
|||
ThrowTypeError("sandbox argument must have been converted to a context."); |
|||
return Local<Context>(); |
|||
} |
|||
|
|||
ContextifyContext* ctx = |
|||
ObjectWrap::Unwrap<ContextifyContext>(hidden_context); |
|||
Persistent<Context> context; |
|||
context.Reset(node_isolate, ctx->context_); |
|||
return PersistentToLocal(node_isolate, context); |
|||
} |
|||
|
|||
|
|||
static bool GlobalPropertyNamedAccessCheck(Local<Object> host, |
|||
Local<Value> key, |
|||
AccessType type, |
|||
Local<Value> data) { |
|||
return true; |
|||
} |
|||
|
|||
|
|||
static bool GlobalPropertyIndexedAccessCheck(Local<Object> host, |
|||
uint32_t key, |
|||
AccessType type, |
|||
Local<Value> data) { |
|||
return true; |
|||
} |
|||
|
|||
|
|||
static void GlobalPropertyGetterCallback( |
|||
Local<String> property, |
|||
const PropertyCallbackInfo<Value>& args) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
Local<Object> data = args.Data()->ToObject(); |
|||
ContextifyContext* ctx = ObjectWrap::Unwrap<ContextifyContext>(data); |
|||
|
|||
Local<Object> sandbox = PersistentToLocal(node_isolate, ctx->sandbox_); |
|||
Local<Value> rv = sandbox->GetRealNamedProperty(property); |
|||
if (rv.IsEmpty()) { |
|||
Local<Object> proxy_global = PersistentToLocal(node_isolate, |
|||
ctx->proxy_global_); |
|||
rv = proxy_global->GetRealNamedProperty(property); |
|||
} |
|||
if (!rv.IsEmpty() && rv == ctx->sandbox_) { |
|||
rv = PersistentToLocal(node_isolate, ctx->proxy_global_); |
|||
} |
|||
|
|||
args.GetReturnValue().Set(rv); |
|||
} |
|||
|
|||
|
|||
static void GlobalPropertySetterCallback( |
|||
Local<String> property, |
|||
Local<Value> value, |
|||
const PropertyCallbackInfo<Value>& args) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
Local<Object> data = args.Data()->ToObject(); |
|||
ContextifyContext* ctx = ObjectWrap::Unwrap<ContextifyContext>(data); |
|||
|
|||
PersistentToLocal(node_isolate, ctx->sandbox_)->Set(property, value); |
|||
} |
|||
|
|||
|
|||
static void GlobalPropertyQueryCallback( |
|||
Local<String> property, |
|||
const PropertyCallbackInfo<Integer>& args) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
Local<Object> data = args.Data()->ToObject(); |
|||
ContextifyContext* ctx = ObjectWrap::Unwrap<ContextifyContext>(data); |
|||
|
|||
Local<Object> sandbox = PersistentToLocal(node_isolate, ctx->sandbox_); |
|||
Local<Object> proxy_global = PersistentToLocal(node_isolate, |
|||
ctx->proxy_global_); |
|||
|
|||
bool in_sandbox = sandbox->GetRealNamedProperty(property).IsEmpty(); |
|||
bool in_proxy_global = |
|||
proxy_global->GetRealNamedProperty(property).IsEmpty(); |
|||
if (!in_sandbox || !in_proxy_global) { |
|||
args.GetReturnValue().Set(None); |
|||
} |
|||
} |
|||
|
|||
|
|||
static void GlobalPropertyDeleterCallback( |
|||
Local<String> property, |
|||
const PropertyCallbackInfo<Boolean>& args) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
Local<Object> data = args.Data()->ToObject(); |
|||
ContextifyContext* ctx = ObjectWrap::Unwrap<ContextifyContext>(data); |
|||
|
|||
bool success = PersistentToLocal(node_isolate, |
|||
ctx->sandbox_)->Delete(property); |
|||
if (!success) { |
|||
success = PersistentToLocal(node_isolate, |
|||
ctx->proxy_global_)->Delete(property); |
|||
} |
|||
args.GetReturnValue().Set(success); |
|||
} |
|||
|
|||
|
|||
static void GlobalPropertyEnumeratorCallback( |
|||
const PropertyCallbackInfo<Array>& args) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
Local<Object> data = args.Data()->ToObject(); |
|||
ContextifyContext* ctx = ObjectWrap::Unwrap<ContextifyContext>(data); |
|||
|
|||
Local<Object> sandbox = PersistentToLocal(node_isolate, ctx->sandbox_); |
|||
args.GetReturnValue().Set(sandbox->GetPropertyNames()); |
|||
} |
|||
}; |
|||
|
|||
class ContextifyScript : ObjectWrap { |
|||
private: |
|||
Persistent<Script> script_; |
|||
|
|||
public: |
|||
static Persistent<FunctionTemplate> script_tmpl; |
|||
|
|||
static void Init(Local<Object> target) { |
|||
HandleScope scope(node_isolate); |
|||
Local<String> class_name = |
|||
FIXED_ONE_BYTE_STRING(node_isolate, "ContextifyScript"); |
|||
|
|||
script_tmpl.Reset(node_isolate, FunctionTemplate::New(New)); |
|||
Local<FunctionTemplate> lscript_tmpl = |
|||
PersistentToLocal(node_isolate, script_tmpl); |
|||
lscript_tmpl->InstanceTemplate()->SetInternalFieldCount(1); |
|||
lscript_tmpl->SetClassName(class_name); |
|||
NODE_SET_PROTOTYPE_METHOD(lscript_tmpl, "runInContext", RunInContext); |
|||
NODE_SET_PROTOTYPE_METHOD(lscript_tmpl, |
|||
"runInThisContext", |
|||
RunInThisContext); |
|||
|
|||
target->Set(class_name, lscript_tmpl->GetFunction()); |
|||
} |
|||
|
|||
|
|||
// args: code, [filename]
|
|||
static void New(const FunctionCallbackInfo<Value>& args) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
if (!args.IsConstructCall()) { |
|||
return ThrowError("Must call vm.Script as a constructor."); |
|||
} |
|||
|
|||
ContextifyScript *contextify_script = new ContextifyScript(); |
|||
contextify_script->Wrap(args.Holder()); |
|||
Local<String> code = args[0]->ToString(); |
|||
Local<String> filename = GetFilenameArg(args, 1); |
|||
|
|||
Local<Context> context = Context::GetCurrent(); |
|||
Context::Scope context_scope(context); |
|||
|
|||
TryCatch try_catch; |
|||
|
|||
Local<Script> v8_script = Script::New(code, filename); |
|||
|
|||
if (v8_script.IsEmpty()) { |
|||
DisplayExceptionLine(try_catch.Message()); |
|||
try_catch.ReThrow(); |
|||
return; |
|||
} |
|||
contextify_script->script_.Reset(node_isolate, v8_script); |
|||
} |
|||
|
|||
|
|||
static bool InstanceOf(const Local<Value>& value) { |
|||
return !value.IsEmpty() && |
|||
PersistentToLocal(node_isolate, script_tmpl)->HasInstance(value); |
|||
} |
|||
|
|||
|
|||
// args: [timeout]
|
|||
static void RunInThisContext(const FunctionCallbackInfo<Value>& args) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
// Assemble arguments
|
|||
TryCatch try_catch; |
|||
uint64_t timeout = GetTimeoutArg(args, 0); |
|||
if (try_catch.HasCaught()) { |
|||
try_catch.ReThrow(); |
|||
return; |
|||
} |
|||
|
|||
// Do the eval within this context
|
|||
EvalMachine(timeout, args, try_catch); |
|||
} |
|||
|
|||
// args: sandbox, [timeout]
|
|||
static void RunInContext(const FunctionCallbackInfo<Value>& args) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
// Assemble arguments
|
|||
TryCatch try_catch; |
|||
if (!args[0]->IsObject()) { |
|||
return ThrowTypeError("sandbox argument must be an object."); |
|||
} |
|||
Local<Object> sandbox = args[0].As<Object>(); |
|||
uint64_t timeout = GetTimeoutArg(args, 1); |
|||
if (try_catch.HasCaught()) { |
|||
try_catch.ReThrow(); |
|||
return; |
|||
} |
|||
|
|||
// Get the context from the sandbox
|
|||
Local<Context> context = |
|||
ContextifyContext::ContextFromContextifiedSandbox(sandbox); |
|||
if (try_catch.HasCaught()) { |
|||
try_catch.ReThrow(); |
|||
return; |
|||
} |
|||
|
|||
// Do the eval within the context
|
|||
Context::Scope context_scope(context); |
|||
EvalMachine(timeout, args, try_catch); |
|||
} |
|||
|
|||
static int64_t GetTimeoutArg(const FunctionCallbackInfo<Value>& args, |
|||
const int i) { |
|||
if (args[i]->IsUndefined()) { |
|||
return 0; |
|||
} |
|||
|
|||
int64_t timeout = args[i]->IntegerValue(); |
|||
if (timeout < 0) { |
|||
ThrowRangeError("timeout must be a positive number"); |
|||
} |
|||
return timeout; |
|||
} |
|||
|
|||
|
|||
static Local<String> GetFilenameArg(const FunctionCallbackInfo<Value>& args, |
|||
const int i) { |
|||
return !args[i]->IsUndefined() |
|||
? args[i]->ToString() |
|||
: FIXED_ONE_BYTE_STRING(node_isolate, "evalmachine.<anonymous>"); |
|||
} |
|||
|
|||
|
|||
static void EvalMachine(const int64_t timeout, |
|||
const FunctionCallbackInfo<Value>& args, |
|||
TryCatch& try_catch) { |
|||
if (!ContextifyScript::InstanceOf(args.This())) { |
|||
return ThrowTypeError( |
|||
"Script methods can only be called on script instances."); |
|||
} |
|||
|
|||
ContextifyScript* wrapped_script = |
|||
ObjectWrap::Unwrap<ContextifyScript>(args.This()); |
|||
Local<Script> script = PersistentToLocal(node_isolate, |
|||
wrapped_script->script_); |
|||
if (script.IsEmpty()) { |
|||
DisplayExceptionLine(try_catch.Message()); |
|||
try_catch.ReThrow(); |
|||
return; |
|||
} |
|||
|
|||
Local<Value> result; |
|||
if (timeout) { |
|||
Watchdog wd(timeout); |
|||
result = script->Run(); |
|||
} else { |
|||
result = script->Run(); |
|||
} |
|||
|
|||
if (try_catch.HasCaught() && try_catch.HasTerminated()) { |
|||
V8::CancelTerminateExecution(args.GetIsolate()); |
|||
return ThrowError("Script execution timed out."); |
|||
} |
|||
|
|||
if (result.IsEmpty()) { |
|||
// Error occurred during execution of the script.
|
|||
DisplayExceptionLine(try_catch.Message()); |
|||
try_catch.ReThrow(); |
|||
return; |
|||
} |
|||
|
|||
args.GetReturnValue().Set(result); |
|||
} |
|||
|
|||
|
|||
~ContextifyScript() { |
|||
script_.Dispose(); |
|||
} |
|||
}; |
|||
|
|||
|
|||
Persistent<FunctionTemplate> ContextifyContext::js_tmpl; |
|||
Persistent<FunctionTemplate> ContextifyContext::data_wrapper_tmpl; |
|||
Persistent<Function> ContextifyContext::data_wrapper_ctor; |
|||
|
|||
Persistent<FunctionTemplate> ContextifyScript::script_tmpl; |
|||
|
|||
void InitContextify(Local<Object> target) { |
|||
HandleScope scope(node_isolate); |
|||
ContextifyContext::Init(target); |
|||
ContextifyScript::Init(target); |
|||
} |
|||
|
|||
} // namespace node
|
|||
|
|||
NODE_MODULE(node_contextify, node::InitContextify); |
@ -1,458 +0,0 @@ |
|||
// Copyright Joyent, Inc. and other Node contributors.
|
|||
//
|
|||
// 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.
|
|||
|
|||
#include "node.h" |
|||
#include "node_script.h" |
|||
#include "node_watchdog.h" |
|||
#include <assert.h> |
|||
|
|||
namespace node { |
|||
|
|||
using v8::Array; |
|||
using v8::Context; |
|||
using v8::Exception; |
|||
using v8::Function; |
|||
using v8::FunctionCallbackInfo; |
|||
using v8::FunctionTemplate; |
|||
using v8::Handle; |
|||
using v8::HandleScope; |
|||
using v8::Local; |
|||
using v8::Object; |
|||
using v8::Persistent; |
|||
using v8::Script; |
|||
using v8::String; |
|||
using v8::TryCatch; |
|||
using v8::V8; |
|||
using v8::Value; |
|||
|
|||
|
|||
class WrappedContext : ObjectWrap { |
|||
public: |
|||
static void Initialize(Handle<Object> target); |
|||
static void New(const FunctionCallbackInfo<Value>& args); |
|||
|
|||
Local<Context> GetV8Context(); |
|||
static Local<Object> NewInstance(); |
|||
static bool InstanceOf(Handle<Value> value); |
|||
|
|||
protected: |
|||
|
|||
static Persistent<FunctionTemplate> constructor_template; |
|||
|
|||
WrappedContext(); |
|||
~WrappedContext(); |
|||
|
|||
Persistent<Context> context_; |
|||
}; |
|||
|
|||
|
|||
Persistent<FunctionTemplate> WrappedContext::constructor_template; |
|||
|
|||
|
|||
class WrappedScript : ObjectWrap { |
|||
public: |
|||
static void Initialize(Handle<Object> target); |
|||
|
|||
enum EvalInputFlags { compileCode, unwrapExternal }; |
|||
enum EvalContextFlags { thisContext, newContext, userContext }; |
|||
enum EvalOutputFlags { returnResult, wrapExternal }; |
|||
enum EvalTimeoutFlags { noTimeout, useTimeout }; |
|||
|
|||
template <EvalInputFlags input_flag, |
|||
EvalContextFlags context_flag, |
|||
EvalOutputFlags output_flag, |
|||
EvalTimeoutFlags timeout_flag> |
|||
static void EvalMachine(const FunctionCallbackInfo<Value>& args); |
|||
|
|||
protected: |
|||
WrappedScript() : ObjectWrap() {} |
|||
~WrappedScript(); |
|||
|
|||
static void New(const FunctionCallbackInfo<Value>& args); |
|||
static void CreateContext(const FunctionCallbackInfo<Value>& args); |
|||
static void RunInContext(const FunctionCallbackInfo<Value>& args); |
|||
static void RunInThisContext(const FunctionCallbackInfo<Value>& args); |
|||
static void RunInNewContext(const FunctionCallbackInfo<Value>& args); |
|||
static void CompileRunInContext(const FunctionCallbackInfo<Value>& args); |
|||
static void CompileRunInThisContext(const FunctionCallbackInfo<Value>& args); |
|||
static void CompileRunInNewContext(const FunctionCallbackInfo<Value>& args); |
|||
|
|||
Persistent<Script> script_; |
|||
}; |
|||
|
|||
|
|||
void CloneObject(Handle<Object> recv, |
|||
Handle<Value> source, |
|||
Handle<Value> target) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
const char raw_script_source[] = |
|||
"(function(source, target) { \n" |
|||
" Object.getOwnPropertyNames(source).forEach(function(key) { \n" |
|||
" try { \n" |
|||
" var desc = Object.getOwnPropertyDescriptor(source, key); \n" |
|||
" if (desc.value === source) desc.value = target; \n" |
|||
" Object.defineProperty(target, key, desc); \n" |
|||
" } catch (e) { \n" |
|||
" // Catch sealed properties errors \n" |
|||
" } \n" |
|||
" }); \n" |
|||
"}); \n"; |
|||
|
|||
Local<String> script_source = |
|||
FIXED_ONE_BYTE_STRING(node_isolate, raw_script_source); |
|||
Local<String> script_name = |
|||
FIXED_ONE_BYTE_STRING(node_isolate, "binding:script"); |
|||
Local<Script> script = Script::Compile(script_source, script_name); |
|||
|
|||
Local<Function> fun = script->Run().As<Function>(); |
|||
assert(fun.IsEmpty() == false); |
|||
assert(fun->IsFunction() == true); |
|||
|
|||
Handle<Value> argv[] = { source, target }; |
|||
Handle<Value> rc = fun->Call(recv, ARRAY_SIZE(argv), argv); |
|||
assert(rc.IsEmpty() == false); |
|||
} |
|||
|
|||
|
|||
void WrappedContext::Initialize(Handle<Object> target) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
Local<FunctionTemplate> t = FunctionTemplate::New(WrappedContext::New); |
|||
t->InstanceTemplate()->SetInternalFieldCount(1); |
|||
t->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "Context")); |
|||
|
|||
target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Context"), t->GetFunction()); |
|||
constructor_template.Reset(node_isolate, t); |
|||
} |
|||
|
|||
|
|||
bool WrappedContext::InstanceOf(Handle<Value> value) { |
|||
return !value.IsEmpty() && HasInstance(constructor_template, value); |
|||
} |
|||
|
|||
|
|||
void WrappedContext::New(const FunctionCallbackInfo<Value>& args) { |
|||
HandleScope scope(node_isolate); |
|||
WrappedContext *t = new WrappedContext(); |
|||
t->Wrap(args.This()); |
|||
} |
|||
|
|||
|
|||
WrappedContext::WrappedContext() : ObjectWrap() { |
|||
context_.Reset(node_isolate, Context::New(node_isolate)); |
|||
} |
|||
|
|||
|
|||
WrappedContext::~WrappedContext() { |
|||
context_.Dispose(); |
|||
} |
|||
|
|||
|
|||
Local<Object> WrappedContext::NewInstance() { |
|||
Local<FunctionTemplate> constructor_template_handle = |
|||
PersistentToLocal(node_isolate, constructor_template); |
|||
return constructor_template_handle->GetFunction()->NewInstance(); |
|||
} |
|||
|
|||
|
|||
Local<Context> WrappedContext::GetV8Context() { |
|||
return PersistentToLocal(node_isolate, context_); |
|||
} |
|||
|
|||
|
|||
void WrappedScript::Initialize(Handle<Object> target) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
Local<FunctionTemplate> t = FunctionTemplate::New(WrappedScript::New); |
|||
t->InstanceTemplate()->SetInternalFieldCount(1); |
|||
// Note: We use 'NodeScript' instead of 'Script' so that we do not
|
|||
// conflict with V8's Script class defined in v8/src/messages.js
|
|||
// See GH-203 https://github.com/joyent/node/issues/203
|
|||
t->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "NodeScript")); |
|||
|
|||
NODE_SET_PROTOTYPE_METHOD(t, |
|||
"createContext", |
|||
WrappedScript::CreateContext); |
|||
|
|||
NODE_SET_PROTOTYPE_METHOD(t, |
|||
"runInContext", |
|||
WrappedScript::RunInContext); |
|||
|
|||
NODE_SET_PROTOTYPE_METHOD(t, |
|||
"runInThisContext", |
|||
WrappedScript::RunInThisContext); |
|||
|
|||
NODE_SET_PROTOTYPE_METHOD(t, |
|||
"runInNewContext", |
|||
WrappedScript::RunInNewContext); |
|||
|
|||
NODE_SET_METHOD(t, |
|||
"createContext", |
|||
WrappedScript::CreateContext); |
|||
|
|||
NODE_SET_METHOD(t, |
|||
"runInContext", |
|||
WrappedScript::CompileRunInContext); |
|||
|
|||
NODE_SET_METHOD(t, |
|||
"runInThisContext", |
|||
WrappedScript::CompileRunInThisContext); |
|||
|
|||
NODE_SET_METHOD(t, |
|||
"runInNewContext", |
|||
WrappedScript::CompileRunInNewContext); |
|||
|
|||
target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "NodeScript"), |
|||
t->GetFunction()); |
|||
} |
|||
|
|||
|
|||
void WrappedScript::New(const FunctionCallbackInfo<Value>& args) { |
|||
assert(args.IsConstructCall() == true); |
|||
HandleScope scope(node_isolate); |
|||
WrappedScript *t = new WrappedScript(); |
|||
t->Wrap(args.This()); |
|||
WrappedScript::EvalMachine< |
|||
compileCode, thisContext, wrapExternal, noTimeout>(args); |
|||
} |
|||
|
|||
|
|||
WrappedScript::~WrappedScript() { |
|||
script_.Dispose(); |
|||
} |
|||
|
|||
|
|||
void WrappedScript::CreateContext(const FunctionCallbackInfo<Value>& args) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
Local<Object> context = WrappedContext::NewInstance(); |
|||
|
|||
if (args.Length() > 0) { |
|||
if (args[0]->IsObject()) { |
|||
Local<Object> sandbox = args[0].As<Object>(); |
|||
|
|||
CloneObject(args.This(), sandbox, context); |
|||
} else { |
|||
return ThrowTypeError( |
|||
"createContext() accept only object as first argument."); |
|||
} |
|||
} |
|||
|
|||
args.GetReturnValue().Set(context); |
|||
} |
|||
|
|||
|
|||
void WrappedScript::RunInContext(const FunctionCallbackInfo<Value>& args) { |
|||
WrappedScript::EvalMachine< |
|||
unwrapExternal, userContext, returnResult, useTimeout>(args); |
|||
} |
|||
|
|||
|
|||
void WrappedScript::RunInThisContext(const FunctionCallbackInfo<Value>& args) { |
|||
WrappedScript::EvalMachine< |
|||
unwrapExternal, thisContext, returnResult, useTimeout>(args); |
|||
} |
|||
|
|||
|
|||
void WrappedScript::RunInNewContext(const FunctionCallbackInfo<Value>& args) { |
|||
WrappedScript::EvalMachine< |
|||
unwrapExternal, newContext, returnResult, useTimeout>(args); |
|||
} |
|||
|
|||
|
|||
void WrappedScript::CompileRunInContext( |
|||
const FunctionCallbackInfo<Value>& args) { |
|||
WrappedScript::EvalMachine< |
|||
compileCode, userContext, returnResult, useTimeout>(args); |
|||
} |
|||
|
|||
|
|||
void WrappedScript::CompileRunInThisContext( |
|||
const FunctionCallbackInfo<Value>& args) { |
|||
WrappedScript::EvalMachine< |
|||
compileCode, thisContext, returnResult, useTimeout>(args); |
|||
} |
|||
|
|||
|
|||
void WrappedScript::CompileRunInNewContext( |
|||
const FunctionCallbackInfo<Value>& args) { |
|||
WrappedScript::EvalMachine< |
|||
compileCode, newContext, returnResult, useTimeout>(args); |
|||
} |
|||
|
|||
|
|||
template <WrappedScript::EvalInputFlags input_flag, |
|||
WrappedScript::EvalContextFlags context_flag, |
|||
WrappedScript::EvalOutputFlags output_flag, |
|||
WrappedScript::EvalTimeoutFlags timeout_flag> |
|||
void WrappedScript::EvalMachine(const FunctionCallbackInfo<Value>& args) { |
|||
HandleScope scope(node_isolate); |
|||
|
|||
if (input_flag == compileCode && args.Length() < 1) { |
|||
return ThrowTypeError("needs at least 'code' argument."); |
|||
} |
|||
|
|||
const int sandbox_index = input_flag == compileCode ? 1 : 0; |
|||
if (context_flag == userContext && |
|||
!WrappedContext::InstanceOf(args[sandbox_index])) { |
|||
return ThrowTypeError("needs a 'context' argument."); |
|||
} |
|||
|
|||
Local<String> code; |
|||
if (input_flag == compileCode) code = args[0]->ToString(); |
|||
|
|||
Local<Object> sandbox; |
|||
if (context_flag == newContext) { |
|||
sandbox = args[sandbox_index]->IsObject() ? args[sandbox_index]->ToObject() |
|||
: Object::New(); |
|||
} else if (context_flag == userContext) { |
|||
sandbox = args[sandbox_index]->ToObject(); |
|||
} |
|||
|
|||
const int filename_index = sandbox_index + |
|||
(context_flag == thisContext? 0 : 1); |
|||
Local<String> filename; |
|||
if (args.Length() > filename_index) { |
|||
filename = args[filename_index]->ToString(); |
|||
} else { |
|||
filename = FIXED_ONE_BYTE_STRING(node_isolate, "evalmachine.<anonymous>"); |
|||
} |
|||
|
|||
uint64_t timeout = 0; |
|||
const int timeout_index = filename_index + 1; |
|||
if (timeout_flag == useTimeout && args.Length() > timeout_index) { |
|||
if (!args[timeout_index]->IsUint32()) { |
|||
return ThrowTypeError("needs an unsigned integer 'ms' argument."); |
|||
} |
|||
timeout = args[timeout_index]->Uint32Value(); |
|||
} |
|||
|
|||
const int display_error_index = timeout_index + |
|||
(timeout_flag == noTimeout ? 0 : 1); |
|||
bool display_error = false; |
|||
if (args.Length() > display_error_index && |
|||
args[display_error_index]->IsBoolean() && |
|||
args[display_error_index]->BooleanValue() == true) { |
|||
display_error = true; |
|||
} |
|||
|
|||
Local<Context> context = Context::GetCurrent(); |
|||
|
|||
Local<Array> keys; |
|||
if (context_flag == newContext) { |
|||
// Create the new context
|
|||
context = Context::New(node_isolate); |
|||
|
|||
} else if (context_flag == userContext) { |
|||
// Use the passed in context
|
|||
WrappedContext *nContext = ObjectWrap::Unwrap<WrappedContext>(sandbox); |
|||
context = nContext->GetV8Context(); |
|||
} |
|||
|
|||
Context::Scope context_scope(context); |
|||
|
|||
// New and user context share code. DRY it up.
|
|||
if (context_flag == userContext || context_flag == newContext) { |
|||
// Copy everything from the passed in sandbox (either the persistent
|
|||
// context for runInContext(), or the sandbox arg to runInNewContext()).
|
|||
CloneObject(args.This(), sandbox, context->Global()->GetPrototype()); |
|||
} |
|||
|
|||
// Catch errors
|
|||
TryCatch try_catch; |
|||
|
|||
// TryCatch must not be verbose to prevent duplicate logging
|
|||
// of uncaught exceptions (we are rethrowing them)
|
|||
try_catch.SetVerbose(false); |
|||
|
|||
Handle<Value> result; |
|||
Local<Script> script; |
|||
|
|||
if (input_flag == compileCode) { |
|||
// well, here WrappedScript::New would suffice in all cases, but maybe
|
|||
// Compile has a little better performance where possible
|
|||
script = output_flag == returnResult ? Script::Compile(code, filename) |
|||
: Script::New(code, filename); |
|||
if (script.IsEmpty()) { |
|||
// FIXME UGLY HACK TO DISPLAY SYNTAX ERRORS.
|
|||
if (display_error) DisplayExceptionLine(try_catch.Message()); |
|||
|
|||
// Hack because I can't get a proper stacktrace on SyntaxError
|
|||
try_catch.ReThrow(); |
|||
return; |
|||
} |
|||
} else { |
|||
WrappedScript *n_script = ObjectWrap::Unwrap<WrappedScript>(args.This()); |
|||
if (!n_script) { |
|||
return ThrowError("Must be called as a method of Script."); |
|||
} else if (n_script->script_.IsEmpty()) { |
|||
return ThrowError( |
|||
"'this' must be a result of previous new Script(code) call."); |
|||
} |
|||
|
|||
script = PersistentToLocal(node_isolate, n_script->script_); |
|||
} |
|||
|
|||
if (output_flag == returnResult) { |
|||
if (timeout) { |
|||
Watchdog wd(timeout); |
|||
result = script->Run(); |
|||
} else { |
|||
result = script->Run(); |
|||
} |
|||
if (try_catch.HasCaught() && try_catch.HasTerminated()) { |
|||
V8::CancelTerminateExecution(args.GetIsolate()); |
|||
return ThrowError("Script execution timed out."); |
|||
} |
|||
if (result.IsEmpty()) { |
|||
if (display_error) DisplayExceptionLine(try_catch.Message()); |
|||
try_catch.ReThrow(); |
|||
return; |
|||
} |
|||
} else { |
|||
WrappedScript *n_script = ObjectWrap::Unwrap<WrappedScript>(args.This()); |
|||
if (!n_script) { |
|||
return ThrowError("Must be called as a method of Script."); |
|||
} |
|||
n_script->script_.Reset(node_isolate, script); |
|||
result = args.This(); |
|||
} |
|||
|
|||
if (context_flag == userContext || context_flag == newContext) { |
|||
// success! copy changes back onto the sandbox object.
|
|||
CloneObject(args.This(), context->Global()->GetPrototype(), sandbox); |
|||
} |
|||
|
|||
args.GetReturnValue().Set(result); |
|||
} |
|||
|
|||
|
|||
void InitEvals(Handle<Object> target) { |
|||
HandleScope scope(node_isolate); |
|||
WrappedContext::Initialize(target); |
|||
WrappedScript::Initialize(target); |
|||
} |
|||
|
|||
|
|||
} // namespace node
|
|||
|
|||
NODE_MODULE(node_evals, node::InitEvals) |
@ -0,0 +1,70 @@ |
|||
// Copyright Joyent, Inc. and other Node contributors.
|
|||
//
|
|||
// 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.
|
|||
|
|||
var common = require('../common'); |
|||
var assert = require('assert'); |
|||
var vm = require('vm'); |
|||
|
|||
// Test 1: vm.runInNewContext
|
|||
var sandbox = {}; |
|||
var result = vm.runInNewContext( |
|||
'foo = "bar"; this.typeofProcess = typeof process; typeof Object;', |
|||
sandbox |
|||
); |
|||
assert.deepEqual(sandbox, { |
|||
foo: 'bar', |
|||
typeofProcess: 'undefined', |
|||
}); |
|||
assert.strictEqual(result, 'function'); |
|||
|
|||
// Test 2: vm.runInContext
|
|||
var sandbox2 = { foo: 'bar' }; |
|||
var context = vm.createContext(sandbox2); |
|||
var result = vm.runInContext( |
|||
'baz = foo; this.typeofProcess = typeof process; typeof Object;', |
|||
context |
|||
); |
|||
assert.deepEqual(sandbox2, { |
|||
foo: 'bar', |
|||
baz: 'bar', |
|||
typeofProcess: 'undefined' |
|||
}); |
|||
assert.strictEqual(result, 'function'); |
|||
|
|||
// Test 3: vm.runInThisContext
|
|||
var result = vm.runInThisContext( |
|||
'vmResult = "foo"; Object.prototype.toString.call(process);' |
|||
); |
|||
assert.strictEqual(global.vmResult, 'foo'); |
|||
assert.strictEqual(result, '[object process]'); |
|||
delete global.vmResult; |
|||
|
|||
// Test 4: vm.runInNewContext
|
|||
var result = vm.runInNewContext( |
|||
'vmResult = "foo"; typeof process;' |
|||
); |
|||
assert.strictEqual(global.vmResult, undefined); |
|||
assert.strictEqual(result, 'undefined'); |
|||
|
|||
// Test 5: vm.createContext
|
|||
var sandbox3 = {}; |
|||
var context2 = vm.createContext(sandbox3); |
|||
assert.strictEqual(sandbox3, context2); |
@ -0,0 +1,34 @@ |
|||
// Copyright Joyent, Inc. and other Node contributors.
|
|||
//
|
|||
// 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.
|
|||
|
|||
var common = require('../common'); |
|||
var assert = require('assert'); |
|||
var vm = require('vm'); |
|||
|
|||
var sandbox = { setTimeout: setTimeout }; |
|||
|
|||
var ctx = vm.createContext(sandbox); |
|||
|
|||
vm.runInContext('setTimeout(function() { x = 3; }, 0);', ctx); |
|||
setTimeout(function () { |
|||
assert.strictEqual(sandbox.x, 3); |
|||
assert.strictEqual(ctx.x, 3); |
|||
}, 1); |
@ -0,0 +1,33 @@ |
|||
// Copyright Joyent, Inc. and other Node contributors.
|
|||
//
|
|||
// 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.
|
|||
|
|||
var common = require('../common'); |
|||
var assert = require('assert'); |
|||
var vm = require('vm'); |
|||
|
|||
var sandbox = { x: 3 }; |
|||
|
|||
var ctx = vm.createContext(sandbox); |
|||
|
|||
assert.strictEqual(vm.runInContext('x;', ctx), 3); |
|||
vm.runInContext('y = 4;', ctx); |
|||
assert.strictEqual(sandbox.y, 4); |
|||
assert.strictEqual(ctx.y, 4); |
@ -0,0 +1,31 @@ |
|||
// Copyright Joyent, Inc. and other Node contributors.
|
|||
//
|
|||
// 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.
|
|||
|
|||
var common = require('../common'); |
|||
var assert = require('assert'); |
|||
var vm = require('vm'); |
|||
|
|||
var ctx = vm.createContext(); |
|||
ctx.window = ctx; |
|||
|
|||
var thisVal = vm.runInContext('this;', ctx); |
|||
var windowVal = vm.runInContext('window;', ctx); |
|||
assert.strictEqual(thisVal, windowVal); |
Loading…
Reference in new issue