// 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" #include "base-object.h" #include "base-object-inl.h" #include "env.h" #include "env-inl.h" #include "util.h" #include "util-inl.h" #include "v8-debug.h" namespace node { using v8::AccessType; using v8::Array; using v8::Boolean; using v8::Context; using v8::Debug; using v8::EscapableHandleScope; using v8::External; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Handle; using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; using v8::None; using v8::Object; using v8::ObjectTemplate; using v8::Persistent; using v8::PropertyCallbackInfo; using v8::Script; using v8::ScriptCompiler; using v8::ScriptOrigin; using v8::String; using v8::TryCatch; using v8::UnboundScript; using v8::V8; using v8::Value; using v8::WeakCallbackData; class ContextifyContext { protected: enum Kind { kSandbox, kContext, kProxyGlobal }; Environment* const env_; Persistent sandbox_; Persistent context_; Persistent proxy_global_; int references_; public: explicit ContextifyContext(Environment* env, Local sandbox) : env_(env), sandbox_(env->isolate(), sandbox), context_(env->isolate(), CreateV8Context(env)), // Wait for sandbox_, proxy_global_, and context_ to die references_(0) { sandbox_.SetWeak(this, WeakCallback); sandbox_.MarkIndependent(); references_++; // Allocation failure or maximum call stack size reached if (context_.IsEmpty()) return; context_.SetWeak(this, WeakCallback); context_.MarkIndependent(); references_++; proxy_global_.Reset(env->isolate(), context()->Global()); proxy_global_.SetWeak(this, WeakCallback); proxy_global_.MarkIndependent(); references_++; } ~ContextifyContext() { context_.Reset(); proxy_global_.Reset(); sandbox_.Reset(); } inline Environment* env() const { return env_; } inline Local context() const { return PersistentToLocal(env()->isolate(), context_); } // XXX(isaacs): This function only exists because of a shortcoming of // the V8 SetNamedPropertyHandler function. // // It does not provide a way to intercept Object.defineProperty(..) // calls. As a result, these properties are not copied onto the // contextified sandbox when a new global property is added via either // a function declaration or a Object.defineProperty(global, ...) call. // // Note that any function declarations or Object.defineProperty() // globals that are created asynchronously (in a setTimeout, callback, // etc.) will happen AFTER the call to copy properties, and thus not be // caught. // // The way to properly fix this is to add some sort of a // Object::SetNamedDefinePropertyHandler() function that takes a callback, // which receives the property name and property descriptor as arguments. // // Luckily, such situations are rare, and asynchronously-added globals // weren't supported by Node's VM module until 0.12 anyway. But, this // should be fixed properly in V8, and this copy function should be // removed once there is a better way. void CopyProperties() { HandleScope scope(env()->isolate()); Local context = PersistentToLocal(env()->isolate(), context_); Local global = context->Global()->GetPrototype()->ToObject(); Local sandbox = PersistentToLocal(env()->isolate(), sandbox_); Local clone_property_method; Local names = global->GetOwnPropertyNames(); int length = names->Length(); for (int i = 0; i < length; i++) { Local key = names->Get(i)->ToString(); bool has = sandbox->HasOwnProperty(key); if (!has) { // Could also do this like so: // // PropertyAttribute att = global->GetPropertyAttributes(key_v); // Local val = global->Get(key_v); // sandbox->ForceSet(key_v, val, att); // // However, this doesn't handle ES6-style properties configured with // Object.defineProperty, and that's exactly what we're up against at // this point. ForceSet(key,val,att) only supports value properties // with the ES3-style attribute flags (DontDelete/DontEnum/ReadOnly), // which doesn't faithfully capture the full range of configurations // that can be done using Object.defineProperty. if (clone_property_method.IsEmpty()) { Local code = FIXED_ONE_BYTE_STRING(env()->isolate(), "(function cloneProperty(source, key, target) {\n" " if (key === 'Proxy') return;\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" "})"); Local fname = FIXED_ONE_BYTE_STRING(env()->isolate(), "binding:script"); Local