sheki
sheki

Reputation: 9349

How to parse JSON in an embedded v8?

I am trying to Parse JS in my embedded V8 application and I get a SIGSEGV always. I am not sure what is happening.

My code to parse json,

v8::Handle<v8::Value> FromJSONString(
    v8::Handle<v8::Value> json_string) {
  v8::HandleScope scope;
  v8::Handle<v8::Context> context = v8::Context::GetCurrent();
  v8::Handle<v8::Object> global = context->Global();

  v8::Handle<v8::Value> JSON_value = global->Get(v8::String::New("JSON"));
  if (!IsObject(JSON_value)) {
    return scope.Close(v8::Undefined());
  }
  v8::Handle<v8::Object> JSON  = JSON_value->ToObject();

  v8::Handle<v8::Value> JSON_parse_value = JSON->Get(v8::String::New("parse"));

  if (JSON_parse_value.IsEmpty() || JSON_parse_value->IsNull() ||
      JSON_parse_value->IsUndefined() ||!JSON_parse_value->IsFunction()) {
    return scope.Close(v8::Undefined());
  }


  v8::Handle<v8::Function> JSON_parse =
      v8::Handle<v8::Function>::Cast(JSON_parse_value);

  return scope.Close(JSON_parse->Call(JSON, 1, &json_string));
}

The specific site which crashes =>

bool extractSource(std::string* source, std::string& body) {
    v8::HandleScope scope; // this is needed and clears the memory
    if (body.empty()) {
        return false;
    }
    v8::Handle<v8::Value> value = v8_server_utils::FromJSONString(body);
    if (value->IsEmpty()) { // CRASHES HERE.
        return false;
    }
    if (value->IsNull()) {
        return false;
    }
    if (value->IsUndefined()) {
        return false;
    }
    if (!value->IsObject()) {
        return false;
    }
    auto object = value->ToObject();
    auto source_key = v8::String::New("source");
    if (object.IsEmpty() || object->IsNull() || object->IsUndefined() ||
        !object->Has(source_key)) {
        return false;
    }
    auto source_obj = object->Get(source_key);
    *source = v8_server_utils::JSStringToCString(source_obj->ToString());
    return true;
}

Upvotes: 6

Views: 5912

Answers (4)

mkrufky
mkrufky

Reputation: 3388

I'm adding this additional answer because my prior answer is still correct, but as of now a newer and better answer is available.

NAN version 2.6.2 was released on April 12, 2017. This version introduces a new object, Nan::JSON

The Nan::JSON object provides c++ versions of the methods offered by the JSON object in javascript, in a way that is backwards compatible with all versions of Node.js supported by NAN. V8 otherwise exposes these methods via the v8::JSON object.

Nan::JSON.Parse

A simple wrapper around v8::JSON::Parse.

Definition:

Nan::MaybeLocal<v8::Value> Nan::JSON::Parse(v8::Local<v8::String> json_string);

Use JSON.Parse(json_string) to parse a string into a v8::Value.

Example:

v8::Local<v8::String> json_string = Nan::New("{ \"JSON\": \"object\" }").ToLocalChecked();

Nan::JSON NanJSON;
Nan::MaybeLocal<v8::Value> result = NanJSON.Parse(json_string);
if (!result.IsEmpty()) {
  v8::Local<v8::Value> val = result.ToLocalChecked();
}

Nan::JSON.Stringify

A simple wrapper around v8::JSON::Stringify.

Definition:

Nan::MaybeLocal<v8::String> Nan::JSON::Stringify(v8::Local<v8::Object> json_object, v8::Local<v8::String> gap = v8::Local<v8::String>());

Use JSON.Stringify(value) to stringify a v8::Object.

Example:

v8::Local<v8::Object> obj = Nan::To<v8::Object>(val).ToLocalChecked();

Nan::JSON NanJSON;
Nan::MaybeLocal<v8::String> result = NanJSON.Stringify(obj);
if (!result.IsEmpty()) {
  v8::Local<v8::String> stringified = result.ToLocalChecked();
}

Refer to the V8 JSON object in the V8 documentation for more information about the original V8 versions these methods and their arguments.

The above was paraphrased from the NAN documentation

Upvotes: 0

mkrufky
mkrufky

Reputation: 3388

If you are writing a native node module and using NAN so that it will be supported across all major versions of Node.js, then I recommend that you try using the native-json npm package.

