标签:v8
上篇说到一个很重要的函数initContextIfNeeded,这里专门来分析下这个函数:
// Create a new environment and setup the global object.
//
// The global object corresponds to a DOMWindow instance. However, to
// allow properties of the JS DOMWindow instance to be shadowed, we
// use a shadow object as the global object and use the JS DOMWindow
// instance as the prototype for that shadow object. The JS DOMWindow
// instance is undetectable from javascript code because the __proto__
// accessors skip that object.
//
// The shadow object and the DOMWindow instance are seen as one object
// from javascript. The javascript object that corresponds to a
// DOMWindow instance is the shadow object. When mapping a DOMWindow
// instance to a V8 object, we return the shadow object.
//
// To implement split-window, see
// 1) https://bugs.webkit.org/show_bug.cgi?id=17249
// 2) https://wiki.mozilla.org/Gecko:SplitWindow
// 3) https://bugzilla.mozilla.org/show_bug.cgi?id=296639
// we need to split the shadow object further into two objects:
// an outer window and an inner window. The inner window is the hidden
// prototype of the outer window. The inner window is the default
// global object of the context. A variable declared in the global
// scope is a property of the inner window.
//
// The outer window sticks to a Frame, it is exposed to JavaScript
// via window.window, window.self, window.parent, etc. The outer window
// has a security token which is the domain. The outer window cannot
// have its own properties. window.foo = ‘x‘ is delegated to the
// inner window.
//
// When a frame navigates to a new page, the inner window is cut off
// the outer window, and the outer window identify is preserved for
// the frame. However, a new inner window is created for the new page.
// If there are JS code holds a closure to the old inner window,
// it won‘t be able to reach the outer window via its global object.
bool V8DOMWindowShell::initContextIfNeeded()
{
// Bail out if the context has already been initialized.
if (!m_context.IsEmpty())
return false;
// Create a handle scope for all local handles.
v8::HandleScope handleScope;
// Setup the security handlers and message listener. This only has
// to be done once.
static bool isV8Initialized = false;
if (!isV8Initialized) {
// Tells V8 not to call the default OOM handler, binding code
// will handle it.
v8::V8::IgnoreOutOfMemoryException();
v8::V8::SetFatalErrorHandler(reportFatalErrorInV8);
v8::V8::SetGlobalGCPrologueCallback(&V8GCController::gcPrologue);
v8::V8::SetGlobalGCEpilogueCallback(&V8GCController::gcEpilogue);
v8::V8::AddMessageListener(&v8UncaughtExceptionHandler);
v8::V8::SetFailedAccessCheckCallbackFunction(reportUnsafeJavaScriptAccess);
#if ENABLE(JAVASCRIPT_DEBUGGER)
ScriptProfiler::initialize();
#endif
isV8Initialized = true;
}
m_context = createNewContext(m_global, 0);
if (m_context.IsEmpty())
return false;
v8::Local<v8::Context> v8Context = v8::Local<v8::Context>::New(m_context);
v8::Context::Scope contextScope(v8Context);
// Store the first global object created so we can reuse it.
if (m_global.IsEmpty()) {
m_global = v8::Persistent<v8::Object>::New(v8Context->Global());
// Bail out if allocation of the first global objects fails.
if (m_global.IsEmpty()) {
disposeContextHandles();
return false;
}
#ifndef NDEBUG
V8GCController::registerGlobalHandle(PROXY, this, m_global);
#endif
}
if (!installHiddenObjectPrototype(v8Context)) {
disposeContextHandles();
return false;
}
if (!installDOMWindow(v8Context, m_frame->domWindow())) {
disposeContextHandles();
return false;
}
updateDocument();
setSecurityToken();
m_frame->loader()->client()->didCreateScriptContextForFrame();
// FIXME: This is wrong. We should actually do this for the proper world once
// we do isolated worlds the WebCore way.
m_frame->loader()->dispatchDidClearWindowObjectInWorld(0);
return true;
}
前面的英文注释很多,同时也说明了这个函数的作用,先把注释翻译一下,也方便自己日后查看:
创建一个新的环境和建立全局对象
//
JavaScript全局对象对应于一个domwindow实例。
然而为了让domWindow实例的属性被隐藏起来,我们使用一个影子对象作为全局对象和使用JS DomWindow实例作为影子对象的属性。
JS domwindow实例在js代码中不会被检测到,因为__proto__访问器略过了这个对象。
影子对象和DomWindow 实例在javascript中被看作同一对象。对应于一个DomWindow实例的javascript对象其实是影子对象。
当映射一个DomWindow实例到一个V8对象的时候,我们返回影子对象。
//
//
为了实现分离窗口,参考:
// 1) https://bugs.webkit.org/show_bug.cgi?id=17249
// 2) https://wiki.mozilla.org/Gecko:SplitWindow
// 3) https://bugzilla.mozilla.org/show_bug.cgi?id=296639
我们需要把隐蔽的对象分成两个对象:
一个外部的window和内部的window.内部的window是外部window的隐藏原型(prototype)。内部window是上下文默认的全局对象。在全局作用域的变量声明是内部window的一个属性。
//
外部window与一个frame相关,它通过window.window,window.self,window.parent等调用暴露给javascript。外部window有一个安全的范围字段。因此window.foo = ‘x‘ 这种只代表内部window。
//
当一个frame导航到一个新页面时,内部window被从外部window切断,外部window标志用来保存frame。然而一个新的内部window被用于创建一个新的页面。如果有JS代码拥有一个以前内部window的闭包,那就不能通过全局对象访问到外部window。
在这个函数内部和之前说的那个有点类似,为所有的局部handle创建handleScope对象,创建上下文,关联上下文到作用域。
在这些步骤过后有一个 updateDocument();为新的document初始化js上下文。在updateDocument()中会调用initContextIfNeeded,为什么updateDocument这个函数会调用initContextIfNeeded呢?看注释:
// There is an existing JavaScript wrapper for the global object
// of this frame. JavaScript code in other frames might hold a
// reference to this wrapper. We eagerly initialize the JavaScript
// context for the new document to make property access on the
// global object wrapper succeed.
存在这个frame的全局对象js外壳。在其他frame的js代码可能拥有这个外壳的引用。我们急切的为这个新document初始化js上下文,是为了能够成功的访问到全局对象的外壳。
在这个updateDocument中除了初始化上下文还要更新document的wraperCache,以及更新JS安全起点和安全字段。
如有问题,欢迎大家指出
V8引擎之initContextIfNeeded(...)函数
标签:v8
原文地址:http://blog.csdn.net/jiangnanyidiao/article/details/44023453