Skip to content
This repository was archived by the owner on Nov 5, 2024. It is now read-only.

Commit d936ef8

Browse files
authored
Merge pull request #2 from lib-ruby-parser/use-new-bytes-api
use new bytes api
2 parents 838f80b + a52681d commit d936ef8

File tree

7 files changed

+213
-127
lines changed

7 files changed

+213
-127
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ ifeq ($(UNAME_S),Darwin)
66
PLATFORM = darwin-x86_64
77
endif
88

9-
VERSION = 3.0.0-3.2
9+
VERSION = 3.0.0-3.3
1010

1111
ifndef BUILD_ENV
1212
BUILD_ENV = debug

bytes.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#ifndef LIB_RUBY_PARSER_NODE_BYTES_H
2+
#define LIB_RUBY_PARSER_NODE_BYTES_H
3+
4+
#include <napi.h>
5+
#include <variant>
6+
#include "lib-ruby-parser.h"
7+
#include "result.h"
8+
9+
namespace lib_ruby_parser_node
10+
{
11+
class Bytes : public lib_ruby_parser::Bytes
12+
{
13+
public:
14+
explicit Bytes(lib_ruby_parser::Bytes bytes)
15+
{
16+
lib_ruby_parser::Bytes(bytes.into_ptr());
17+
}
18+
19+
Napi::Value to_v8_value(Napi::Env env) const
20+
{
21+
Napi::TypedArray array = Napi::Uint8Array::New(env, this->size());
22+
for (size_t i = 0; i < this->size(); i++)
23+
{
24+
array.Set(i, this->at(i));
25+
}
26+
return array;
27+
}
28+
29+
static Result<lib_ruby_parser::Bytes> FromV8(Napi::Value array)
30+
{
31+
if (!array.IsTypedArray())
32+
{
33+
return "must be a typed array";
34+
}
35+
36+
auto typed_array = array.As<Napi::TypedArray>();
37+
if (typed_array.TypedArrayType() != napi_uint8_array)
38+
{
39+
return "must be a typed array";
40+
}
41+
42+
auto unit8_array = array.As<Napi::Uint8Array>();
43+
44+
auto size = unit8_array.ElementLength();
45+
auto ptr = (char *)malloc(size * sizeof(char));
46+
memcpy(ptr, unit8_array.Data(), size);
47+
auto bytes = lib_ruby_parser::Bytes(ptr, size);
48+
49+
return std::move(bytes);
50+
}
51+
};
52+
} // namespace lib_ruby_parser_node
53+
54+
#endif // LIB_RUBY_PARSER_NODE_BYTES_H

convert.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,10 @@ namespace lib_ruby_parser_node
239239

240240
Napi::Value convert(lib_ruby_parser::Bytes bytes, Napi::Env env)
241241
{
242-
Napi::Array array = Napi::Array::New(env, bytes.size());
242+
Napi::TypedArray array = Napi::Uint8Array::New(env, bytes.size());
243243
for (size_t i = 0; i < bytes.size(); i++)
244244
{
245-
array.Set(i, Napi::Number::New(env, bytes.at(i)));
245+
array.Set(i, bytes.at(i));
246246
}
247247
return array;
248248
}

custom_decoder.h

Lines changed: 19 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,22 @@
33

44
#include <napi.h>
55
#include "lib-ruby-parser.h"
6+
#include "bytes.h"
7+
#include <iostream>
68

