OLD | NEW |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-2016 Eyeo GmbH | 3 * Copyright (C) 2006-2016 Eyeo GmbH |
4 * | 4 * |
5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
8 * | 8 * |
9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * GNU General Public License for more details. | 12 * GNU General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU General Public License | 14 * You should have received a copy of the GNU General Public License |
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
16 */ | 16 */ |
17 | 17 |
18 #include <AdblockPlus.h> | 18 #include <AdblockPlus.h> |
19 #include "GlobalJsObject.h" | 19 #include "GlobalJsObject.h" |
20 #include "JsContext.h" | 20 #include "JsContext.h" |
| 21 #include "JsEngineInternal.h" |
| 22 #include "JsEngineTransition.h" |
21 #include "JsError.h" | 23 #include "JsError.h" |
22 #include "Scheduler.h" | 24 #include "Scheduler.h" |
23 #include "Utils.h" | 25 #include "Utils.h" |
24 | 26 |
25 const AdblockPlus::ImmediateSingleUseThreadType AdblockPlus::ImmediateSingleUseT
hread = {}; | 27 const AdblockPlus::ImmediateSingleUseThreadType AdblockPlus::ImmediateSingleUseT
hread = {}; |
26 | 28 |
27 class AdblockPlus::JsEngine::SchedulerImpl | 29 class AdblockPlus::JsEngine::SchedulerImpl |
28 : public SchedulerT<SingleUseWorker> | 30 : public SchedulerT<SingleUseWorker> |
29 { | 31 { |
30 public: | 32 public: |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 public: | 69 public: |
68 static void Init() | 70 static void Init() |
69 { | 71 { |
70 // it's threadsafe since C++11 and it will be instantiated only once and | 72 // it's threadsafe since C++11 and it will be instantiated only once and |
71 // destroyed at the application exit | 73 // destroyed at the application exit |
72 static V8Initializer initializer; | 74 static V8Initializer initializer; |
73 } | 75 } |
74 }; | 76 }; |
75 } | 77 } |
76 | 78 |
| 79 V8ExecutionScope::V8ExecutionScope(JsEngineInternal* engine) |
| 80 : lock(engine->GetIsolate()), |
| 81 isolateScope(engine->GetIsolate()), |
| 82 handleScope(engine->GetIsolate()), |
| 83 contextScope(engine->GetContextAsLocal()) |
| 84 {} |
| 85 |
77 AdblockPlus::ScopedV8Isolate::ScopedV8Isolate() | 86 AdblockPlus::ScopedV8Isolate::ScopedV8Isolate() |
78 { | 87 { |
79 V8Initializer::Init(); | 88 V8Initializer::Init(); |
80 isolate = v8::Isolate::New(); | 89 isolate = v8::Isolate::New(); |
81 } | 90 } |
82 | 91 |
83 AdblockPlus::ScopedV8Isolate::~ScopedV8Isolate() | 92 AdblockPlus::ScopedV8Isolate::~ScopedV8Isolate() |
84 { | 93 { |
85 isolate->Dispose(); | 94 isolate->Dispose(); |
86 isolate = nullptr; | 95 isolate = nullptr; |
87 } | 96 } |
88 | 97 |
89 AdblockPlus::JsEngine::JsEngine(const ScopedV8IsolatePtr& isolate) | 98 AdblockPlus::JsEngine::JsEngine(const ScopedV8IsolatePtr& isolate) |
90 : isolate(isolate), | 99 : isolate(isolate), |
91 scheduler(new SchedulerImpl()) // use std::make_unique after we upgrade out
of VS2012 | 100 scheduler(new SchedulerImpl()) // TODO: make_unique once available |
92 {} | 101 {} |
93 | 102 |
| 103 JsEngineInternal::JsEngineInternal(const AdblockPlus::ScopedV8IsolatePtr& isolat
e) |
| 104 : AdblockPlus::JsEngine(isolate), |
| 105 context(isolate->Get(),v8::Context::New(isolate->Get())) |
| 106 { |
| 107 /* |
| 108 * Enter v8 scope for our context so that we can initialize its global object. |
| 109 */ |
| 110 const v8::Context::Scope contextScope(GetContextAsLocal()); |
| 111 auto globalObject = GetGlobalObject(); |
| 112 auto propertyName = AdblockPlus::Utils::ToV8String(GetIsolate(), "setTimeout")
; |
| 113 globalObject->Set(propertyName, MakeCallback(::CallbackForSetTimeout)); |
| 114 // TODO: Move the rest of the global object initializations here |
| 115 } |
| 116 |
| 117 /** |
| 118 * \par Design Notes |
| 119 * It is technically necessary to construct JsEngine instances *only* within a f
actory. |
| 120 * Initialization requires that certain transient v8 scopes be set up |
| 121 * before initialization and torn down afterwards. |
| 122 * C++ has no syntax to use anything like a sentry object in the constructor its
elf. |
| 123 * Thus we need to establish v8 scope within every C++ scope that constructs an
object. |
| 124 */ |
94 AdblockPlus::JsEnginePtr AdblockPlus::JsEngine::New(const AppInfo& appInfo, cons
t ScopedV8IsolatePtr& isolate) | 125 AdblockPlus::JsEnginePtr AdblockPlus::JsEngine::New(const AppInfo& appInfo, cons
t ScopedV8IsolatePtr& isolate) |
95 { | 126 { |
96 JsEnginePtr result(new JsEngine(isolate)); | 127 auto isolateP = isolate->Get(); |
| 128 /* |
| 129 * TODO: Remove `locker`. |
| 130 * Until #3595 is fixed, unit tests allocate isolates outside of this class. |
| 131 * Once we're no longer doing that, we may assume that this class holds |
| 132 * exclusive access to the nascent isolate. |
| 133 * The factory and constructor constitute a single-threaded usage, |
| 134 * and there will be no need to lock the isolate. |
| 135 */ |
| 136 const v8::Locker locker(isolateP); |
| 137 /* |
| 138 * Set up v8 scopes for isolate and handle. |
| 139 * We cannot set up the v8 scope for context because it doesn't exist |
| 140 * until after the constructor for `JsEngineInternal` returns. |
| 141 */ |
| 142 const v8::Isolate::Scope isolateScope(isolateP); |
| 143 const v8::HandleScope handleScope(isolateP); |
97 | 144 |
98 const v8::Locker locker(result->GetIsolate()); | 145 std::shared_ptr<JsEngineInternal> engine(std::make_shared<JsEngineInternal>(is
olate)); |
99 const v8::Isolate::Scope isolateScope(result->GetIsolate()); | |
100 const v8::HandleScope handleScope(result->GetIsolate()); | |
101 | 146 |
102 result->context.reset(new v8::Persistent<v8::Context>(result->GetIsolate(), | 147 JsEnginePtr result(engine); |
103 v8::Context::New(result->GetIsolate()))); | 148 // Establish a context scope for the legacy setup of the global object |
| 149 const v8::Context::Scope contextScope(engine->GetContextAsLocal()); |
104 AdblockPlus::GlobalJsObject::Setup(result, appInfo, result->GetGlobalObject())
; | 150 AdblockPlus::GlobalJsObject::Setup(result, appInfo, result->GetGlobalObject())
; |
105 return result; | 151 return result; |
106 } | 152 } |
107 | 153 |
| 154 v8::Local<v8::Context> JsEngineInternal::GetContextAsLocal() const |
| 155 { |
| 156 return v8::Local<v8::Context>::New(isolate->Get(), context); |
| 157 } |
| 158 |
108 AdblockPlus::JsValuePtr AdblockPlus::JsEngine::GetGlobalObject() | 159 AdblockPlus::JsValuePtr AdblockPlus::JsEngine::GetGlobalObject() |
109 { | 160 { |
110 JsContext context(shared_from_this()); | 161 return JsValuePtr(new JsValue(shared_from_this(), ToInternal(this)->GetGlobalO
bject())); |
111 return JsValuePtr(new JsValue(shared_from_this(), context.GetV8Context()->Glob
al())); | 162 } |
| 163 |
| 164 v8::Local<v8::Object> JsEngineInternal::GetGlobalObject() |
| 165 { |
| 166 return GetContextAsLocal()->Global(); |
| 167 } |
| 168 |
| 169 v8::Local<v8::Value> JsEngineInternal::ApplyFunction( |
| 170 v8::Local<v8::Function> func, |
| 171 AllocatedArray<v8::Local<v8::Value>> args) |
| 172 { |
| 173 return ApplyFunction(GetGlobalObject(), func, std::move(args)); |
| 174 } |
| 175 |
| 176 v8::Local<v8::Value> JsEngineInternal::ApplyFunction( |
| 177 v8::Local<v8::Object> thisObject, |
| 178 v8::Local<v8::Function> func, |
| 179 AllocatedArray<v8::Local<v8::Value>> args) |
| 180 { |
| 181 const v8::TryCatch tryCatch; |
| 182 v8::Local<v8::Value> result = func->Call(thisObject, args.Size(), args.Get()); |
| 183 if (tryCatch.HasCaught()) |
| 184 { |
| 185 throw AdblockPlus::JsError(tryCatch.Exception(), tryCatch.Message()); |
| 186 } |
| 187 return result; |
112 } | 188 } |
113 | 189 |
114 AdblockPlus::JsValuePtr AdblockPlus::JsEngine::Evaluate(const std::string& sourc
e, | 190 AdblockPlus::JsValuePtr AdblockPlus::JsEngine::Evaluate(const std::string& sourc
e, |
115 const std::string& filename) | 191 const std::string& filename) |
116 { | 192 { |
117 const JsContext context(shared_from_this()); | 193 const JsContext context(shared_from_this()); |
118 const v8::TryCatch tryCatch; | 194 const v8::TryCatch tryCatch; |
119 const v8::Handle<v8::Script> script = CompileScript(GetIsolate(), source, | 195 const v8::Handle<v8::Script> script = CompileScript(GetIsolate(), source, |
120 filename); | 196 filename); |
121 CheckTryCatch(tryCatch); | 197 CheckTryCatch(tryCatch); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
192 const v8::Local<const v8::External> external = | 268 const v8::Local<const v8::External> external = |
193 v8::Local<const v8::External>::Cast(arguments.Data()); | 269 v8::Local<const v8::External>::Cast(arguments.Data()); |
194 std::weak_ptr<JsEngine>* data = | 270 std::weak_ptr<JsEngine>* data = |
195 static_cast<std::weak_ptr<JsEngine>*>(external->Value()); | 271 static_cast<std::weak_ptr<JsEngine>*>(external->Value()); |
196 JsEnginePtr result = data->lock(); | 272 JsEnginePtr result = data->lock(); |
197 if (!result) | 273 if (!result) |
198 throw std::runtime_error("Oops, our JsEngine is gone, how did that happen?")
; | 274 throw std::runtime_error("Oops, our JsEngine is gone, how did that happen?")
; |
199 return result; | 275 return result; |
200 } | 276 } |
201 | 277 |
| 278 /** |
| 279 * \par Implementation Notes |
| 280 * We initialize the `Data()` element of the callback arguments with `this`, |
| 281 * which raises an issue about life cycle. |
| 282 * The result of `FunctionTemplate::New` is to create a function |
| 283 * whose lifespan is the same as the context it's created in. |
| 284 * The context is an instance member of the engine, |
| 285 * so as long as the engine exists, so does the context. |
| 286 * Since evaluation in v8 only occurs during the ordinary lifespan of the engine |
| 287 * (i.e. after the constructor and before the destructor), |
| 288 * the `this` pointer will be valid whenever the callback function might be ca
lled. |
| 289 */ |
| 290 v8::Local<v8::Function> JsEngineInternal::MakeCallback(v8::InvocationCallback ca
llback) |
| 291 { |
| 292 return v8::FunctionTemplate::New(callback, v8::External::New(this))->GetFuncti
on(); |
| 293 } |
| 294 |
| 295 JsEngineInternal* JsEngineInternal::ExtractEngine(const v8::Arguments& arguments
) |
| 296 { |
| 297 return static_cast<JsEngineInternal*>(v8::Local<v8::External>::Cast(arguments.
Data())->Value()); |
| 298 } |
| 299 |
202 AdblockPlus::JsValueList AdblockPlus::JsEngine::ConvertArguments(const v8::Argum
ents& arguments) | 300 AdblockPlus::JsValueList AdblockPlus::JsEngine::ConvertArguments(const v8::Argum
ents& arguments) |
203 { | 301 { |
204 const JsContext context(shared_from_this()); | 302 const JsContext context(shared_from_this()); |
205 JsValueList list; | 303 JsValueList list; |
206 for (int i = 0; i < arguments.Length(); i++) | 304 for (int i = 0; i < arguments.Length(); i++) |
207 list.push_back(JsValuePtr(new JsValue(shared_from_this(), arguments[i]))); | 305 list.push_back(JsValuePtr(new JsValue(shared_from_this(), arguments[i]))); |
208 return list; | 306 return list; |
209 } | 307 } |
210 | 308 |
211 AdblockPlus::FileSystemPtr AdblockPlus::JsEngine::GetFileSystem() | 309 AdblockPlus::FileSystemPtr AdblockPlus::JsEngine::GetFileSystem() |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 } | 344 } |
247 | 345 |
248 void AdblockPlus::JsEngine::SetLogSystem(AdblockPlus::LogSystemPtr val) | 346 void AdblockPlus::JsEngine::SetLogSystem(AdblockPlus::LogSystemPtr val) |
249 { | 347 { |
250 if (!val) | 348 if (!val) |
251 throw std::runtime_error("LogSystem cannot be null"); | 349 throw std::runtime_error("LogSystem cannot be null"); |
252 | 350 |
253 logSystem = val; | 351 logSystem = val; |
254 } | 352 } |
255 | 353 |
256 | |
257 void AdblockPlus::JsEngine::SetGlobalProperty(const std::string& name, | 354 void AdblockPlus::JsEngine::SetGlobalProperty(const std::string& name, |
258 AdblockPlus::JsValuePtr value) | 355 AdblockPlus::JsValuePtr value) |
259 { | 356 { |
| 357 JsContext jsContext(shared_from_this()); |
260 auto global = GetGlobalObject(); | 358 auto global = GetGlobalObject(); |
261 if (!global) | 359 if (!global) |
262 throw std::runtime_error("Global object cannot be null"); | 360 throw std::runtime_error("Global object cannot be null"); |
263 global->SetProperty(name, value); | 361 global->SetProperty(name, value); |
264 } | 362 } |
265 | 363 |
266 void AdblockPlus::JsEngine::Schedule(std::function<void()> task, | 364 void AdblockPlus::JsEngine::Schedule(std::function<void()> task, |
267 AdblockPlus::ImmediateSingleUseThreadType) | 365 AdblockPlus::ImmediateSingleUseThreadType) |
268 { | 366 { |
269 // The present version of the scheduler only does immediate and single-use | 367 // The present version of the scheduler only does immediate and single-use |
270 // It does not, however, detach threads like the legacy thread behavior did. | 368 // It does not, however, detach threads like the legacy thread behavior did. |
271 scheduler->Run(task); | 369 scheduler->Run(task); |
272 } | 370 } |
273 | 371 |
274 void AdblockPlus::JsEngine::WaitForQuietScheduler() | 372 void AdblockPlus::JsEngine::WaitForQuietScheduler() |
275 { | 373 { |
276 scheduler->JoinAll(); | 374 scheduler->JoinAll(); |
277 } | 375 } |
| 376 |
| 377 JsEngineInternal* ToInternal(AdblockPlus::JsEnginePtr p) |
| 378 { |
| 379 return static_cast<JsEngineInternal*>(p.get()); |
| 380 } |
| 381 |
| 382 JsEngineInternal* ToInternal(AdblockPlus::JsEngine* p) |
| 383 { |
| 384 return static_cast<JsEngineInternal*>(p); |
| 385 } |
OLD | NEW |