Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: src/JsEngine.cpp

Issue 29369365: Issue #4692 - Rewrite `SetTimeout` facility to avoid `JsValuePtr` and `JsValueList`
Patch Set: Created Dec. 19, 2016, 10:24 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/JsEngine.cpp
===================================================================
--- a/src/JsEngine.cpp
+++ b/src/JsEngine.cpp
@@ -18,6 +18,8 @@
#include <AdblockPlus.h>
#include "GlobalJsObject.h"
#include "JsContext.h"
+#include "JsEngineInternal.h"
+#include "JsEngineTransition.h"
#include "JsError.h"
#include "Scheduler.h"
#include "Utils.h"
@@ -74,6 +76,13 @@
};
}
+V8ExecutionScope::V8ExecutionScope(JsEngineInternal* engine)
+ : lock(engine->GetIsolate()),
+ isolateScope(engine->GetIsolate()),
+ handleScope(engine->GetIsolate()),
+ contextScope(engine->GetContextAsLocal())
+{}
+
AdblockPlus::ScopedV8Isolate::ScopedV8Isolate()
{
V8Initializer::Init();
@@ -88,27 +97,94 @@
AdblockPlus::JsEngine::JsEngine(const ScopedV8IsolatePtr& isolate)
: isolate(isolate),
- scheduler(new SchedulerImpl()) // use std::make_unique after we upgrade out of VS2012
+ scheduler(new SchedulerImpl()) // TODO: make_unique once available
{}
+JsEngineInternal::JsEngineInternal(const AdblockPlus::ScopedV8IsolatePtr& isolate)
+ : AdblockPlus::JsEngine(isolate),
+ context(isolate->Get(),v8::Context::New(isolate->Get()))
+{
+ /*
+ * Enter v8 scope for our context so that we can initialize its global object.
+ */
+ const v8::Context::Scope contextScope(GetContextAsLocal());
+ auto globalObject = GetGlobalObject();
+ auto propertyName = AdblockPlus::Utils::ToV8String(GetIsolate(), "setTimeout");
+ globalObject->Set(propertyName, MakeCallback(::CallbackForSetTimeout));
Eric 2016/12/19 22:39:53 New initialization of global object without any us
+ // TODO: Move the rest of the global object initializations here
+}
+
+/**
+ * \par Design Notes
+ * It is technically necessary to construct JsEngine instances *only* within a factory.
+ * Initialization requires that certain transient v8 scopes be set up
+ * before initialization and torn down afterwards.
+ * C++ has no syntax to use anything like a sentry object in the constructor itself.
+ * Thus we need to establish v8 scope within every C++ scope that constructs an object.
+ */
AdblockPlus::JsEnginePtr AdblockPlus::JsEngine::New(const AppInfo& appInfo, const ScopedV8IsolatePtr& isolate)
{
- JsEnginePtr result(new JsEngine(isolate));
+ auto isolateP = isolate->Get();
+ /*
+ * TODO: Remove `locker`.
+ * Until #3595 is fixed, unit tests allocate isolates outside of this class.
+ * Once we're no longer doing that, we may assume that this class holds
+ * exclusive access to the nascent isolate.
+ * The factory and constructor constitute a single-threaded usage,
+ * and there will be no need to lock the isolate.
+ */
+ const v8::Locker locker(isolateP);
+ /*
+ * Set up v8 scopes for isolate and handle.
+ * We cannot set up the v8 scope for context because it doesn't exist
+ * until after the constructor for `JsEngineInternal` returns.
+ */
+ const v8::Isolate::Scope isolateScope(isolateP);
+ const v8::HandleScope handleScope(isolateP);
- const v8::Locker locker(result->GetIsolate());
- const v8::Isolate::Scope isolateScope(result->GetIsolate());
- const v8::HandleScope handleScope(result->GetIsolate());
+ std::shared_ptr<JsEngineInternal> engine(std::make_shared<JsEngineInternal>(isolate));
- result->context.reset(new v8::Persistent<v8::Context>(result->GetIsolate(),
- v8::Context::New(result->GetIsolate())));
+ JsEnginePtr result(engine);
+ // Establish a context scope for the legacy setup of the global object
+ const v8::Context::Scope contextScope(engine->GetContextAsLocal());
AdblockPlus::GlobalJsObject::Setup(result, appInfo, result->GetGlobalObject());
return result;
}
+v8::Local<v8::Context> JsEngineInternal::GetContextAsLocal() const
+{
+ return v8::Local<v8::Context>::New(isolate->Get(), context);
+}
+
AdblockPlus::JsValuePtr AdblockPlus::JsEngine::GetGlobalObject()
{
- JsContext context(shared_from_this());
- return JsValuePtr(new JsValue(shared_from_this(), context.GetV8Context()->Global()));
+ return JsValuePtr(new JsValue(shared_from_this(), ToInternal(this)->GetGlobalObject()));
+}
+
+v8::Local<v8::Object> JsEngineInternal::GetGlobalObject()
+{
+ return GetContextAsLocal()->Global();
+}
+
+v8::Local<v8::Value> JsEngineInternal::ApplyFunction(
+ v8::Local<v8::Function> func,
+ AllocatedArray<v8::Local<v8::Value>> args)
+{
+ return ApplyFunction(GetGlobalObject(), func, std::move(args));
+}
+
+v8::Local<v8::Value> JsEngineInternal::ApplyFunction(
+ v8::Local<v8::Object> thisObject,
+ v8::Local<v8::Function> func,
+ AllocatedArray<v8::Local<v8::Value>> args)
+{
+ const v8::TryCatch tryCatch;
+ v8::Local<v8::Value> result = func->Call(thisObject, args.Size(), args.Get());
+ if (tryCatch.HasCaught())
+ {
+ throw AdblockPlus::JsError(tryCatch.Exception(), tryCatch.Message());
+ }
+ return result;
}
AdblockPlus::JsValuePtr AdblockPlus::JsEngine::Evaluate(const std::string& source,
@@ -199,6 +275,28 @@
return result;
}
+/**
+ * \par Implementation Notes
+ * We initialize the `Data()` element of the callback arguments with `this`,
+ * which raises an issue about life cycle.
+ * The result of `FunctionTemplate::New` is to create a function
+ * whose lifespan is the same as the context it's created in.
+ * The context is an instance member of the engine,
+ * so as long as the engine exists, so does the context.
+ * Since evaluation in v8 only occurs during the ordinary lifespan of the engine
+ * (i.e. after the constructor and before the destructor),
+ * the `this` pointer will be valid whenever the callback function might be called.
+ */
+v8::Local<v8::Function> JsEngineInternal::MakeCallback(v8::InvocationCallback callback)
+{
+ return v8::FunctionTemplate::New(callback, v8::External::New(this))->GetFunction();
+}
+
+JsEngineInternal* JsEngineInternal::ExtractEngine(const v8::Arguments& arguments)
+{
+ return static_cast<JsEngineInternal*>(v8::Local<v8::External>::Cast(arguments.Data())->Value());
+}
+
AdblockPlus::JsValueList AdblockPlus::JsEngine::ConvertArguments(const v8::Arguments& arguments)
{
const JsContext context(shared_from_this());
@@ -253,10 +351,10 @@
logSystem = val;
}
-
void AdblockPlus::JsEngine::SetGlobalProperty(const std::string& name,
AdblockPlus::JsValuePtr value)
{
+ JsContext jsContext(shared_from_this());
auto global = GetGlobalObject();
if (!global)
throw std::runtime_error("Global object cannot be null");
@@ -275,3 +373,13 @@
{
scheduler->JoinAll();
}
+
+JsEngineInternal* ToInternal(AdblockPlus::JsEnginePtr p)
+{
+ return static_cast<JsEngineInternal*>(p.get());
+}
+
+JsEngineInternal* ToInternal(AdblockPlus::JsEngine* p)
+{
+ return static_cast<JsEngineInternal*>(p);
+}
« src/GlobalJsObject.cpp ('K') | « src/JsContext.cpp ('k') | src/JsEngineInternal.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld