#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::ArrayBuffer; 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::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; using v8::Maybe; using v8::MaybeLocal; using v8::Name; using v8::NamedPropertyHandlerConfiguration; using v8::None; using v8::Object; using v8::ObjectTemplate; using v8::Persistent; using v8::PropertyAttribute; using v8::PropertyCallbackInfo; using v8::Script; using v8::ScriptCompiler; using v8::ScriptOrigin; using v8::String; using v8::TryCatch; using v8::Uint8Array; using v8::UnboundScript; using v8::V8; using v8::Value; using v8::WeakCallbackInfo; class ContextifyContext { protected: // V8 reserves the first field in context objects for the debugger. We use the // second field to hold a reference to the sandbox object. enum { kSandboxObjectIndex = 1 }; Environment* const env_; Persistent context_; public: ContextifyContext(Environment* env, Local sandbox_obj) : env_(env) { Local v8_context = CreateV8Context(env, sandbox_obj); context_.Reset(env->isolate(), v8_context); // Allocation failure or maximum call stack size reached if (context_.IsEmpty()) return; context_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); context_.MarkIndependent(); } ~ContextifyContext() { context_.Reset(); } inline Environment* env() const { return env_; } inline Local context() const { return PersistentToLocal(env()->isolate(), context_); } inline Local global_proxy() const { return context()->Global(); } inline Local sandbox() const { return Local::Cast(context()->GetEmbedderData(kSandboxObjectIndex)); } // 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(env()->isolate()); Local sandbox_obj = 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(env()->isolate()); bool has = sandbox_obj->HasOwnProperty(context, key).FromJust(); 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