Chromium Study

V8 and Blink

토픽 Chromium Study

V8 and Blink

Isolate, Context, World

V8 API를 다루는 코딩을 할 때, Isolate, Context, World의 개념을 이해하는 것이 중요합니다. 이것들은 코드베이스에서 각각 v8::Isolate, v8::Context, DOMWrapperWorld로 표현됩니다.

 

Isolate는 물리적인 쓰레드에 해당합니다. Isolate와 블링크의 물리적 쓰레드는 1:1 관계입니다. 메인 쓰레드는 자체 Isolate를 가집니다. 워커 쓰레드도 자체 Isolate를 가집니다.

 

Context는 전역 객체에 해당합니다(프레임의 경우 프레임의 윈도우 객체). 각 프레임은 자체 윈도우 객체를 가지기 때문에 렌더러 프로세스에는 Context가 여러개 있게 됩니다. V8 API를 호출할 때에는 올바른 컨텍스트 내에 있는지 확인해야 합니다. 그렇지 않으면, v8::Isolate::GetCurrentContext()가 잘못된 context를 반환하고, 최악의 경우 객체 누수로 보안상 문제가 발생할 수 있습니다.

 

World는 Chrome 확장기능의 컨텐츠 스크립트(content script)를 지원하기위한 개념입니다. World는 웹표준의 어느 것과도 일치하지 않습니다. 컨텐츠 스크립트는 웹페이지의 DOM을 공유하고자 합니다. 그러나 보안상의 이유로, 컨텐츠 스크립트의 자바스크립트 객체는 웹페이지의 자바스크립트 힙메모리와 격리되어야 합니다. (또한 어느 한 컨텐츠 스크립트의 자바스크립트 힙메모리는 다른 컨텐츠 스크립트의 자바스크립트 힙메모리와 격리되어야 합니다.) 격리를 현실화하기 위하여, 메인 쓰레드는 웹페이지를 위한 하나의 메인 world와 각 컨텐츠 스크립트를 위한 하나의 격리된 world를 생성합니다. 메인 world와 격리된 world는 같은 C++ DOM 객체에 접근할 수 있지만 그것들의 자바스크립트 객체들은 격리되어 있습니다. 이 격리는 하나의 C++ DOM 객체를 감싼 다중 V8 래퍼(wrapper)로 구현됩니다. 즉, world 당 하나의 V8 래퍼.

 

 

컨텍스트와 World와 프레임의 관계는 어떻게 될까요?

 

메인 쓰레드에 N개의 World가 있다고 상상해 보십시오(one main world + (N - 1) 격리된 world). 그렇다면 한 프레임은 N개의 윈도우 객체를 가지고, 각각은 하나의 world를 위해 사용됩니다. 컨텍스트는 윈도우 객체에 해당하는 개념입니다. 이것은 M개의 프레임과 N개의 월드가 있다할 때, M * N 개의 컨텍스트가 있다는 뜻입니다(단, 컨텍스트는 지연 생성이 됩니다).

 

워커의 경우에는, 오직 하나의 월드와 하나의 광역 객체가 있습니다. 따라서 오직 하나의 컨텍스트만 있습니다.

 

다시 말하지만, V8 API를 사용할 때에는 올바른 컨텍스트를 사용하도록 정말 주의해야합니다. 그렇지 않으면 격리된 월드간에 자바스크립트 객체가 누출되어 보안 재해가 발생할 수 있습니다(예, A.com의 확장기능이 B.com의 확장기능을 조작할 수 있음).

 

If you want to learn more:

 

V8 APIs

다수의 V8 API가 //v8/include/v8.h에 정의되어 있습니다. V8 API는 저수준이고 제대로 사용하기 어렵기 때문에, platform/bindings/에서 V8 API를 감싼 한 무리의 헬퍼 클래스를 제공합니다. 개발자는 가능한 한 헬퍼 클래스를 사용하는 것을 고려해야 합니다. 코드 내에서 V8 API를 많이 사용한다면 그 파일은 bindings/{core, modules} 디렉토리에 위치해야 합니다.

 

V8은 V8 객체를 가리키기 위하여 핸들을 사용합니다. 가장 일반적인 핸들은 v8::Local<>이며, 머신 스택의 V8 객체를 가리키는데 사용합니다. v8::HandleScope를 머신 스택에 할당한 후에 v8::Local<>가 사용되어야 합니다. v8::Local<>은 머신 스택 외부에서 사용해서는 안됩니다:

 

void function() {

  v8::HandleScope scope;

 v8::Local<v8::Object> object = ...;  // This is correct.

}

 

class SomeObject : public GarbageCollected<SomeObject> {

 v8::Local<v8::Object> object_;  // This is wrong.

};

 

머신 스택 외부의 V8 객체를 가리키려면, wrapper tracing을 사용해야만 합니다. 어쨌건 이것과 함께 참조 사이클을 생성하지 않도록 정말 주의해야 합니다. 일반 V8 API는 사용하기 까다롭습니다. 하려는 작업에 확신이 없다면 blink-review-bindings@ 그룹에 문의하십시오.

 

If you want to learn more:

 

  • How to use V8 APIs and helper classes: platform/bindings/HowToUseV8FromBlink.md

 

V8 wrappers

각 C++ DOM 객체(예, 노드)는 각자의 해당 V8 래퍼를 가지고 있습니다. 정확히 말하자면, 각 C++ DOM 객체는 월드마다 각자의 상응하는 래퍼를 가지고 있습니다.

 

V8 래퍼에는 상응하는 C++ DOM 객체에대한 강력한 참조가 있습니다. 반면에 C++ DOM 객체는 V8 래퍼와 약한 연결을 가지고 있습니다. 그래서 V8 래퍼를 일정 시간 동안 활성 상태로 유지하려면, 명시적으로 수행하여야 합니다. 그렇지 않으면 V8 래퍼가 일찍 수집되고 V8 래퍼위의 JS 프로퍼티가 유실될 것 입니다...

 

div = document.getElementbyId("div");

child = div.firstChild;

child.foo = "bar";

child = null;

gc();  // If we don't do anything, the V8 wrapper of |firstChild| is collected by the GC.

assert(div.firstChild.foo === "bar");  //...and this will fail.

 

If we don't do anything, child is collected by the GC and thus child.foo is lost. To keep the V8 wrapper of div.firstChild alive, we have to add a mechanism that "keeps the V8 wrapper of div.firstChild alive as long as the DOM tree which div belongs to is reachable from V8".

 

아무것도 하지 않으면, child는 GC에 의해 수집될 것이므로 child.foo는 유실될 것입니다. V8 래퍼의 div.firstChild를 유지하기 위해, “div에 속한 DOM 트리가 V8로부터 접근가능한 동안 div.firstChild의 V8 래퍼가 유지되도록 하는” 메커니즘을 추가하여야 합니다.

 

There are two ways to keep V8 wrappers alive: ActiveScriptWrappable and wrapper tracing.

 

V8 래퍼를 유지하는 두 가지 방법이 있습니다: ActiveScriptWrappablewrapper tracing.

 

If you want to learn more:

 

댓글

댓글 본문