I'm glad to finally say I have a good start as far as I can tell for implementing a good OpenCV addon for node js.
Its not a sprint but a marathon, OpenCV is one of the largest, most complex libraries I had to deal with and I've tested many ways to implement it. finally found a sound way to actually do it.
The adventure started some time ago when I attempted to use a few functions to display an augmented reality diffusion tensor file on a marker with a tablet (I got some help from Frank from DSI Studio, Thanks Frank!), it later became a challenge as I learned more and more about C++, Node js and OpenCV.
The first few things I've tried was using ffmpeg api in node js, ffmpeg is written in C, which lacks automatic object lifetime management (constructors/destructors) but contains allocators where you need to explicitly free unused objects and buffers.
I've decided the best way was to try and implement a C++ wrapper on top of these APIs. it was challenging to find the appropriate lifecycle from the documentation but reading the C code helped a lot, eventually I've completed a C++ wrapper which could access most of ffmpeg API with an intuitive API.
The adventure didn't end there, I've used OpenCV Matrix (cv::Mat) as the base container for both image and audio frames which allowed me to explore the OpenCV API with both video and passthrough audio but with a possibility to eventually process the audio as well since its easily accessible.
I've started to explore the OpenCV API and I really liked the idea to manipulate the video frames with Node js, so I decided to implement a few of OpenCV APIs but then I thought how hard would it be to implement the entire OpenCV API in node js, so I went ahead and learned about Node Addons, V8 and NAN.
Many people think that Javascript is not suitable for processing Video/Audio but the truth of the matter is that Javascript is not really processing anything, its just a scripting engine. The real magic stays in C/C++ domain and the delay Node js/v8 adds is negligible when you see how much time compressing a frame or executing canny on it takes, those microseconds do add up, but in terms of percentage, it should be nearly invisible in the total program execution time, besides, this is more for fun than for science.
But... Javascript is not entirely suitable for scripting complex APIs and if you add the number of function overloads possible in OpenCV, it will probably be hell to use not to talk about development, this made me think in the Typescript direction, which can both help the intellisense and make development easier since it enforces some type checking at development time and that could eventually be used to make the API a lot more readable and intuitive.
A few things made developing the API not the most fun thing in the world, for example, the way v8 exposes its internal objects, everything looks like an object, all numbers are actually a double and there is no possibility to overload function implementation internally.
These reasons and more made me think and develop node-overload-resolution project, which addressed most of these problems by allowing me to add overloads to functions, add parameter validations, automatic conversion between v8 data types and C++ data types, automatically execute function implementations in the libuv threadpool as async functions and handling return values and exceptions without explicitly writing a single line of code in the addon itself.
So why am I writing all of this today?
Today the first test passed, not my tests, not all the tests, but the first OpenCV test which I ported to typescript to make sure the API is working properly.
Hopefully its not the last :-)
But let me be completely honest, it is by no way or shape ready for anything, the amount of APIs implemented are very little, even the build at the moment is complex and takes anywhere between 30 minutes to an hour. Since I've created the opencv_ts branch I didn't even check if the code compiles on linux, which it did before, on linux32/64 and even arm and NDK, sorry linux guys, in terms of IDE for C++, I have yet to see anything which comes close to Visual Studio ;-)
Showing posts with label v8. Show all posts
Showing posts with label v8. Show all posts
Friday, January 13, 2017
Tuesday, December 13, 2016
Node js C++ Addon Overload Resolution - Refactor & Add C++ Type Converter
Remember node-overload-resolution ?
I've been working on it for a while to see how I can support automatic async support.
I always believed if you can work a little harder to save a lot of time later, its most likely going to pay off, one of the biggest motivation to write the node-overload-resolution project was to easily convert C++ libraries to Node js without changing the API too much.
One of the major roadblocks in the last version of node-overload-resolution to this goal is that v8 objects are not accessible through any other thread other than node js main thread since node doesn't release the locker on v8 vm. To solve this issue I thought and implemented a way to parse and store the v8 objects as their projected C++ objects. in essence copying v8::String to std::string, v8::Number to double and so on.
Some issues I've encountered with this approach is how to store v8 Objects, C++ Class wrappers and Arrays, but if I can actually tell the resolution module which C++ types each v8 type translates to, maybe it could work.
I've been working on this premise and implemented a converter and value holder on top of the working overload resolution module and currently it supports Number, Function, String, Buffer, a typed struct and ObjectWrap.
Currently there are issues with Function, its implemented as or::Callback, but the actual function is not called yet.
Other issues is that I couldn't find a translation for Promise, Proxy and RegExp.
I think this module is on the correct path at the moment, it should be relatively easy to support async function calls by adding a check if the matched function have an additional function callback parameter, caching the parameter value as C++ type, pushing the function call to libuv threadpool and executing the return value conversion and callbacks activation when the thread finishes execution.
You can find the code on the native_types branch.
The code is concentrated in 3 main components, the value_holder, the value_converter and the generic_value_holder.
The value_holder is a derived template, storing the Value inside a template type.
The value_converter is a derived template with template specialization for each major type handled, the specialization is both for primitives (including v8 basic types) and for derived classes for IStructuredObject and ObjectWrap classes, allowing a more specific behavior for structures and C++ classes, such as parsing/creating new v8 objects as well as ObjectWrap::Wrap and ObjectWrap::Unwrap.
for example, this template specialization is for all derived classes of ObjectWrap, it wraps/unwraps to/from v8::Object:
lastly the generic_value_holder stores a pair of value_holder and value_converter and by that it can act as some sort of non-convertible variant that can return a v8 object from intended C++ types.
Update 2016-12-14:
The automatic async has been partially implemented, at this moment, the tests are working as far as I can see. what happens is that the overload resolution is checking the last argument passed to the function, if its not part of the function parameters and its type is a function, its assumed to be an async request. so the overload resolution engine stores all the function arguments in memory as C++ objects, calls the function and post process the return value and lastly calls the callback function supplied.
for example, assuming you have a function test(arg1 : string, arg2 : number), if a match is found that contains test("a",1), it will execute the function synchronously. But if a match is found as test("a",1,function(err,val){}), it will be executed asynchronously (note that there is no way to know which parameters are defined for function(err,val), so the example above is for clarity sake.
The functions implementation have to use info.at<T>(index) to read the parameters and have to use info.SetReturnValue<T>(value) to return a value, otherwise the implementation will fail because node js keeps the vm locked and there's no access to v8 objects through normal info[index] mechanism, if the implementer insists on using info[index], an access violation will occur and the application will crash when executing as async.
TODO:
- Finish the callback implementation.
- Implement Async call detection and execution - partial.
- This() access from inside a class member function
- cleanup Tags: async, node js, node-overload-resolution, templates, v8
I've been working on it for a while to see how I can support automatic async support.
I always believed if you can work a little harder to save a lot of time later, its most likely going to pay off, one of the biggest motivation to write the node-overload-resolution project was to easily convert C++ libraries to Node js without changing the API too much.
One of the major roadblocks in the last version of node-overload-resolution to this goal is that v8 objects are not accessible through any other thread other than node js main thread since node doesn't release the locker on v8 vm. To solve this issue I thought and implemented a way to parse and store the v8 objects as their projected C++ objects. in essence copying v8::String to std::string, v8::Number to double and so on.
Some issues I've encountered with this approach is how to store v8 Objects, C++ Class wrappers and Arrays, but if I can actually tell the resolution module which C++ types each v8 type translates to, maybe it could work.
I've been working on this premise and implemented a converter and value holder on top of the working overload resolution module and currently it supports Number, Function, String, Buffer, a typed struct and ObjectWrap.
Currently there are issues with Function, its implemented as or::Callback, but the actual function is not called yet.
Other issues is that I couldn't find a translation for Promise, Proxy and RegExp.
I think this module is on the correct path at the moment, it should be relatively easy to support async function calls by adding a check if the matched function have an additional function callback parameter, caching the parameter value as C++ type, pushing the function call to libuv threadpool and executing the return value conversion and callbacks activation when the thread finishes execution.
You can find the code on the native_types branch.
The code is concentrated in 3 main components, the value_holder, the value_converter and the generic_value_holder.
The value_holder is a derived template, storing the Value inside a template type.
template<typename T>
class value_holder : public value_holder_base {
public:
T Value;
value_holder() : value_holder_base() {}
value_holder(T val) : value_holder_base(), Value(val) {}
virtual ~value_holder() {}
};
class value_holder : public value_holder_base {
public:
T Value;
value_holder() : value_holder_base() {}
value_holder(T val) : value_holder_base(), Value(val) {}
virtual ~value_holder() {}
};
for example, this template specialization is for all derived classes of ObjectWrap, it wraps/unwraps to/from v8::Object:
template<typename T>
class value_converter<T*, typename std::enable_if<std::is_base_of<ObjectWrap, T>::value>::type> : publicvalue_converter_base {
public:
virtual T* convert(v8::Local<v8::Value> from) {
return or::ObjectWrap::Unwrap<T>(from.As<v8::Object>());
}
virtual v8::Local<v8::Value> convert(T* from) {
return from->Wrap();
}
virtual v8::Local<v8::Value> convert(std::shared_ptr<value_holder_base> from) {
auto from_value = std::dynamic_pointer_cast<value_holder<T*>>(from);
return from_value->Value->Wrap();
}
virtual std::shared_ptr<value_holder_base> read(v8::Local<v8::Value> val) {
auto parsed_value = std::make_shared<value_holder<T*>>();
parsed_value->Value = convert(val);
return parsed_value;
}
};
class value_converter<T*, typename std::enable_if<std::is_base_of<ObjectWrap, T>::value>::type> : publicvalue_converter_base {
public:
virtual T* convert(v8::Local<v8::Value> from) {
return or::ObjectWrap::Unwrap<T>(from.As<v8::Object>());
}
virtual v8::Local<v8::Value> convert(T* from) {
return from->Wrap();
}
virtual v8::Local<v8::Value> convert(std::shared_ptr<value_holder_base> from) {
auto from_value = std::dynamic_pointer_cast<value_holder<T*>>(from);
return from_value->Value->Wrap();
}
virtual std::shared_ptr<value_holder_base> read(v8::Local<v8::Value> val) {
auto parsed_value = std::make_shared<value_holder<T*>>();
parsed_value->Value = convert(val);
return parsed_value;
}
};
lastly the generic_value_holder stores a pair of value_holder and value_converter and by that it can act as some sort of non-convertible variant that can return a v8 object from intended C++ types.
class generic_value_holder {
private:
std::shared_ptr< or ::value_converter_base> _prefetcher;
std::shared_ptr< or ::value_holder_base> _value;
public:
void Set(std::shared_ptr< or ::value_converter_base> value_converter, std::shared_ptr< or ::value_holder_base> value) {
_prefetcher = value_converter;
_value = value;
}
template<typename T>
void Set(T returnValue) {
//store value_converter type
auto returnPrefetcher = std::make_shared < or ::value_converter<T>>();
//store value inside a valueholder
auto valueHolder = std::make_shared < or ::value_holder<T>>();
valueHolder->Value = returnValue;
Set(returnPrefetcher, valueHolder);
}
v8::Local<v8::Value> Get() {
return _prefetcher->convert(_value);
}
};
private:
std::shared_ptr< or ::value_converter_base> _prefetcher;
std::shared_ptr< or ::value_holder_base> _value;
public:
void Set(std::shared_ptr< or ::value_converter_base> value_converter, std::shared_ptr< or ::value_holder_base> value) {
_prefetcher = value_converter;
_value = value;
}
template<typename T>
void Set(T returnValue) {
//store value_converter type
auto returnPrefetcher = std::make_shared < or ::value_converter<T>>();
//store value inside a valueholder
auto valueHolder = std::make_shared < or ::value_holder<T>>();
valueHolder->Value = returnValue;
Set(returnPrefetcher, valueHolder);
}
v8::Local<v8::Value> Get() {
return _prefetcher->convert(_value);
}
};
Update 2016-12-14:
The automatic async has been partially implemented, at this moment, the tests are working as far as I can see. what happens is that the overload resolution is checking the last argument passed to the function, if its not part of the function parameters and its type is a function, its assumed to be an async request. so the overload resolution engine stores all the function arguments in memory as C++ objects, calls the function and post process the return value and lastly calls the callback function supplied.
for example, assuming you have a function test(arg1 : string, arg2 : number), if a match is found that contains test("a",1), it will execute the function synchronously. But if a match is found as test("a",1,function(err,val){}), it will be executed asynchronously (note that there is no way to know which parameters are defined for function(err,val), so the example above is for clarity sake.
The functions implementation have to use info.at<T>(index) to read the parameters and have to use info.SetReturnValue<T>(value) to return a value, otherwise the implementation will fail because node js keeps the vm locked and there's no access to v8 objects through normal info[index] mechanism, if the implementer insists on using info[index], an access violation will occur and the application will crash when executing as async.
TODO:
- Finish the callback implementation.
- Implement Async call detection and execution - partial.
- This() access from inside a class member function
- cleanup Tags: async, node js, node-overload-resolution, templates, v8
Saturday, September 24, 2016
Learning about V8 Objects
V8 Objects are documented thoroughly, however, reading documentation doesn't always help when you want to know how exactly it will manifest when calling a function with particular set of parameters in a particular state.
I've wrote this helper project to determine the exact values for each function call.
for example:
- calling a function without any parameters yields the following Nan::NAN_METHOD_ARGS_TYPE info
- calling a member function (prototype) yields:
- calling a "static" function yields:
There are many more parameters to explore, how does it show when an object is a child of another object? is there really an "int64" value in v8? spoiler: no. Can you do a ToInt32 on float numbers? What's the value of NumberValue for a particular string?
Have fun exploring :-)
Tags: node, v8
I've wrote this helper project to determine the exact values for each function call.
for example:
- calling a function without any parameters yields the following Nan::NAN_METHOD_ARGS_TYPE info
1: {
2: "Length": 0,
3: "Callee": {
4: "IsUndefined": false,
5: "IsNull": false,
6: "IsTrue": false,
7: "IsFalse": false,
8: "IsName": false,
9: "IsString": false,
10: "IsSymbol": false,
11: "IsFunction": true,
12: "GetName": "test",
13: "GetInferredName": "",
14: "GetDebugName": "test",
15: "GetScriptLineNumber": -1,
16: "GetScriptColumnNumber": -1,
17: "IsBuiltin": true,
18: "ScriptId": 0,
19: "GetScriptOrigin": {
20: "Options": {
21: "IsEmbedderDebugScript": false,
22: "IsSharedCrossOrigin": false,
23: "IsOpaque": false,
24: "Flags": 0
25: }
26: },
27: "IsArray": false,
28: "IsObject": true,
29: "PropertyNames": [],
30: "GetConstructorName": "Function",
31: "InternalFieldCount": 0,
32: "HasNamedLookupInterceptor": false,
33: "HasIndexedLookupInterceptor": false,
34: "GetIdentityHash": 535707258,
35: "IsCallable": true,
36: "GetPrototype": {
37: "IsUndefined": false,
38: "IsNull": false,
39: "IsTrue": false,
40: "IsFalse": false,
41: "IsName": false,
42: "IsString": false,
43: "IsSymbol": false,
44: "IsFunction": true,
45: "IsArray": false,
46: "IsObject": true,
47: "IsBoolean": false,
48: "IsNumber": false,
49: "IsExternal": false,
50: "IsInt32": false,
51: "IsUint32": false,
52: "IsDate": false,
53: "IsArgumentsObject": false,
54: "IsBooleanObject": false,
55: "IsNumberObject": false,
56: "IsStringObject": false,
57: "IsSymbolObject": false,
58: "IsNativeError": false,
59: "IsRegExp": false,
60: "IsGeneratorFunction": false,
61: "IsGeneratorObject": false,
62: "IsPromise": false,
63: "IsMap": false,
64: "IsSet": false,
65: "IsMapIterator": false,
66: "IsSetIterator": false,
67: "IsWeakMap": false,
68: "IsWeakSet": false,
69: "IsArrayBuffer": false,
70: "IsArrayBufferView": false,
71: "IsTypedArray": false,
72: "IsUint8Array": false,
73: "IsUint8ClampedArray": false,
74: "IsInt8Array": false,
75: "IsUint16Array": false,
76: "IsInt16Array": false,
77: "IsUint32Array": false,
78: "IsInt32Array": false,
79: "IsFloat32Array": false,
80: "IsFloat64Array": false,
81: "IsDataView": false,
82: "IsSharedArrayBuffer": false,
83: "IsProxy": false,
84: "ToBoolean": true,
85: "ToNumber": null,
86: "ToString": "function () {}",
87: "ToDetailString": "function () {}",
88: "ToInteger": 0,
89: "ToUint32": 0,
90: "ToInt32": 0,
91: "BooleanValue": true,
92: "NumberValue": null,
93: "IntegerValue": 0,
94: "Uint32Value": 0,
95: "Int32Value": 0,
96: "PropertyNames": [],
97: "GetConstructorName": "Function",
98: "InternalFieldCount": 0,
99: "HasNamedLookupInterceptor": false,
100: "HasIndexedLookupInterceptor": false,
101: "GetIdentityHash": 2033009011,
102: "IsCallable": true
103: },
104: "IsBoolean": false,
105: "IsNumber": false,
106: "IsExternal": false,
107: "IsInt32": false,
108: "IsUint32": false,
109: "IsDate": false,
110: "IsArgumentsObject": false,
111: "IsBooleanObject": false,
112: "IsNumberObject": false,
113: "IsStringObject": false,
114: "IsSymbolObject": false,
115: "IsNativeError": false,
116: "IsRegExp": false,
117: "IsGeneratorFunction": false,
118: "IsGeneratorObject": false,
119: "IsPromise": false,
120: "IsMap": false,
121: "IsSet": false,
122: "IsMapIterator": false,
123: "IsSetIterator": false,
124: "IsWeakMap": false,
125: "IsWeakSet": false,
126: "IsArrayBuffer": false,
127: "IsArrayBufferView": false,
128: "IsTypedArray": false,
129: "IsUint8Array": false,
130: "IsUint8ClampedArray": false,
131: "IsInt8Array": false,
132: "IsUint16Array": false,
133: "IsInt16Array": false,
134: "IsUint32Array": false,
135: "IsInt32Array": false,
136: "IsFloat32Array": false,
137: "IsFloat64Array": false,
138: "IsDataView": false,
139: "IsSharedArrayBuffer": false,
140: "IsProxy": false,
141: "ToBoolean": true,
142: "ToNumber": null,
143: "ToString": "function test() { [native code] }",
144: "ToDetailString": "function test() { [native code] }",
145: "ToInteger": 0,
146: "ToUint32": 0,
147: "ToInt32": 0,
148: "BooleanValue": true,
149: "NumberValue": null,
150: "IntegerValue": 0,
151: "Uint32Value": 0,
152: "Int32Value": 0
153: },
154: "IsConstructCall": false,
155: "This": {
156: "IsUndefined": false,
157: "IsNull": false,
158: "IsTrue": false,
159: "IsFalse": false,
160: "IsName": false,
161: "IsString": false,
162: "IsSymbol": false,
163: "IsFunction": false,
164: "IsArray": false,
165: "IsObject": true,
166: "PropertyNames": [],
167: "GetConstructorName": "Object",
168: "InternalFieldCount": 0,
169: "HasNamedLookupInterceptor": false,
170: "HasIndexedLookupInterceptor": false,
171: "GetIdentityHash": 716126579,
172: "IsCallable": false,
173: "GetPrototype": {
174: "IsUndefined": false,
175: "IsNull": false,
176: "IsTrue": false,
177: "IsFalse": false,
178: "IsName": false,
179: "IsString": false,
180: "IsSymbol": false,
181: "IsFunction": false,
182: "IsArray": false,
183: "IsObject": true,
184: "IsBoolean": false,
185: "IsNumber": false,
186: "IsExternal": false,
187: "IsInt32": false,
188: "IsUint32": false,
189: "IsDate": false,
190: "IsArgumentsObject": false,
191: "IsBooleanObject": false,
192: "IsNumberObject": false,
193: "IsStringObject": false,
194: "IsSymbolObject": false,
195: "IsNativeError": false,
196: "IsRegExp": false,
197: "IsGeneratorFunction": false,
198: "IsGeneratorObject": false,
199: "IsPromise": false,
200: "IsMap": false,
201: "IsSet": false,
202: "IsMapIterator": false,
203: "IsSetIterator": false,
204: "IsWeakMap": false,
205: "IsWeakSet": false,
206: "IsArrayBuffer": false,
207: "IsArrayBufferView": false,
208: "IsTypedArray": false,
209: "IsUint8Array": false,
210: "IsUint8ClampedArray": false,
211: "IsInt8Array": false,
212: "IsUint16Array": false,
213: "IsInt16Array": false,
214: "IsUint32Array": false,
215: "IsInt32Array": false,
216: "IsFloat32Array": false,
217: "IsFloat64Array": false,
218: "IsDataView": false,
219: "IsSharedArrayBuffer": false,
220: "IsProxy": false,
221: "ToBoolean": true,
222: "ToNumber": null,
223: "ToString": "[object Object]",
224: "ToDetailString": "#<Object>",
225: "ToObject": {},
226: "ToInteger": 0,
227: "ToUint32": 0,
228: "ToInt32": 0,
229: "BooleanValue": true,
230: "NumberValue": null,
231: "IntegerValue": 0,
232: "Uint32Value": 0,
233: "Int32Value": 0,
234: "PropertyNames": [],
235: "GetConstructorName": "Object",
236: "InternalFieldCount": 0,
237: "HasNamedLookupInterceptor": false,
238: "HasIndexedLookupInterceptor": false,
239: "GetIdentityHash": 1536014436,
240: "IsCallable": false
241: },
242: "IsBoolean": false,
243: "IsNumber": false,
244: "IsExternal": false,
245: "IsInt32": false,
246: "IsUint32": false,
247: "IsDate": false,
248: "IsArgumentsObject": false,
249: "IsBooleanObject": false,
250: "IsNumberObject": false,
251: "IsStringObject": false,
252: "IsSymbolObject": false,
253: "IsNativeError": false,
254: "IsRegExp": false,
255: "IsGeneratorFunction": false,
256: "IsGeneratorObject": false,
257: "IsPromise": false,
258: "IsMap": false,
259: "IsSet": false,
260: "IsMapIterator": false,
261: "IsSetIterator": false,
262: "IsWeakMap": false,
263: "IsWeakSet": false,
264: "IsArrayBuffer": false,
265: "IsArrayBufferView": false,
266: "IsTypedArray": false,
267: "IsUint8Array": false,
268: "IsUint8ClampedArray": false,
269: "IsInt8Array": false,
270: "IsUint16Array": false,
271: "IsInt16Array": false,
272: "IsUint32Array": false,
273: "IsInt32Array": false,
274: "IsFloat32Array": false,
275: "IsFloat64Array": false,
276: "IsDataView": false,
277: "IsSharedArrayBuffer": false,
278: "IsProxy": false,
279: "ToBoolean": true,
280: "ToNumber": null,
281: "ToString": "[object Object]",
282: "ToDetailString": "#<Object>",
283: "ToObject": {},
284: "ToInteger": 0,
285: "ToUint32": 0,
286: "ToInt32": 0,
287: "BooleanValue": true,
288: "NumberValue": null,
289: "IntegerValue": 0,
290: "Uint32Value": 0,
291: "Int32Value": 0
292: },
293: "Info": []
294: }
- calling a member function (prototype) yields:
1: {
2: "Length": 0,
3: "Callee": {
4: "IsUndefined": false,
5: "IsNull": false,
6: "IsTrue": false,
7: "IsFalse": false,
8: "IsName": false,
9: "IsString": false,
10: "IsSymbol": false,
11: "IsFunction": true,
12: "GetName": "test_member_function",
13: "GetInferredName": "",
14: "GetDebugName": "test_member_function",
15: "GetScriptLineNumber": -1,
16: "GetScriptColumnNumber": -1,
17: "IsBuiltin": true,
18: "ScriptId": 0,
19: "GetScriptOrigin": {
20: "Options": {
21: "IsEmbedderDebugScript": false,
22: "IsSharedCrossOrigin": false,
23: "IsOpaque": false,
24: "Flags": 0
25: }
26: },
27: "IsArray": false,
28: "IsObject": true,
29: "PropertyNames": [],
30: "GetConstructorName": "Function",
31: "InternalFieldCount": 0,
32: "HasNamedLookupInterceptor": false,
33: "HasIndexedLookupInterceptor": false,
34: "GetIdentityHash": 1864397598,
35: "IsCallable": true,
36: "GetPrototype": {
37: "IsUndefined": false,
38: "IsNull": false,
39: "IsTrue": false,
40: "IsFalse": false,
41: "IsName": false,
42: "IsString": false,
43: "IsSymbol": false,
44: "IsFunction": true,
45: "IsArray": false,
46: "IsObject": true,
47: "IsBoolean": false,
48: "IsNumber": false,
49: "IsExternal": false,
50: "IsInt32": false,
51: "IsUint32": false,
52: "IsDate": false,
53: "IsArgumentsObject": false,
54: "IsBooleanObject": false,
55: "IsNumberObject": false,
56: "IsStringObject": false,
57: "IsSymbolObject": false,
58: "IsNativeError": false,
59: "IsRegExp": false,
60: "IsGeneratorFunction": false,
61: "IsGeneratorObject": false,
62: "IsPromise": false,
63: "IsMap": false,
64: "IsSet": false,
65: "IsMapIterator": false,
66: "IsSetIterator": false,
67: "IsWeakMap": false,
68: "IsWeakSet": false,
69: "IsArrayBuffer": false,
70: "IsArrayBufferView": false,
71: "IsTypedArray": false,
72: "IsUint8Array": false,
73: "IsUint8ClampedArray": false,
74: "IsInt8Array": false,
75: "IsUint16Array": false,
76: "IsInt16Array": false,
77: "IsUint32Array": false,
78: "IsInt32Array": false,
79: "IsFloat32Array": false,
80: "IsFloat64Array": false,
81: "IsDataView": false,
82: "IsSharedArrayBuffer": false,
83: "IsProxy": false,
84: "ToBoolean": true,
85: "ToNumber": null,
86: "ToString": "function () {}",
87: "ToDetailString": "function () {}",
88: "ToInteger": 0,
89: "ToUint32": 0,
90: "ToInt32": 0,
91: "BooleanValue": true,
92: "NumberValue": null,
93: "IntegerValue": 0,
94: "Uint32Value": 0,
95: "Int32Value": 0,
96: "PropertyNames": [],
97: "GetConstructorName": "Function",
98: "InternalFieldCount": 0,
99: "HasNamedLookupInterceptor": false,
100: "HasIndexedLookupInterceptor": false,
101: "GetIdentityHash": 1117981863,
102: "IsCallable": true
103: },
104: "IsBoolean": false,
105: "IsNumber": false,
106: "IsExternal": false,
107: "IsInt32": false,
108: "IsUint32": false,
109: "IsDate": false,
110: "IsArgumentsObject": false,
111: "IsBooleanObject": false,
112: "IsNumberObject": false,
113: "IsStringObject": false,
114: "IsSymbolObject": false,
115: "IsNativeError": false,
116: "IsRegExp": false,
117: "IsGeneratorFunction": false,
118: "IsGeneratorObject": false,
119: "IsPromise": false,
120: "IsMap": false,
121: "IsSet": false,
122: "IsMapIterator": false,
123: "IsSetIterator": false,
124: "IsWeakMap": false,
125: "IsWeakSet": false,
126: "IsArrayBuffer": false,
127: "IsArrayBufferView": false,
128: "IsTypedArray": false,
129: "IsUint8Array": false,
130: "IsUint8ClampedArray": false,
131: "IsInt8Array": false,
132: "IsUint16Array": false,
133: "IsInt16Array": false,
134: "IsUint32Array": false,
135: "IsInt32Array": false,
136: "IsFloat32Array": false,
137: "IsFloat64Array": false,
138: "IsDataView": false,
139: "IsSharedArrayBuffer": false,
140: "IsProxy": false,
141: "ToBoolean": true,
142: "ToNumber": null,
143: "ToString": "function test_member_function() { [native code] }",
144: "ToDetailString": "function test_member_function() { [native code] }",
145: "ToInteger": 0,
146: "ToUint32": 0,
147: "ToInt32": 0,
148: "BooleanValue": true,
149: "NumberValue": null,
150: "IntegerValue": 0,
151: "Uint32Value": 0,
152: "Int32Value": 0
153: },
154: "IsConstructCall": false,
155: "This": {
156: "IsUndefined": false,
157: "IsNull": false,
158: "IsTrue": false,
159: "IsFalse": false,
160: "IsName": false,
161: "IsString": false,
162: "IsSymbol": false,
163: "IsFunction": false,
164: "IsArray": false,
165: "IsObject": true,
166: "PropertyNames": [],
167: "GetConstructorName": "base_class",
168: "InternalFieldCount": 1,
169: "HasNamedLookupInterceptor": false,
170: "HasIndexedLookupInterceptor": false,
171: "GetIdentityHash": 1554520219,
172: "IsCallable": false,
173: "GetPrototype": {
174: "IsUndefined": false,
175: "IsNull": false,
176: "IsTrue": false,
177: "IsFalse": false,
178: "IsName": false,
179: "IsString": false,
180: "IsSymbol": false,
181: "IsFunction": false,
182: "IsArray": false,
183: "IsObject": true,
184: "IsBoolean": false,
185: "IsNumber": false,
186: "IsExternal": false,
187: "IsInt32": false,
188: "IsUint32": false,
189: "IsDate": false,
190: "IsArgumentsObject": false,
191: "IsBooleanObject": false,
192: "IsNumberObject": false,
193: "IsStringObject": false,
194: "IsSymbolObject": false,
195: "IsNativeError": false,
196: "IsRegExp": false,
197: "IsGeneratorFunction": false,
198: "IsGeneratorObject": false,
199: "IsPromise": false,
200: "IsMap": false,
201: "IsSet": false,
202: "IsMapIterator": false,
203: "IsSetIterator": false,
204: "IsWeakMap": false,
205: "IsWeakSet": false,
206: "IsArrayBuffer": false,
207: "IsArrayBufferView": false,
208: "IsTypedArray": false,
209: "IsUint8Array": false,
210: "IsUint8ClampedArray": false,
211: "IsInt8Array": false,
212: "IsUint16Array": false,
213: "IsInt16Array": false,
214: "IsUint32Array": false,
215: "IsInt32Array": false,
216: "IsFloat32Array": false,
217: "IsFloat64Array": false,
218: "IsDataView": false,
219: "IsSharedArrayBuffer": false,
220: "IsProxy": false,
221: "ToBoolean": true,
222: "ToNumber": null,
223: "ToString": "[object Object]",
224: "ToDetailString": "#<base_class>",
225: "ToObject": {},
226: "ToInteger": 0,
227: "ToUint32": 0,
228: "ToInt32": 0,
229: "BooleanValue": true,
230: "NumberValue": null,
231: "IntegerValue": 0,
232: "Uint32Value": 0,
233: "Int32Value": 0,
234: "PropertyNames": [],
235: "GetConstructorName": "Object",
236: "InternalFieldCount": 0,
237: "HasNamedLookupInterceptor": false,
238: "HasIndexedLookupInterceptor": false,
239: "GetIdentityHash": 1008670604,
240: "IsCallable": false
241: },
242: "IsBoolean": false,
243: "IsNumber": false,
244: "IsExternal": false,
245: "IsInt32": false,
246: "IsUint32": false,
247: "IsDate": false,
248: "IsArgumentsObject": false,
249: "IsBooleanObject": false,
250: "IsNumberObject": false,
251: "IsStringObject": false,
252: "IsSymbolObject": false,
253: "IsNativeError": false,
254: "IsRegExp": false,
255: "IsGeneratorFunction": false,
256: "IsGeneratorObject": false,
257: "IsPromise": false,
258: "IsMap": false,
259: "IsSet": false,
260: "IsMapIterator": false,
261: "IsSetIterator": false,
262: "IsWeakMap": false,
263: "IsWeakSet": false,
264: "IsArrayBuffer": false,
265: "IsArrayBufferView": false,
266: "IsTypedArray": false,
267: "IsUint8Array": false,
268: "IsUint8ClampedArray": false,
269: "IsInt8Array": false,
270: "IsUint16Array": false,
271: "IsInt16Array": false,
272: "IsUint32Array": false,
273: "IsInt32Array": false,
274: "IsFloat32Array": false,
275: "IsFloat64Array": false,
276: "IsDataView": false,
277: "IsSharedArrayBuffer": false,
278: "IsProxy": false,
279: "ToBoolean": true,
280: "ToNumber": null,
281: "ToString": "[object base_class]",
282: "ToDetailString": "#<base_class>",
283: "ToObject": {},
284: "ToInteger": 0,
285: "ToUint32": 0,
286: "ToInt32": 0,
287: "BooleanValue": true,
288: "NumberValue": null,
289: "IntegerValue": 0,
290: "Uint32Value": 0,
291: "Int32Value": 0
292: },
293: "Info": []
294: }
- calling a "static" function yields:
1: {
2: "Length": 0,
3: "Callee": {
4: "IsUndefined": false,
5: "IsNull": false,
6: "IsTrue": false,
7: "IsFalse": false,
8: "IsName": false,
9: "IsString": false,
10: "IsSymbol": false,
11: "IsFunction": true,
12: "GetName": "test_static_function",
13: "GetInferredName": "",
14: "GetDebugName": "test_static_function",
15: "GetScriptLineNumber": -1,
16: "GetScriptColumnNumber": -1,
17: "IsBuiltin": true,
18: "ScriptId": 0,
19: "GetScriptOrigin": {
20: "Options": {
21: "IsEmbedderDebugScript": false,
22: "IsSharedCrossOrigin": false,
23: "IsOpaque": false,
24: "Flags": 0
25: }
26: },
27: "IsArray": false,
28: "IsObject": true,
29: "PropertyNames": [],
30: "GetConstructorName": "Function",
31: "InternalFieldCount": 0,
32: "HasNamedLookupInterceptor": false,
33: "HasIndexedLookupInterceptor": false,
34: "GetIdentityHash": 994160730,
35: "IsCallable": true,
36: "GetPrototype": {
37: "IsUndefined": false,
38: "IsNull": false,
39: "IsTrue": false,
40: "IsFalse": false,
41: "IsName": false,
42: "IsString": false,
43: "IsSymbol": false,
44: "IsFunction": true,
45: "IsArray": false,
46: "IsObject": true,
47: "IsBoolean": false,
48: "IsNumber": false,
49: "IsExternal": false,
50: "IsInt32": false,
51: "IsUint32": false,
52: "IsDate": false,
53: "IsArgumentsObject": false,
54: "IsBooleanObject": false,
55: "IsNumberObject": false,
56: "IsStringObject": false,
57: "IsSymbolObject": false,
58: "IsNativeError": false,
59: "IsRegExp": false,
60: "IsGeneratorFunction": false,
61: "IsGeneratorObject": false,
62: "IsPromise": false,
63: "IsMap": false,
64: "IsSet": false,
65: "IsMapIterator": false,
66: "IsSetIterator": false,
67: "IsWeakMap": false,
68: "IsWeakSet": false,
69: "IsArrayBuffer": false,
70: "IsArrayBufferView": false,
71: "IsTypedArray": false,
72: "IsUint8Array": false,
73: "IsUint8ClampedArray": false,
74: "IsInt8Array": false,
75: "IsUint16Array": false,
76: "IsInt16Array": false,
77: "IsUint32Array": false,
78: "IsInt32Array": false,
79: "IsFloat32Array": false,
80: "IsFloat64Array": false,
81: "IsDataView": false,
82: "IsSharedArrayBuffer": false,
83: "IsProxy": false,
84: "ToBoolean": true,
85: "ToNumber": null,
86: "ToString": "function () {}",
87: "ToDetailString": "function () {}",
88: "ToInteger": 0,
89: "ToUint32": 0,
90: "ToInt32": 0,
91: "BooleanValue": true,
92: "NumberValue": null,
93: "IntegerValue": 0,
94: "Uint32Value": 0,
95: "Int32Value": 0,
96: "PropertyNames": [],
97: "GetConstructorName": "Function",
98: "InternalFieldCount": 0,
99: "HasNamedLookupInterceptor": false,
100: "HasIndexedLookupInterceptor": false,
101: "GetIdentityHash": 1329899702,
102: "IsCallable": true
103: },
104: "IsBoolean": false,
105: "IsNumber": false,
106: "IsExternal": false,
107: "IsInt32": false,
108: "IsUint32": false,
109: "IsDate": false,
110: "IsArgumentsObject": false,
111: "IsBooleanObject": false,
112: "IsNumberObject": false,
113: "IsStringObject": false,
114: "IsSymbolObject": false,
115: "IsNativeError": false,
116: "IsRegExp": false,
117: "IsGeneratorFunction": false,
118: "IsGeneratorObject": false,
119: "IsPromise": false,
120: "IsMap": false,
121: "IsSet": false,
122: "IsMapIterator": false,
123: "IsSetIterator": false,
124: "IsWeakMap": false,
125: "IsWeakSet": false,
126: "IsArrayBuffer": false,
127: "IsArrayBufferView": false,
128: "IsTypedArray": false,
129: "IsUint8Array": false,
130: "IsUint8ClampedArray": false,
131: "IsInt8Array": false,
132: "IsUint16Array": false,
133: "IsInt16Array": false,
134: "IsUint32Array": false,
135: "IsInt32Array": false,
136: "IsFloat32Array": false,
137: "IsFloat64Array": false,
138: "IsDataView": false,
139: "IsSharedArrayBuffer": false,
140: "IsProxy": false,
141: "ToBoolean": true,
142: "ToNumber": null,
143: "ToString": "function test_static_function() { [native code] }",
144: "ToDetailString": "function test_static_function() { [native code] }",
145: "ToInteger": 0,
146: "ToUint32": 0,
147: "ToInt32": 0,
148: "BooleanValue": true,
149: "NumberValue": null,
150: "IntegerValue": 0,
151: "Uint32Value": 0,
152: "Int32Value": 0
153: },
154: "IsConstructCall": false,
155: "This": {
156: "IsUndefined": false,
157: "IsNull": false,
158: "IsTrue": false,
159: "IsFalse": false,
160: "IsName": false,
161: "IsString": false,
162: "IsSymbol": false,
163: "IsFunction": true,
164: "GetName": "base_class",
165: "GetInferredName": "",
166: "GetDebugName": "base_class",
167: "GetScriptLineNumber": -1,
168: "GetScriptColumnNumber": -1,
169: "IsBuiltin": true,
170: "ScriptId": 0,
171: "GetScriptOrigin": {
172: "Options": {
173: "IsEmbedderDebugScript": false,
174: "IsSharedCrossOrigin": false,
175: "IsOpaque": false,
176: "Flags": 0
177: }
178: },
179: "IsArray": false,
180: "IsObject": true,
181: "PropertyNames": [],
182: "GetConstructorName": "Function",
183: "InternalFieldCount": 0,
184: "HasNamedLookupInterceptor": false,
185: "HasIndexedLookupInterceptor": false,
186: "GetIdentityHash": 612374797,
187: "IsCallable": true,
188: "GetPrototype": {
189: "IsUndefined": false,
190: "IsNull": false,
191: "IsTrue": false,
192: "IsFalse": false,
193: "IsName": false,
194: "IsString": false,
195: "IsSymbol": false,
196: "IsFunction": true,
197: "IsArray": false,
198: "IsObject": true,
199: "IsBoolean": false,
200: "IsNumber": false,
201: "IsExternal": false,
202: "IsInt32": false,
203: "IsUint32": false,
204: "IsDate": false,
205: "IsArgumentsObject": false,
206: "IsBooleanObject": false,
207: "IsNumberObject": false,
208: "IsStringObject": false,
209: "IsSymbolObject": false,
210: "IsNativeError": false,
211: "IsRegExp": false,
212: "IsGeneratorFunction": false,
213: "IsGeneratorObject": false,
214: "IsPromise": false,
215: "IsMap": false,
216: "IsSet": false,
217: "IsMapIterator": false,
218: "IsSetIterator": false,
219: "IsWeakMap": false,
220: "IsWeakSet": false,
221: "IsArrayBuffer": false,
222: "IsArrayBufferView": false,
223: "IsTypedArray": false,
224: "IsUint8Array": false,
225: "IsUint8ClampedArray": false,
226: "IsInt8Array": false,
227: "IsUint16Array": false,
228: "IsInt16Array": false,
229: "IsUint32Array": false,
230: "IsInt32Array": false,
231: "IsFloat32Array": false,
232: "IsFloat64Array": false,
233: "IsDataView": false,
234: "IsSharedArrayBuffer": false,
235: "IsProxy": false,
236: "ToBoolean": true,
237: "ToNumber": null,
238: "ToString": "function () {}",
239: "ToDetailString": "function () {}",
240: "ToInteger": 0,
241: "ToUint32": 0,
242: "ToInt32": 0,
243: "BooleanValue": true,
244: "NumberValue": null,
245: "IntegerValue": 0,
246: "Uint32Value": 0,
247: "Int32Value": 0,
248: "PropertyNames": [],
249: "GetConstructorName": "Function",
250: "InternalFieldCount": 0,
251: "HasNamedLookupInterceptor": false,
252: "HasIndexedLookupInterceptor": false,
253: "GetIdentityHash": 1329899702,
254: "IsCallable": true
255: },
256: "IsBoolean": false,
257: "IsNumber": false,
258: "IsExternal": false,
259: "IsInt32": false,
260: "IsUint32": false,
261: "IsDate": false,
262: "IsArgumentsObject": false,
263: "IsBooleanObject": false,
264: "IsNumberObject": false,
265: "IsStringObject": false,
266: "IsSymbolObject": false,
267: "IsNativeError": false,
268: "IsRegExp": false,
269: "IsGeneratorFunction": false,
270: "IsGeneratorObject": false,
271: "IsPromise": false,
272: "IsMap": false,
273: "IsSet": false,
274: "IsMapIterator": false,
275: "IsSetIterator": false,
276: "IsWeakMap": false,
277: "IsWeakSet": false,
278: "IsArrayBuffer": false,
279: "IsArrayBufferView": false,
280: "IsTypedArray": false,
281: "IsUint8Array": false,
282: "IsUint8ClampedArray": false,
283: "IsInt8Array": false,
284: "IsUint16Array": false,
285: "IsInt16Array": false,
286: "IsUint32Array": false,
287: "IsInt32Array": false,
288: "IsFloat32Array": false,
289: "IsFloat64Array": false,
290: "IsDataView": false,
291: "IsSharedArrayBuffer": false,
292: "IsProxy": false,
293: "ToBoolean": true,
294: "ToNumber": null,
295: "ToString": "function base_class() { [native code] }",
296: "ToDetailString": "function base_class() { [native code] }",
297: "ToInteger": 0,
298: "ToUint32": 0,
299: "ToInt32": 0,
300: "BooleanValue": true,
301: "NumberValue": null,
302: "IntegerValue": 0,
303: "Uint32Value": 0,
304: "Int32Value": 0
305: },
306: "Info": []
307: }
There are many more parameters to explore, how does it show when an object is a child of another object? is there really an "int64" value in v8? spoiler: no. Can you do a ToInt32 on float numbers? What's the value of NumberValue for a particular string?
Have fun exploring :-)
Tags: node, v8
Subscribe to:
Comments (Atom)