Skip to content

Commit b533226

Browse files
mkrufkykkoopa
authored andcommitted
nan: add support for JSON::Parse & Stringify (nodejs#651)
* nan: add support for JSON::Parse & Stringify Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: entry points need a Nan::HandleScope Add a Nan::HandleScope to each static entry point. The private singleton class constructor does not need a Nan::HandleScope, because it can only be called from one of the static entry points. Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: ditch the singleton and remove all persistence Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: we *always* need a HandleScope Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: use Nan::To<v8::String> rather than ->ToString() Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: use Nan::Get rather than v8::Object::Get Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: use Nan::To<v8::Object> rather than ->ToObject() Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: proper HandleScope placement for Parse() Signed-off-by: Michael Ira Krufky <[email protected]> * doc/json.md: Nan::To<v8::Object> returns a MaybeLocal<v8::Object> Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: dont call ToLocalChecked on an empty MaybeLocal Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: replace v8::MaybeLocal with Nan::MaybeLocal Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: check MaybeLocal returned by Nan::To<v8::Object> Signed-off-by: Michael Ira Krufky <[email protected]> * remove rvagg from notifications * nan_json.h: replace v8::MaybeLocal with Nan::MaybeLocal Signed-off-by: Michael Ira Krufky <[email protected]> * convert Nan::JSON to an RAII class Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: all entry points need a Nan::EscapableHandleScope Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: v8::JSON::Parse() that takes no context returns v8::Local<v8::Value> Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: check !Nan::Callback.IsEmpty() before calling it Signed-off-by: Michael Ira Krufky <[email protected]> * Revert "remove rvagg from notifications" This reverts commit 00a98ec. * nan_json.h: rename for consistency: jsonObject->json_object, jsonString->json_string Signed-off-by: Michael Ira Krufky <[email protected]> * test/cpp/json-parse.cpp: fix memory leak Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: rename m_cb_parse->parse_cb_ & m_cb_stringify->stringify_cb_ Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: remove unnecessary destructor Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: identify the #endif's Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: rename parseMethod->parse_method & stringifyMethod->stringify_method Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: use Local<Value>.As<Function> rather than Local<Function>::Cast Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: convert camelCase locals to snake_case Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: remove parens from precompiler directives Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: check NODE_MODULE_VERSION rather than NODE_MAJOR_VERSION & NODE_MINOR_VERSION Signed-off-by: Michael Ira Krufky <[email protected]> * doc/json.md: check MaybeLocal before extraction Signed-off-by: Michael Ira Krufky <[email protected]> * doc/json.md: clean up 'Use JSON.Api(arg) to ...' short description Signed-off-by: Michael Ira Krufky <[email protected]> * doc/json.md: Nan::JSON.[Api] instead of Nan::JSON::[Api] Signed-off-by: Michael Ira Krufky <[email protected]> * test/cpp/json-parse.cpp: check MaybeLocal before proceeding Signed-off-by: Michael Ira Krufky <[email protected]> * test/cpp/json-stringify.cpp: improve argument checks Signed-off-by: Michael Ira Krufky <[email protected]> * test/cpp/strings.cpp: check MaybeLocal before proceeding Signed-off-by: Michael Ira Krufky <[email protected]> * test/cpp/json-*.cpp: return Nan::Undefined() in the MaybeLocal.IsEmpty() case Signed-off-by: Michael Ira Krufky <[email protected]> * test/js/json-parse-test.js: add more test cases Signed-off-by: Michael Ira Krufky <[email protected]> * test/js/json-stringify-test.js: add more test cases Signed-off-by: Michael Ira Krufky <[email protected]> * test/js/json-parse-test.js: add spaces near square brackets in arrays Signed-off-by: Michael Ira Krufky <[email protected]> * test/cpp/json-stringify.cpp: check gap argument before extraction Signed-off-by: Michael Ira Krufky <[email protected]> * test/cpp/json-stringify.cpp: replace v8::MaybeLocal with Nan::MaybeLocal Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: JSON constructor needs a Nan::HandleScope Signed-off-by: Michael Ira Krufky <[email protected]> * test/cpp/json-*.cpp: prefer the fast primitive setters where available replace ReturnValue::Set(Nan::Undefined()) with ReturnValue::SetUndefined() Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: add assertions to constructor sanity checks Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: convert remaining sanity checks to assertions Signed-off-by: Michael Ira Krufky <[email protected]> * test/cpp/json-*.cpp: no need to set return value to Undefined as it is the default Signed-off-by: Michael Ira Krufky <[email protected]> * nan_json.h: remove superfluous newlines after assert Signed-off-by: Michael Ira Krufky <[email protected]>
1 parent 5b7083b commit b533226

File tree

12 files changed

+479
-3
lines changed

12 files changed

+479
-3
lines changed

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ LINT_SOURCES = \
2323
nan_converters_pre_43_inl.h \
2424
nan_implementation_12_inl.h \
2525
nan_implementation_pre_12_inl.h \
26+
nan_json.h \
2627
nan_maybe_43_inl.h \
2728
nan_maybe_pre_43_inl.h \
2829
nan_new.h \
@@ -46,6 +47,8 @@ LINT_SOURCES = \
4647
test/cpp/indexedinterceptors.cpp \
4748
test/cpp/converters.cpp \
4849
test/cpp/isolatedata.cpp \
50+
test/cpp/json-parse.cpp \
51+
test/cpp/json-stringify.cpp \
4952
test/cpp/makecallback.cpp \
5053
test/cpp/morenews.cpp \
5154
test/cpp/multifile1.cpp \
@@ -90,14 +93,14 @@ forcetest:
9093
docs: README.md doc/.build.sh doc/asyncworker.md doc/buffers.md doc/callback.md \
9194
doc/converters.md doc/errors.md doc/maybe_types.md doc/methods.md doc/new.md \
9295
doc/node_misc.md doc/persistent.md doc/scopes.md doc/script.md doc/string_bytes.md \
93-
doc/v8_internals.md doc/v8_misc.md
96+
doc/v8_internals.md doc/json.md doc/v8_misc.md
9497
doc/.build.sh
9598

9699

97100
$(ADDONS): nan.h nan_new.h nan_implementation_pre_12_inl.h nan_implementation_12_inl.h \
98101
nan_callbacks.h nan_callbacks_12_inl.h nan_callbacks_pre_12_inl.h \
99102
nan_converters.h nan_converters_43_inl.h nan_converters_pre_43_inl.h \
100-
nan_maybe_43_inl.h nan_maybe_pre_43_inl.h \
103+
nan_json.h nan_maybe_43_inl.h nan_maybe_pre_43_inl.h \
101104
nan_persistent_12_inl.h nan_persistent_pre_12_inl.h nan_private.h \
102105
nan_weak.h nan_string_bytes.h test/binding.gyp $(SOURCES)
103106
cd test/ && ../node_modules/.bin/node-gyp rebuild

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,15 @@ NAN provides a `v8::Script` helpers as the API has changed over the supported ve
210210
- <a href="doc/script.md#api_nan_run_script"><b><code>Nan::RunScript()</code></b></a>
211211

212212

213+
### JSON
214+
215+
The _JSON_ object provides the c++ versions of the methods offered by the `JSON` object in javascript. V8 exposes these methods via the `v8::JSON` object.
216+
217+
- <a href="doc/json.md#api_nan_json_parse"><b><code>Nan::JSON.Parse</code></b></a>
218+
- <a href="doc/json.md#api_nan_json_stringify"><b><code>Nan::JSON.Stringify</code></b></a>
219+
220+
Refer to the V8 JSON object in the [V8 documentation](https://v8docs.nodesource.com/node-7.4/da/d6f/classv8_1_1_j_s_o_n.html) for more information about these methods and their arguments.
221+
213222
### Errors
214223

215224
NAN includes helpers for creating, throwing and catching Errors as much of this functionality varies across the supported versions of V8 and must be abstracted.

doc/.build.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ files=" \
88
converters.md \
99
maybe_types.md \
1010
script.md \
11+
json.md \
1112
errors.md \
1213
buffers.md \
1314
callback.md \

doc/json.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
## JSON
2+
3+
The _JSON_ object provides the c++ versions of the methods offered by the `JSON` object in javascript. V8 exposes these methods via the `v8::JSON` object.
4+
5+
- <a href="#api_nan_json_parse"><b><code>Nan::JSON.Parse</code></b></a>
6+
- <a href="#api_nan_json_stringify"><b><code>Nan::JSON.Stringify</code></b></a>
7+
8+
Refer to the V8 JSON object in the [V8 documentation](https://v8docs.nodesource.com/node-7.4/da/d6f/classv8_1_1_j_s_o_n.html) for more information about these methods and their arguments.
9+
10+
<a name="api_nan_json_parse"></a>
11+
12+
### Nan::JSON.Parse
13+
14+
A simple wrapper around [`v8::JSON::Parse`](https://v8docs.nodesource.com/node-7.4/da/d6f/classv8_1_1_j_s_o_n.html#a936310d2540fb630ed37d3ee3ffe4504).
15+
16+
Definition:
17+
18+
```c++
19+
Nan::MaybeLocal<v8::Value> Nan::JSON::Parse(v8::Local<v8::String> json_string);
20+
```
21+
22+
Use `JSON.Parse(json_string)` to parse a string into a `v8::Value`.
23+
24+
Example:
25+
26+
```c++
27+
v8::Local<v8::String> json_string = Nan::New("{ \"JSON\": \"object\" }").ToLocalChecked();
28+
29+
Nan::JSON NanJSON;
30+
Nan::MaybeLocal<v8::Value> result = NanJSON.Parse(json_string);
31+
if (!result.IsEmpty()) {
32+
v8::Local<v8::Value> val = result.ToLocalChecked();
33+
}
34+
```
35+
36+
<a name="api_nan_json_stringify"></a>
37+
38+
### Nan::JSON.Stringify
39+
40+
A simple wrapper around [`v8::JSON::Stringify`](https://v8docs.nodesource.com/node-7.4/da/d6f/classv8_1_1_j_s_o_n.html#a44b255c3531489ce43f6110209138860).
41+
42+
Definition:
43+
44+
```c++
45+
Nan::MaybeLocal<v8::String> Nan::JSON::Stringify(v8::Local<v8::Object> json_object, v8::Local<v8::String> gap = v8::Local<v8::String>());
46+
```
47+
48+
Use `JSON.Stringify(value)` to stringify a `v8::Object`.
49+
50+
Example:
51+
52+
```c++
53+
// using `v8::Local<v8::Value> val` from the `JSON::Parse` example
54+
v8::Local<v8::Object> obj = Nan::To<v8::Object>(val).ToLocalChecked();
55+
56+
Nan::JSON NanJSON;
57+
Nan::MaybeLocal<v8::String> result = NanJSON.Stringify(obj);
58+
if (!result.IsEmpty()) {
59+
v8::Local<v8::String> stringified = result.ToLocalChecked();
60+
}
61+
```
62+

nan.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2310,6 +2310,10 @@ MakeMaybe(MaybeMaybe<T> v) {
23102310

23112311
#include "nan_typedarray_contents.h" // NOLINT(build/include)
23122312

2313+
//=== JSON =====================================================================
2314+
2315+
#include "nan_json.h" // NOLINT(build/include)
2316+
23132317
} // end of namespace Nan
23142318

23152319
#endif // NAN_H_

nan_json.h

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*********************************************************************
2+
* NAN - Native Abstractions for Node.js
3+
*
4+
* Copyright (c) 2017 NAN contributors
5+
*
6+
* MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
7+
********************************************************************/
8+
9+
#ifndef NAN_JSON_H_
10+
#define NAN_JSON_H_
11+
12+
#if NODE_MODULE_VERSION < NODE_0_12_MODULE_VERSION
13+
#define NAN_JSON_H_NEED_PARSE 1
14+
#else
15+
#define NAN_JSON_H_NEED_PARSE 0
16+
#endif // NODE_MODULE_VERSION < NODE_0_12_MODULE_VERSION
17+
18+
#if NODE_MODULE_VERSION > NODE_6_0_MODULE_VERSION
19+
#define NAN_JSON_H_NEED_STRINGIFY 0
20+
#else
21+
#define NAN_JSON_H_NEED_STRINGIFY 1
22+
#endif // NODE_MODULE_VERSION > NODE_6_0_MODULE_VERSION
23+
24+
class JSON {
25+
public:
26+
JSON() {
27+
#if NAN_JSON_H_NEED_PARSE + NAN_JSON_H_NEED_STRINGIFY
28+
Nan::HandleScope scope;
29+
30+
Nan::MaybeLocal<v8::Value> maybe_global_json = Nan::Get(
31+
Nan::GetCurrentContext()->Global(),
32+
Nan::New("JSON").ToLocalChecked()
33+
);
34+
35+
assert(!maybe_global_json.IsEmpty() && "global JSON is empty");
36+
v8::Local<v8::Value> val_global_json = maybe_global_json.ToLocalChecked();
37+
38+
assert(val_global_json->IsObject() && "global JSON is not an object");
39+
Nan::MaybeLocal<v8::Object> maybe_obj_global_json =
40+
Nan::To<v8::Object>(val_global_json);
41+
42+
assert(!maybe_obj_global_json.IsEmpty() && "global JSON object is empty");
43+
v8::Local<v8::Object> global_json = maybe_obj_global_json.ToLocalChecked();
44+
45+
#if NAN_JSON_H_NEED_PARSE
46+
Nan::MaybeLocal<v8::Value> maybe_parse_method = Nan::Get(
47+
global_json, Nan::New("parse").ToLocalChecked()
48+
);
49+
50+
assert(!maybe_parse_method.IsEmpty() && "JSON.parse is empty");
51+
v8::Local<v8::Value> parse_method = maybe_parse_method.ToLocalChecked();
52+
53+
assert(parse_method->IsFunction() && "JSON.parse is not a function");
54+
parse_cb_.Reset(parse_method.As<v8::Function>());
55+
#endif // NAN_JSON_H_NEED_PARSE
56+
57+
#if NAN_JSON_H_NEED_STRINGIFY
58+
Nan::MaybeLocal<v8::Value> maybe_stringify_method = Nan::Get(
59+
global_json, Nan::New("stringify").ToLocalChecked()
60+
);
61+
62+
assert(!maybe_stringify_method.IsEmpty() && "JSON.stringify is empty");
63+
v8::Local<v8::Value> stringify_method =
64+
maybe_stringify_method.ToLocalChecked();
65+
66+
assert(
67+
stringify_method->IsFunction() && "JSON.stringify is not a function"
68+
);
69+
stringify_cb_.Reset(stringify_method.As<v8::Function>());
70+
#endif // NAN_JSON_H_NEED_STRINGIFY
71+
#endif // NAN_JSON_H_NEED_PARSE + NAN_JSON_H_NEED_STRINGIFY
72+
}
73+
74+
inline
75+
Nan::MaybeLocal<v8::Value> Parse(v8::Local<v8::String> json_string) {
76+
Nan::EscapableHandleScope scope;
77+
#if NAN_JSON_H_NEED_PARSE
78+
return scope.Escape(parse(json_string));
79+
#else
80+
#if NODE_MODULE_VERSION > NODE_6_0_MODULE_VERSION
81+
Nan::MaybeLocal<v8::Value> result =
82+
v8::JSON::Parse(Nan::GetCurrentContext(), json_string);
83+
84+
if (result.IsEmpty()) return v8::Local<v8::Value>();
85+
return scope.Escape(result.ToLocalChecked());
86+
#else
87+
return scope.Escape(v8::JSON::Parse(json_string));
88+
#endif // NODE_MODULE_VERSION > NODE_6_0_MODULE_VERSION
89+
#endif // NAN_JSON_H_NEED_PARSE
90+
}
91+
92+
inline
93+
Nan::MaybeLocal<v8::String> Stringify(v8::Local<v8::Object> json_object) {
94+
Nan::EscapableHandleScope scope;
95+
Nan::MaybeLocal<v8::String> result =
96+
#if NAN_JSON_H_NEED_STRINGIFY
97+
Nan::To<v8::String>(stringify(json_object));
98+
#else
99+
v8::JSON::Stringify(Nan::GetCurrentContext(), json_object);
100+
#endif // NAN_JSON_H_NEED_STRINGIFY
101+
if (result.IsEmpty()) return v8::Local<v8::String>();
102+
return scope.Escape(result.ToLocalChecked());
103+
}
104+
105+
inline
106+
Nan::MaybeLocal<v8::String> Stringify(v8::Local<v8::Object> json_object,
107+
v8::Local<v8::String> gap) {
108+
Nan::EscapableHandleScope scope;
109+
Nan::MaybeLocal<v8::String> result =
110+
#if NAN_JSON_H_NEED_STRINGIFY
111+
Nan::To<v8::String>(stringify(json_object, gap));
112+
#else
113+
v8::JSON::Stringify(Nan::GetCurrentContext(), json_object, gap);
114+
#endif // NAN_JSON_H_NEED_STRINGIFY
115+
if (result.IsEmpty()) return v8::Local<v8::String>();
116+
return scope.Escape(result.ToLocalChecked());
117+
}
118+
119+
private:
120+
NAN_DISALLOW_ASSIGN_COPY_MOVE(JSON)
121+
#if NAN_JSON_H_NEED_PARSE
122+
Nan::Callback parse_cb_;
123+
#endif // NAN_JSON_H_NEED_PARSE
124+
#if NAN_JSON_H_NEED_STRINGIFY
125+
Nan::Callback stringify_cb_;
126+
#endif // NAN_JSON_H_NEED_STRINGIFY
127+
128+
#if NAN_JSON_H_NEED_PARSE
129+
inline v8::Local<v8::Value> parse(v8::Local<v8::Value> arg) {
130+
assert(!parse_cb_.IsEmpty() && "parse_cb_ is empty");
131+
return parse_cb_.Call(1, &arg);
132+
}
133+
#endif // NAN_JSON_H_NEED_PARSE
134+
135+
#if NAN_JSON_H_NEED_STRINGIFY
136+
inline v8::Local<v8::Value> stringify(v8::Local<v8::Value> arg) {
137+
assert(!stringify_cb_.IsEmpty() && "stringify_cb_ is empty");
138+
return stringify_cb_.Call(1, &arg);
139+
}
140+
141+
inline v8::Local<v8::Value> stringify(v8::Local<v8::Value> arg,
142+
v8::Local<v8::String> gap) {
143+
assert(!stringify_cb_.IsEmpty() && "stringify_cb_ is empty");
144+
145+
v8::Local<v8::Value> argv[] = {
146+
arg,
147+
Nan::Null(),
148+
gap
149+
};
150+
return stringify_cb_.Call(3, argv);
151+
}
152+
#endif // NAN_JSON_H_NEED_STRINGIFY
153+
};
154+
155+
#endif // NAN_JSON_H_

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"Nathan Rajlich <[email protected]> (https://github.com/TooTallNate)",
2020
"Brett Lawson <[email protected]> (https://github.com/brett19)",
2121
"Ben Noordhuis <[email protected]> (https://github.com/bnoordhuis)",
22-
"David Siegel <[email protected]> (https://github.com/agnat)"
22+
"David Siegel <[email protected]> (https://github.com/agnat)",
23+
"Michael Ira Krufky <[email protected]> (https://github.com/mkrufky)"
2324
],
2425
"devDependencies": {
2526
"bindings": "~1.2.1",

test/binding.gyp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,12 @@
156156
"target_name" : "private"
157157
, "sources" : [ "cpp/private.cpp" ]
158158
}
159+
, {
160+
"target_name" : "parse"
161+
, "sources" : [ "cpp/json-parse.cpp" ]
162+
}
163+
, {
164+
"target_name" : "stringify"
165+
, "sources" : [ "cpp/json-stringify.cpp" ]
166+
}
159167
]}

test/cpp/json-parse.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*********************************************************************
2+
* NAN - Native Abstractions for Node.js
3+
*
4+
* Copyright (c) 2017 NAN contributors
5+
*
6+
* MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
7+
********************************************************************/
8+
9+
#include <nan.h>
10+
11+
NAN_METHOD(Parse) {
12+
Nan::JSON NanJSON;
13+
14+
Nan::MaybeLocal<v8::String> inp = Nan::To<v8::String>(info[0]);
15+
16+
if (!inp.IsEmpty()) {
17+
Nan::MaybeLocal<v8::Value> result = NanJSON.Parse(
18+
inp.ToLocalChecked()
19+
);
20+
21+
if (!result.IsEmpty()) {
22+
info.GetReturnValue().Set(result.ToLocalChecked());
23+
}
24+
}
25+
}
26+
27+
NAN_MODULE_INIT(Init) {
28+
Nan::Set(target
29+
, Nan::New<v8::String>("parse").ToLocalChecked()
30+
, Nan::New<v8::FunctionTemplate>(Parse)->GetFunction()
31+
);
32+
}
33+
34+
NODE_MODULE(parse, Init)

0 commit comments

Comments
 (0)