this, This and this

How To Wrap A Native Object





May 30th, 2014

Lu Yuan

The meaning of title

1. this


In JavaScript

what is "this" ?



CONTEXT

RUNTIME

DYNAMIC

The meaning of title

2. This


In V8 API

v8::Arguments::This()

v8::AccessorInfo::This()


Represent "this" in JavaScript

The meaning of title

3. this


In native

This object

The wrapped objects we met


In web browser:
DOM Elements
BOM Elements
Plugins

In Node.js:
Objects in native modules
Native extensions

A Compatibility Issue

SD9005: IE 中一个对象的 native 方法是跟该对象绑定的

<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="d1">Div Element</div>
<script type="text/javascript">
    var $1 = function(id){return document.getElementById(id);};
    var $2 = document.getElementById;

    alert($1('d1'));
    alert($2('d1'));
</script>
</body>
</html>
TypeError: undefined is not a function 

丢失的 this 引用

————周爱民

document.getElementById = (function(fn){
    return function(){
        return fn.apply(document, arguments);
    };
})(document.getElementById);

var $2 = document.getElementById;
    


Why?
"document" object is wrapped in non-IE browsers.

How to wrap it?


KEYWORDS:

SetAlignedPointerInInternalField
void v8::Object::SetAlignedPointerInInternalField(int index, void* 	value)
GetAlignedPointerInInternalField
v8::Object::V8_INLINE(void * GetAlignedPointerFromInternalField(int index))  

A Wrapper

node_object_wrap.h
class NODE_EXTERN ObjectWrap {
 public:
  ObjectWrap ( ) {
    refs_ = 0;
  }


  virtual ~ObjectWrap ( ) {
    if (!handle_.IsEmpty()) {
      assert(handle_.IsNearDeath());
      handle_.ClearWeak();
      handle_->SetPointerInInternalField(0, 0);
      handle_.Dispose();
      handle_.Clear();
    }
  }


  template 
  static inline T* Unwrap (v8::Handle<:object> handle) {
    assert(!handle.IsEmpty());
    assert(handle->InternalFieldCount() > 0);
    return static_cast(handle->GetPointerFromInternalField(0));
  }


  v8::Persistent<:object> handle_; // ro

 protected:
  inline void Wrap (v8::Handle<:object> handle) {
    assert(handle_.IsEmpty());
    assert(handle->InternalFieldCount() > 0);
    handle_ = v8::Persistent<:object>::New(handle);
    handle_->SetPointerInInternalField(0, this);
    MakeWeak();
  }


  inline void MakeWeak (void) {
    handle_.MakeWeak(this, WeakCallback);
    handle_.MarkIndependent();
  }

  /* Ref() marks the object as being attached to an event loop.
   * Refed objects will not be garbage collected, even if
   * all references are lost.
   */
  virtual void Ref() {
    assert(!handle_.IsEmpty());
    refs_++;
    handle_.ClearWeak();
  }

  /* Unref() marks an object as detached from the event loop.  This is its
   * default state.  When an object with a "weak" reference changes from
   * attached to detached state it will be freed. Be careful not to access
   * the object after making this call as it might be gone!
   * (A "weak reference" means an object that only has a
   * persistant handle.)
   *
   * DO NOT CALL THIS FROM DESTRUCTOR
   */
  virtual void Unref() {
    assert(!handle_.IsEmpty());
    assert(!handle_.IsWeak());
    assert(refs_ > 0);
    if (--refs_ == 0) { MakeWeak(); }
  }


  int refs_; // ro


 private:
  static void WeakCallback (v8::Persistent<:value> value, void *data) {
    v8::HandleScope scope;

    ObjectWrap *obj = static_cast(data);
    assert(value == obj->handle_);
    assert(!obj->refs_);
    assert(value.IsNearDeath());
    delete obj;
  }
}; 



A Demo

this.cc
#include <node.h>
#include <v8.h>
#include <string>

using namespace v8;

class This : public node::ObjectWrap {
 public:
  This() : node::ObjectWrap(), str_("This is a test.") {}
  ~This() {}

  static Handle New(const Arguments& args) {
    HandleScope scope;

    This* _this_ = new This;
    _this_->Wrap(args.This());

    return Undefined();
  }

  static Handle Test(const Arguments& args) {
    HandleScope scope;

    This* _this_ = node::ObjectWrap::Unwrap(args.This());
    
    if (!_this_) {
      return ThrowException(Exception::TypeError(String::New("Illegal invocation")));
    }

    return scope.Close(String::New(_this_->Test().c_str()));
  }

private:
  std::string Test() {
    return str_;
  }

  std::string str_;
};

void init(Handle target) {
  HandleScope scope;

  Local t = FunctionTemplate::New(This::New);
  t->InstanceTemplate()->SetInternalFieldCount(1);
  
  t->SetClassName(String::NewSymbol("This"));
  NODE_SET_PROTOTYPE_METHOD(t, "test", This::Test);
  target->Set(String::NewSymbol("ThisInstance"), t->GetFunction()->NewInstance());
}

NODE_MODULE(this, init);


A Demo

Edit binding.gyp script
{
  "targets": [
    {
      "target_name": "this",
      "sources": [ "this.cc" ]
    }
  ]
} 
Compile with node-gyp
$ node-gyp configure
$ node-gyp build 
Test
$ node
> var t = require('./build/Release/this');
> t.ThisInstance.test();  // "This is a test." 

Reproduce the issue


$ node
> var t = require('./build/Release/this');
> var a = t.ThisInstance.test;
> a();

"this" in C++ native is a null pointer.




Thanks

this, This and this

By luyuan

this, This and this

this, This and this - How to wrap a native object

  • 1,283