polardbxengine/storage/ndb/nodejs/jones-ndb/impl/include/common/JsWrapper.h

330 lines
11 KiB
C++

/*
Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef NODEJS_ADAPTER_INCLUDE_JSWRAPPER_H
#define NODEJS_ADAPTER_INCLUDE_JSWRAPPER_H
#include <node.h>
#include "unified_debug.h"
using v8::Isolate;
using v8::Persistent;
using v8::Eternal;
using v8::ObjectTemplate;
using v8::EscapableHandleScope;
using v8::Handle;
using v8::Local;
using v8::Object;
using v8::Value;
using v8::Exception;
using v8::String;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::PropertyCallbackInfo;
/* A Persistent<T> can be cast to a Local<T>. See:
https://groups.google.com/forum/#!msg/v8-users/6kSAbnUb-rQ/9G5RmCpsDIMJ.
At some point this can be replaced by persisent.Get(isolate)
*/
template<class T>
inline Local<T> ToLocal(Persistent<T>* p_) {
return *reinterpret_cast<Local<T>*>(p_);
}
template<class T>
inline Local<T> ToLocal(const Persistent<T>* p_) {
return *reinterpret_cast<const Local<T>*>(p_);
}
/* Some compatibility */
#if NODE_MAJOR_VERSION > 3
#define BUFFER_HANDLE v8::MaybeLocal
#define LOCAL_BUFFER(B) B.ToLocalChecked()
#define IsExternalAscii IsExternalOneByte
#define COPY_TO_BUFFER(Iso,Data,Len) node::Buffer::Copy(Iso,Data,Len).ToLocalChecked()
#define USE_FOR_BUFFER(Iso,Data,Len) node::Buffer::New(Iso,Data,Len).ToLocalChecked()
#define RECLAIM_PARAM v8::WeakCallbackInfo< GcReclaimer<CPP_OBJECT> >
#define EXTRA_SET_WEAK ,v8::WeakCallbackType::kParameter
#else
#define BUFFER_HANDLE v8::Local
#define LOCAL_BUFFER(B) B
#define COPY_TO_BUFFER(Iso,Data,Len) node::Buffer::New(Iso,Data,Len)
#define USE_FOR_BUFFER(Iso,Data,Len) node::Buffer::Use(Iso,Data,Len)
#define RECLAIM_PARAM v8::WeakCallbackData<Value, GcReclaimer<CPP_OBJECT> >
#define EXTRA_SET_WEAK
#endif
/* Signature of a V8 function wrapper
*/
typedef FunctionCallbackInfo<Value> Arguments;
typedef void V8WrapperFn(const Arguments &);
/* Signatures for property setters & getters
*/
typedef PropertyCallbackInfo<Value> AccessorInfo; // for getter
typedef PropertyCallbackInfo<void> SetterInfo;
typedef void (*Getter) (Local<String>, const AccessorInfo &);
typedef void (*Setter) (Local<String>, Local<Value>, const SetterInfo &);
/*****************************************************************
Code to confirm that C++ types wrapped as JavaScript values
are unwrapped back to the original type. This can be disabled.
ENABLE_WRAPPER_TYPE_CHECKS is defined in adapter_global.h
******************************************************************/
#if ENABLE_WRAPPER_TYPE_CHECKS
#include <typeinfo>
inline void check_class_id(const char *a, const char *b) {
if(a != b) {
fprintf(stderr, " !!! Expected %s but unwrapped %s !!!\n", b, a);
assert(a == b);
}
}
#define TYPE_CHECK_T(x) const char * x
#define SET_CLASS_ID(env, PTR) env.class_id = typeid(PTR).name()
#define SET_THIS_CLASS_ID(PTR) class_id = typeid(PTR).name()
#define CHECK_CLASS_ID(env, PTR) check_class_id(env->class_id, typeid(PTR).name())
#else
#define TYPE_CHECK_T(x)
#define SET_CLASS_ID(env, PTR)
#define SET_THIS_CLASS_ID(PTR)
#define CHECK_CLASS_ID(env, PTR)
#endif
/* Delete a native C++ object when the Garbage Collector reclaims its
JavaScript handle.
The GcReclaimer holds the pointer to be deleted, its classname for
debugging output, and the weak JS reference (which should be Reset).
*/
template<typename CPP_OBJECT> class GcReclaimer; // forward declaration
template<typename CPP_OBJECT>
void onGcReclaim(const RECLAIM_PARAM & data) {
GcReclaimer<CPP_OBJECT> * reclaimer = data.GetParameter();
reclaimer->reclaim();
delete reclaimer;
}
template<typename CPP_OBJECT> class GcReclaimer {
public:
GcReclaimer(const char * cls, CPP_OBJECT * p) : classname(cls), ptr(p) { }
void SetWeakReference(Isolate *isolate, Handle<Value> obj) {
notifier.Reset(isolate, obj);
notifier.MarkIndependent();
notifier.SetWeak(this, onGcReclaim<CPP_OBJECT> EXTRA_SET_WEAK);
}
void reclaim() {
delete ptr;
DEBUG_PRINT_DETAIL("GC Reclaim %s %p", classname, ptr);
notifier.Reset();
}
private:
const char * classname;
CPP_OBJECT * ptr;
Persistent<Value> notifier;
};
/*****************************************************************
An Envelope is a simple structure providing some safety
& convenience for wrapped classes.
All objects are wrapped using two internal fields.
The first points to the envelope; the second to the object itself.
******************************************************************/
class Envelope {
public:
/* Instance variables */
int magic; // for safety when unwrapping
TYPE_CHECK_T(class_id); // for checking type of wrapped object
const char * classname; // for debugging output
Eternal<ObjectTemplate> stencil; // for creating JavaScript objects
Isolate * isolate;
bool isVO;
/* Constructor */
Envelope(const char *name) :
magic(0xF00D),
classname(name),
isolate(Isolate::GetCurrent()),
isVO(false)
{
EscapableHandleScope scope(isolate);
Local<ObjectTemplate> proto = ObjectTemplate::New(isolate);
proto->SetInternalFieldCount(2);
stencil.Set(isolate, proto);
}
/* Instance Methods */
Local<Object> newWrapper() {
return stencil.Get(isolate)->NewInstance();
}
void addMethod(const char *name, V8WrapperFn wrapper) {
stencil.Get(isolate)->Set(
String::NewFromUtf8(isolate, name, v8::String::kInternalizedString),
FunctionTemplate::New(isolate, wrapper)
);
}
template<typename PTR>
Local<Value> wrap(PTR ptr) {
if(ptr) {
DEBUG_PRINT("Envelope wrapping %s: %p", classname, ptr);
SET_THIS_CLASS_ID(PTR);
Local<Object> wrapper = newWrapper();
wrapper->SetAlignedPointerInInternalField(0, (void *) this);
wrapper->SetAlignedPointerInInternalField(1, (void *) ptr);
return wrapper;
}
/* But if ptr was null, return a JavaScript null: */
return Null(isolate);
}
/* An overloaded wrap() method for the special case of converting a
const char * to a JS String.
*/
Local<Value> wrap(const char * str) {
return String::NewFromUtf8(isolate, str);
}
void addAccessor(const char *name, Getter accessor) {
stencil.Get(isolate)->SetAccessor(
String::NewFromUtf8(isolate, name, v8::String::kInternalizedString),
accessor
);
}
void addAccessor(Local<String> name, Getter getter,
Setter setter = 0,
Handle<Value> data = Handle<Value>()) {
stencil.Get(isolate)->SetNativeDataProperty(name, getter, setter, data,
v8::DontDelete,
Local<v8::AccessorSignature>(),
v8::DEFAULT);
}
/*****************************************************************
Create a weak handle for a wrapped object.
Use it to delete the wrapped object when the GC wants to reclaim the handle.
Don't do this if ptr is null (and wrapper object is therefore JS Null).
For safety, the compiler will not let you use this on any "const PTR" type;
(if you hold a const pointer to something, you probably don't own its
memory allocation).
******************************************************************/
template<typename P>
void freeFromGC(P * ptr, Handle<Value> obj) {
if(ptr) {
GcReclaimer<P> * reclaimer = new GcReclaimer<P>(classname, ptr);
reclaimer->SetWeakReference(isolate, obj);
}
}
};
/*****************************************************************
Construct a wrapped object.
arg0: pointer to the object to be wrapped.
arg1: an Envelope reference
arg2: a reference to a v8 object, which must have already been
initialized from a proper ObjectTemplate.
The *usual* case is to use Envelope.wrap() instead of this.
******************************************************************/
template <typename PTR>
void wrapPointerInObject(PTR ptr,
Envelope & env,
Handle<Object> obj) {
DEBUG_PRINT("wrapPointerInObject for %s: %p", env.classname, ptr);
DEBUG_ASSERT(obj->InternalFieldCount() == 2);
SET_CLASS_ID(env, PTR);
obj->SetAlignedPointerInInternalField(0, (void *) & env);
obj->SetAlignedPointerInInternalField(1, (void *) ptr);
}
/* Specializations for non-pointers reduce gcc warnings.
Only specialize over primitive types. */
template <> inline void wrapPointerInObject(int, Envelope &, Handle<Object>) {
assert(0);
}
template <> inline void wrapPointerInObject(unsigned long long int, Envelope &, Handle<Object>) {
assert(0);
}
template <> inline void wrapPointerInObject(unsigned int, Envelope &, Handle<Object>) {
assert(0);
}
template <> inline void wrapPointerInObject(double, Envelope &, Handle<Object>) {
assert(0);
}
/*****************************************************************
Unwrap a native pointer from a JavaScript object
arg0: a reference to a v8 object, which must have already been
initialized from a proper ObjectTemplate.
TODO: Find a way to prevent wrapping a pointer as one
type and unwrapping it as another.
******************************************************************/
template <typename PTR>
PTR unwrapPointer(Handle<Object> obj) {
PTR ptr;
DEBUG_ASSERT(obj->InternalFieldCount() == 2);
ptr = static_cast<PTR>(obj->GetAlignedPointerFromInternalField(1));
#ifdef UNIFIED_DEBUG
Envelope * env = static_cast<Envelope *>(obj->GetAlignedPointerFromInternalField(0));
assert(env->magic == 0xF00D);
CHECK_CLASS_ID(env, PTR);
DEBUG_PRINT_DETAIL("Unwrapping %s: %p", env->classname, ptr);
#endif
return ptr;
}
/*****************************************************************
Capture an error message from a C++ routine
Provide a method to run later (in the v8 main JavaScript thread)
and generate a JavaScript Error object from the message
******************************************************************/
class NativeCodeError {
public:
const char * message;
NativeCodeError(const char * msg) : message(msg) {}
virtual ~NativeCodeError() {}
virtual Local<Value> toJS() {
EscapableHandleScope scope(Isolate::GetCurrent());
return scope.Escape(Exception::Error(
String::NewFromUtf8(Isolate::GetCurrent(), message)));
}
};
#endif