• 翻译一篇SpiderMonkey GC的文章


    前言
      这篇文章包含了对SpiderMonkey中Rooted<T>, Handle<T>的解释。
      翻译自 https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/GC_Rooting_Guide
      原文中GC thing pointer不知如何翻译,故在此引用原文。
      下面是原文的翻译。
    简介
      这篇文章解释了如何与SpiderMonkey GC协同工作。由于SpiderMonkey的自动GC,所以熟练掌握GC中的每一个易错点是至关重要的。而SpiderMonkey中的rooting接口,就是为了简化这些工作的。
    什么是GC thing pointer
      GC thing是SpiderMonkey中被自动内存管理着的对象,主要包括以下几种:
        JS::Value
        JSObject*
        JSString*
        JSScript*
        jsid
      要注意,虽然JS::Value和jsid不是指针,但它们内部可能包含指向其他对象的指针,所以它们也算是GC thing pointer。
      如果你使用了这些对象中的任意一种,那么你就要遵循这篇文章所介绍的规范。如果你不遵守,那么你的程序就可能出错(可能会引用已经被释放的变量)。
    栈上的GC thing
      JS::Rooted<T>
        所有存储在栈上的GC thing *,包括局部变量和函数形参,都必须使用JSRooted<T>来包装。
        从程序员角度来说,JS::Rooted<T>对象使用起来就像一个原始指针一样。JS::Rooted<T>构造函数只接受一个JSContext*和一个初始值。
        引擎里已经有一些简化书写的typedef:
          typedef JS::Rooted<JS::Value> JS:RootedValue;
          typedef JS::Rooted<JS::JSObject*> JS:RootedJSObject;
          typedef JS::Rooted<JS::JSString*> JS:RootedJSString;
          typedef JS::Rooted<JS::JSScript*> JS:RootedJSScript;
          typedef JS::Rooted<JS::jsid> JS:RootedId;
        例如,你不能这样写:
          JSObject* localObj = JS_GetObjectOfSomeSort(cx);
        而应该写成这样:
          JS::RootedObject localObj(cx, JS_GetObjectOfSomeSort(cx));
        有时候你可能会忘记写JSRootedObject这样的形式,但不要担心,SpiderMonkey已经考虑到了这一点,就是下面要介绍的JSHandle<T>。
      JS::Handle<T>
        所有作为函数形参的GC thing pointer,都必须被包装成JS::Handle<T>的形式。
        JS::Handle<T>是JS::Rooted<T>的一种引用形式。你不能手动创建一个JS::Handle<T>,所有的JS::Handle<T>都是由JS::Rooted<T>隐式转换来的。
        由于只有JS::Rooted<T>会转换到JS::Handle<T>,而原始指针并不会,这样当你在函数参数里使用了原始指针而不是JS::Rooted<T>的时候,编译器就可以检测出来。
        JS::Handle<T>的行为像一个真正的引用一样,不可能改变引用的指向。
        同样,JS::Handle<T>也有一些typedef.
        下面是错误的做法:
        JSObject* someFunction(JSContext *cx, JSObject* obj) {
          // ...
        }
        下面是正确的做法:
        JSObject* someFunction(JSContext *cx, JS::HandleObject obj) {
          // ...
        }
        但是,你不能对一个输出参数用JS::Handle<T>,下面描述如何做。
      JS::MutableHandle<T>
        所有作为输出参数而使用的GC thing pointer,都必须使用JS::MutableHandle<T>。
        JS::MutableHandle<T>也是JS::Rooted<T>的一种引用,但是不像JS::Handle<T>,它可能会修改JS::Rooted<T>的内部结构。所有的JS::MutableHandle<T>除了下面2点之外,就和JS::Handle一样:
          1.具有一个set(T& t)方法
          2.必须从JS:Rooted<T>手动创建。
        下面是错误的例子:
        bool maybeGetValue(JSContext *cx, JS::Value* valueOut) {
          // ...
          if (!wasError)
            *valueOut = resultValue;
          return wasError;
        } 

        void otherFunction(JSContext *cx) {

          JS::Value value;
          bool success = maybeGetValue(cx, &value);
          // ...
        }

        下面是正确的例子
        bool maybeGetValue(JSContext *cx, JS::MutableHandleValue valueOut) {
          // ...
          if (!wasError)
            valueOut.set(resultValue);
          return wasError;
        }

        void otherFunction(JSContext *cx) {
          JS::RootedValue value(cx);
          bool success = maybeGetValue(cx, &value);
          // ...
        }

      返回值
        令人惊讶的是,返回一个原始指针是安全的!但是要注意,一旦得到了一个原始指针,就一定要用JS::Rooted<T>去包装它。

      AutoRooters
        GC thing pointer在使用前应该尽可能地被JS::Rooted<T>包装。但是有些情况下,不可能使用JS::Rooted<T>或担心使用了JS::Rooted<T>而产生的性能问题,这时可以使用AutoRooter。
          typedef JS::Value[] AutoArrayRooter
          typedef js::Vector<JS::Value> AutoValueVector
          typedef js::Vector<jsid> AutoIdVector
          typedef js::Vector<JSObject*> AutoObjectVector
          typedef js::Vector<JSScript*> AutoScriptVector
        如果上面的这些typedef不能满足你的要求,请自己继承JS::CustomAutoRooter类,并重写virtual trace()方法。

    堆上的GC thing pointer
      在堆上的GC thing pointer必须使用JS::Heap<T>,除非你已经使用了JS_Add<T>Root()或者JS::PersistentRooted,但除非必要,请不要使用这两种方法。但是JS::Heap<T>* 需要你自己管理。
      JS::Heap<T>的构造函数不需要JSContext*,可以传一个初始值,也可以不传。像上面的其他模板类一样,它的行为也像原始指针。
      有时候你创建了一个结构,它既包括堆上的GC thing pointer也包括栈上的GC thing pointer,这个情况无解,请重构你的程序。
      对于JS::Heap<T>,暂时没有typedef。
      错误的做法:
      struct HeapStruct
      {
        JSObject* mSomeObject;
        JS::Value mSomeValue;
      };
      正确的做法:
      struct HeapStruct
      {
        JS::Heap<JSObject*> mSomeObject;
        JS::Heap<JS::Value> mSomeValue;
      };

    总结
      对栈上的局部变量,使用JS::Rooted<T>
      对函数参数,使用JS::Handle<T>
      对函数的输出参数,使用JS::MutableHandle<T>
      使用从JS::Rooted<T>到JS::Handle<T>的隐式转换
      使用从&JS::Rooted<T>到JS::MutableHandle<T>的显式转换
      函数返回值可以是原始指针
      对于集合,使用JS::Rooted<T>或AutoRooter
      对于堆上的数据,使用JS::Heap<T>。但是Heap<T>本身需要你自己管理。
      别在堆上使用JS::Rooted<T>, JS::Handle<T>和JS::MutableHandle<T>
      函数参数别使用JS::Rooted<T>,使用JS::Handle<T>或JS::MutableHandle<T>
      JS::PersistentRooted<T>会使变量常驻内存,直到程序结束。

  • 相关阅读:
    java 泛型 类型作为参量 Class<T> transform
    面向对象的类方法只具有命名空间的作用
    编程语言沉思录—编程语言的体系结构
    类型约束的作用
    函数重载、多态与型变
    函数类型与型变
    型变(逆变)函数
    scala 型变
    泛型编程的几个关键概念
    泛型是包含类型参数的抽象类型—类型构造器
  • 原文地址:https://www.cnblogs.com/zzrom/p/5387903.html
Copyright © 2020-2023  润新知