The package exposes the following methods:

static Nan::MaybeLocal<v8::Value> Native::JSON::Parse(v8::Local<v8::String> jsonString);

static Nan::MaybeLocal<v8::String> Native::JSON::Stringify(v8::Local<v8::Object> jsonObject, v8::Local<v8::String> gap = v8::Local<v8::String>())

As for the changes in v8::JSON in the versions of node supported by NAN:

v8::JSON::Parse was first available in node version 0.12.x - it is not otherwise available in versions 0.8 or 0.10.x

v8::JSON::Stringify was first available in node version 7 - it is not otherwise available in earlier versions

The native-json package's Native::JSON singleton class will perform the requests in the most efficient way available depending on the version of Node.js being built against. If the method is available in that version of V8 then it will be called. Otherwise, the Native::JSON singleton class falls back to grabbing the JSON object from the global context.

Source code is available on the github repository: node-native-json

Here is an example of how to use Native::JSON::Parse:

v8::Local<v8::String> json_string = Nan::New("{ \"JSON\": \"object\" }").ToLocalChecked();

v8::Local<v8::Value> val = Native::JSON::Parse(json_string).ToLocalChecked();

Upvotes: 1

stj
stj

Reputation: 9097

The following code should work provided that you're on an older V8 version, such as v3.14:

v8::Handle<v8::Value> FromJsonString (v8::Handle<v8::Value> jsonString) {
  v8::HandleScope scope;
  v8::Handle<v8::Context> context = v8::Context::GetCurrent();
  v8::Handle<v8::Object> global = context->Global();

  // find JSON object in global scope
  v8::Handle<v8::Value> jsonValue = global->Get(v8::String::New("JSON"));

  if (! jsonValue->IsObject()) {
    return scope.Close(v8::Undefined());
  }

  v8::Handle<v8::Object> json = jsonValue->ToObject();

  // find "parse" attribute
  v8::Handle<v8::Value> parse = json->Get(v8::String::New("parse"));

  if (parse.IsEmpty() ||
      ! parse->IsFunction()) {
    return scope.Close(v8::Undefined());
  }

  // cast into a function    
  v8::Handle<v8::Function> parseFunction = v8::Handle<v8::Function>::Cast(parse);

  // and call it
  return scope.Close(parseFunction->Call(json, 1, &jsonString));
}


static bool ExtractSource (std::string& source, std::string const& body) {
  v8::HandleScope scope;

  // convert whole body from cstring to V8 string
  v8::Handle<v8::String> bodyString = v8::String::New(body.c_str(), body.size());

  // call JSON.parse() on the body
  v8::Handle<v8::Value> value = FromJsonString(bodyString);

  // check if result is valid
  if (value.IsEmpty() ||
      value->IsNull() ||
      value->IsUndefined() ||
      ! value->IsObject()) {
    return false;
  }

  auto object = value->ToObject();

  // extract "source" attribute from result     
  auto sourceKey = v8::String::New("source");

  if (object.IsEmpty() || 
      object->IsNull() || 
      object->IsUndefined() ||
      ! object->Has(sourceKey)) {
    return false;
  }

  auto sourceValue = object->Get(sourceKey);

  if (! sourceValue->IsString()) {
    return false;
  }

  // convert the v8 string value into a cstring
  v8::String::Utf8Value sourceString(sourceValue->ToString());

  if (*sourceString == nullptr) {
    return false;
  }

  source.assign(*sourceString, sourceString.length());
  return true;
}

int main () {
  // test data
  std::string body = "{ \"body\": \"test\", \"source\": \"some string value\" }";
  // result
  std::string source;

  // call function and dump result
  std::cout << "is valid: " << ExtractSource(source, body) << std::endl;
  std::cout << "value of source: '" << source << "'" << std::endl;
}

Upvotes: 3

iefserge
iefserge

Reputation: 1146

You can use JSON Parse function exposed through API:

v8::Local<v8::String> str; // some string
v8::Local<v8::Value> result = v8::JSON::Parse(str);

Newer versions of V8 provide EscapableHandleScope you need to use to return handle from function:

v8::EscapableHandleScope scope(isolate);
return scope.Escape(value);

This

if (value->IsEmpty()) { // CRASHES HERE.

should probably be

if (value.IsEmpty())

Hope this helps.

Upvotes: 7

Related Questions