#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(env()->isolate());
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(env()->isolate());
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