79
namespace lib_ruby_parser_node
810
{
911

1012
class JsCustomDecoder : public lib_ruby_parser::CustomDecoder
1113
{
1214
public:
13-
struct DecodeError
14-
{
15-
bool has_error;
16-
std::string error;
17-
};
18-
1915
Napi::FunctionReference callback;
20-
std::shared_ptr<DecodeError> error;
16+
std::shared_ptr<std::string> error;
2117

22-
JsCustomDecoder(const Napi::Function &callback, std::shared_ptr<DecodeError> error)
18+
JsCustomDecoder(const Napi::Function &callback)
2319
{
2420
this->callback = Napi::Persistent(callback);
25-
this->error = error;
21+
this->error = std::shared_ptr<std::string>(nullptr);
2622
}
2723

2824
Napi::Env env()
@@ -32,54 +28,33 @@ namespace lib_ruby_parser_node
3228

3329
virtual Result rewrite(std::string encoding, lib_ruby_parser::Bytes input)
3430
{
35-
Napi::Value raw_response = callback.Call({
31+
Napi::Value response = this->callback.Call({
3632
Napi::String::New(env(), encoding),
3733
convert(std::move(input), env()),
3834
});
39-
if (!raw_response.IsObject())
40-
return JsError("response must be an object");
41-
42-
Napi::Object response = raw_response.As<Napi::Object>();
43-
44-
Napi::Value success = response.Get("success");
45-
if (!success.IsBoolean())
46-
return JsError("'success' field must be true/false");
4735

48-
if (success.ToBoolean().Value())
36+
if (response.IsString())
4937
{
50-
// success, consume 'output' field
51-
if (!response.Get("output").IsArray())
52-
return JsError("'output' field must be an array");
53-
54-
Napi::Array output = response.Get("output").As<Napi::Array>();
55-
auto ptr = (char *)malloc(output.Length());
56-
for (size_t i = 0; i < output.Length(); i++)
57-
{
58-
Napi::Value byte = output[i];
59-
if (!byte.IsNumber())
60-
{
61-
return JsError("'output' field contains invalid byte");
62-
}
63-
ptr[i] = byte.ToNumber().Int32Value();
64-
}
65-
return Result::Ok(lib_ruby_parser::Bytes(ptr, output.Length()));
38+
// error
39+
Napi::String js_error = Napi::String::New(env(), response.ToString().Utf8Value());
40+
return JsError(js_error);
6641
}
67-
else
42+
// success
43+
auto bytes_result = Bytes::FromV8(response);
44+
if (bytes_result.is_err())
6845
{
69-
// error, consume 'error' field
70-
if (!response.Get("error").IsString())
71-
return JsError("'error' field must be a string");
72-
73-
Napi::String error = Napi::String::New(env(), response.Get("error").ToString().Utf8Value());
74-
return Result::Error(error);
46+
return JsError("custom decoder output: " + bytes_result.get_err());
7547
}
48+
49+
auto bytes = bytes_result.get();
50+
return Result::Ok(std::move(bytes));
7651
}
7752
virtual ~JsCustomDecoder() {}
7853

7954
Result JsError(std::string message)
8055
{
81-
this->error->has_error = true;
82-
this->error->error = "custom_rewriter: " + message;
56+
auto new_error = new std::string("custom_rewriter: " + message);
57+
this->error.reset(new_error);
8358
return Result::Error(message);
8459
}
8560
};

node_bindings.cc

Lines changed: 71 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,97 +2,110 @@
22
#include "lib-ruby-parser.h"
33
#include "convert.h"
44
#include "custom_decoder.h"
5+
#include "bytes.h"
6+
#include "result.h"
57
#include <iostream>
68
#include <variant>
79
#include <tuple>
810

911
namespace lib_ruby_parser_node
1012
{
11-
Napi::Value JsThrow(Napi::Env env, std::string message)
13+
Napi::Value JsThrow(Napi::Env env, const std::string &message)
1214
{
1315
Napi::TypeError::New(env, message).ThrowAsJavaScriptException();
1416
return env.Null();
1517
}
1618

17-
using SharedDecodeError = std::shared_ptr<JsCustomDecoder::DecodeError>;
18-
using BuildParserOptionsSuccess = std::tuple<lib_ruby_parser::ParserOptions, SharedDecodeError>;
19-
using BuildParserOptionsResult = std::variant<BuildParserOptionsSuccess, std::string>;
20-
21-
BuildParserOptionsResult BuildParserOptions(Napi::Object &object)
19+
class ParserOptions : public lib_ruby_parser::ParserOptions
2220
{
23-
lib_ruby_parser::ParserOptions options;
24-
options.record_tokens = object.Get("record_tokens").ToBoolean().Value();
25-
options.debug = object.Get("debug").ToBoolean().Value();
26-
Napi::Value buffer_name = object.Get("buffer_name");
27-
if (buffer_name.IsString())
28-
{
29-
options.buffer_name = buffer_name.As<Napi::String>().Utf8Value();
30-
}
31-
else if (buffer_name.IsUndefined())
32-
{
33-
// ok, default is used
34-
}
35-
else
36-
{
37-
return "buffer_name must be string/undefined";
38-
}
39-
SharedDecodeError decode_error = std::make_shared<JsCustomDecoder::DecodeError>();
40-
Napi::Value custom_decoder = object.Get("custom_decoder");
41-
if (custom_decoder.IsFunction())
42-
{
43-
options.custom_decoder = std::make_unique<JsCustomDecoder>(custom_decoder.As<Napi::Function>(), decode_error);
44-
}
45-
else if (custom_decoder.IsUndefined())
46-
{
47-
// ok, default is used
48-
}
49-
else
21+
public:
22+
std::shared_ptr<std::string> decode_error;
23+
24+
static Result<ParserOptions> FromV8(Napi::Value value)
5025
{
51-
return "custom_decoder must be function/undefined";
26+
if (!value.IsObject())
27+
{
28+
return "parser_options must be an object";
29+
}
30+
Napi::Object object = value.As<Napi::Object>();
31+
ParserOptions options;
32+
33+
options.record_tokens = object.Get("record_tokens").ToBoolean().Value();
34+
options.debug = object.Get("debug").ToBoolean().Value();
35+
36+
Napi::Value buffer_name = object.Get("buffer_name");
37+
if (buffer_name.IsString())
38+
{
39+
options.buffer_name = buffer_name.As<Napi::String>().Utf8Value();
40+
}
41+
else if (buffer_name.IsUndefined())
42+
{
43+
// ok, default is used
44+
}
45+
else
46+
{
47+
return "buffer_name must be string/undefined";
48+
}
49+
50+
Napi::Value custom_decoder = object.Get("custom_decoder");
51+
if (custom_decoder.IsFunction())
52+
{
53+
auto decoder = std::make_unique<JsCustomDecoder>(custom_decoder.As<Napi::Function>());
54+
options.decode_error = decoder->error;
55+
options.custom_decoder = std::move(decoder);
56+
}
57+
else if (custom_decoder.IsUndefined())
58+
{
59+
// ok, default is used
60+
}
61+
else
62+
{
63+
return "custom_decoder must be function/undefined";
64+
}
65+
66+
return std::move(options);
5267
}
68+
};
5369

54-
return std::make_tuple(std::move(options), decode_error);
55-
}
56-
57-
Napi::Value parse(const Napi::CallbackInfo &info)
70+
Result<std::unique_ptr<lib_ruby_parser::ParserResult>> parse(const Napi::CallbackInfo &info)
5871
{
5972
Napi::Env env = info.Env();
6073

6174
if (info.Length() != 2)
62-
return JsThrow(env, "Wrong number of arguments (expected 2)");
75+
{
76+
return "Wrong number of arguments (expected 2)";
77+
}
78+
79+
UNWRAP_RESULT(bytes, Bytes::FromV8(info[0]));
80+
UNWRAP_RESULT(options, ParserOptions::FromV8(info[1]));
6381

64-
if (!info[0].IsString())
65-
return JsThrow(env, "The first argument must be a string");
66-
std::string source = info[0].As<Napi::String>().Utf8Value();
82+
auto decode_error = options.decode_error;
6783

68-
if (!info[1].IsObject())
69-
return JsThrow(env, "The second argument must be an object");
70-
Napi::Object js_options = info[1].As<Napi::Object>();
84+
auto result = lib_ruby_parser::ParserResult::from_source(std::move(bytes), std::move(options));
7185

72-
auto build_result = BuildParserOptions(js_options);
73-
if (auto error = std::get_if<std::string>(&build_result))
86+
if (decode_error)
7487
{
75-
return JsThrow(env, *error);
88+
return std::string(*(decode_error.get()));
7689
}
77-
auto tuple = std::get<BuildParserOptionsSuccess>(std::move(build_result));
78-
auto decode_error = std::get<1>(tuple);
79-
auto options = std::get<0>(std::move(tuple));
90+
return std::move(result);
91+
}
8092

81-
auto result = lib_ruby_parser::ParserResult::from_source(source, std::move(options));
93+
Napi::Value js_parse(const Napi::CallbackInfo &info)
94+
{
95+
Napi::Env env = info.Env();
8296

83-
if (decode_error->has_error)
97+
auto result = parse(info);
98+
if (result.is_err())
8499
{
85-
Napi::TypeError::New(env, decode_error->error)
86-
.ThrowAsJavaScriptException();
87-
return env.Null();
100+
return JsThrow(env, result.get_err());
88101
}
89-
return convert(std::move(result), env);
102+
return convert(result.get(), env);
90103
}
91104

92105
Napi::Object Init(Napi::Env env, Napi::Object exports)
93106
{
94107
exports.Set(Napi::String::New(env, "parse"),
95-
Napi::Function::New(env, parse));
108+
Napi::Function::New(env, js_parse));
96109

97110
InitCustomTypes(env, exports);
98111
InitNodeTypes(env, exports);

result.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#ifndef LIB_RUBY_PARSER_NODE_RESULT_H
2+
#define LIB_RUBY_PARSER_NODE_RESULT_H
3+
4+
#include <variant>
5+
#include <string>
6+
7+
namespace lib_ruby_parser_node
8+
{
9+
#define UNWRAP_RESULT(var, call) \
10+
auto result_##var = (call); \
11+
if (result_##var.is_err()) \
12+
{ \
13+
return result_##var.get_err(); \
14+
} \
15+
auto var = result_##var.get();
16+
17+
template <typename T>
18+
class Result
19+
{
20+
std::variant<std::string, T> data;
21+
22+
public:
23+
Result() = default;
24+
Result(T value) : data(std::move(value)){};
25+
Result(std::string error) : data(error){};
26+
Result(const char *error) : data(error){};
27+
28+
bool is_ok()
29+
{
30+
return std::holds_alternative<T>(data);
31+
}
32+
33+
bool is_err()
34+
{
35+
return std::holds_alternative<std::string>(data);
36+
}
37+
38+
T
39+
get()
40+
{
41+
return std::move(std::get<T>(data));
42+
}
43+
44+
std::string get_err()
45+
{
46+
return std::get<std::string>(data);
47+
}
48+
}; // namespace lib_ruby_parser_node
49+
} // namespace lib_ruby_parser_node
50+
51+
#endif // LIB_RUBY_PARSER_NODE_RESULT_H

0 commit comments

Comments
 (0)