Skip to content

Commit 967d144

Browse files
committed
wrapper: don't use json to convert it to a js object, but do it properly
also adjust the types to correct include u64 numbers as bigint
1 parent cb9a96b commit 967d144

File tree

5 files changed

+417
-41
lines changed

5 files changed

+417
-41
lines changed

binding.gyp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
],
5959
],
6060
"defines": ["V8_DEPRECATION_WARNINGS=1"],
61-
"sources": ["src/cpp/wrapper.cpp"],
61+
"sources": ["src/cpp/wrapper.cpp", "src/cpp/convert.cpp"],
6262
"include_dirs": [
6363
"<!@(node -e \"require('nan')\")",
6464
"<!@(pkg-config oopetris-recordings --cflags-only-I | sed s/-I//g)",

src/cpp/convert.cpp

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
2+
3+
#include "./convert.hpp"
4+
5+
#include <limits>
6+
7+
8+
static v8::Local<v8::Value> new_bigint_from_uint64t(v8::Isolate* isolate, uint64_t value) {
9+
10+
return v8::BigInt::NewFromUnsigned(isolate, value);
11+
}
12+
13+
static v8::Local<v8::Value> new_bigint_from_int64t(v8::Isolate* isolate, int64_t value) {
14+
return v8::BigInt::New(isolate, value);
15+
}
16+
17+
static v8::Local<v8::Value>
18+
information_value_to_js(v8::Isolate* isolate, const recorder::InformationValue& information_value) {
19+
20+
return std::visit(
21+
helper::overloaded{
22+
[](const std::string& value) -> v8::Local<v8::Value> {
23+
return Nan::New<v8::String>(value).ToLocalChecked();
24+
},
25+
[](const float& value) -> v8::Local<v8::Value> { return Nan::New<v8::Number>(value); },
26+
[](const double& value) -> v8::Local<v8::Value> { return Nan::New<v8::Number>(value); },
27+
[](const bool& value) -> v8::Local<v8::Value> { return Nan::New<v8::Boolean>(value); },
28+
[](const u8& value) -> v8::Local<v8::Value> { return Nan::New<v8::Uint32>(value); },
29+
[](const i8& value) -> v8::Local<v8::Value> { return Nan::New<v8::Int32>(value); },
30+
[](const u32& value) -> v8::Local<v8::Value> { return Nan::New<v8::Uint32>(value); },
31+
[](const i32& value) -> v8::Local<v8::Value> { return Nan::New<v8::Int32>(value); },
32+
[isolate](const u64& value) -> v8::Local<v8::Value> {
33+
return new_bigint_from_uint64t(isolate, value);
34+
},
35+
[isolate](const i64& value) -> v8::Local<v8::Value> {
36+
return new_bigint_from_int64t(isolate, value);
37+
},
38+
[isolate](const std::vector<recorder::InformationValue>& values) -> v8::Local<v8::Value> {
39+
v8::Local<v8::Array> array = v8::Array::New(isolate);
40+
41+
size_t i = 0;
42+
for (auto& value : values) {
43+
Nan::Set(array, i, information_value_to_js(isolate, value));
44+
++i;
45+
}
46+
47+
return array;
48+
} },
49+
information_value.underlying()
50+
);
51+
}
52+
53+
static v8::Local<v8::Value>
54+
information_to_js(v8::Isolate* isolate, const recorder::AdditionalInformation& information) {
55+
56+
v8::Local<v8::Object> result = Nan::New<v8::Object>();
57+
58+
for (const auto& [key, raw_value] : information) {
59+
v8::Local<v8::String> keyValue = Nan::New<v8::String>(key).ToLocalChecked();
60+
auto value = information_value_to_js(isolate, raw_value);
61+
Nan::Set(result, keyValue, value).Check();
62+
}
63+
64+
return result;
65+
}
66+
67+
const char* event_to_string(InputEvent event) {
68+
switch (event) {
69+
case InputEvent::RotateLeftPressed:
70+
return "RotateLeftPressed";
71+
case InputEvent::RotateRightPressed:
72+
return "RotateRightPressed";
73+
case InputEvent::MoveLeftPressed:
74+
return "MoveLeftPressed";
75+
case InputEvent::MoveRightPressed:
76+
return "MoveRightPressed";
77+
case InputEvent::MoveDownPressed:
78+
return "MoveDownPressed";
79+
case InputEvent::DropPressed:
80+
return "DropPressed";
81+
case InputEvent::HoldPressed:
82+
return "HoldPressed";
83+
case InputEvent::RotateLeftReleased:
84+
return "RotateLeftReleased";
85+
case InputEvent::RotateRightReleased:
86+
return "RotateRightReleased";
87+
case InputEvent::MoveLeftReleased:
88+
return "MoveLeftReleased";
89+
case InputEvent::MoveRightReleased:
90+
return "MoveRightReleased";
91+
case InputEvent::MoveDownReleased:
92+
return "MoveDownReleased";
93+
case InputEvent::DropReleased:
94+
return "DropReleased";
95+
case InputEvent::HoldReleased:
96+
return "HoldReleased";
97+
default:
98+
assert(false && "UNREACHABLE");
99+
return "<ERROR>";
100+
}
101+
}
102+
103+
static inline v8::Local<v8::Value> event_to_js_string(v8::Isolate* isolate, InputEvent event) {
104+
return Nan::New<v8::String>(event_to_string(event)).ToLocalChecked();
105+
}
106+
107+
static v8::Local<v8::Value> u64_to_appropiate_number(v8::Isolate* isolate, uint64_t value) {
108+
109+
if (value > std::numeric_limits<uint32_t>::max()) {
110+
return new_bigint_from_uint64t(isolate, value);
111+
}
112+
return Nan::New<v8::Uint32>(static_cast<uint32_t>(value));
113+
}
114+
115+
116+
static v8::Local<v8::Value> record_to_js(v8::Isolate* isolate, const recorder::Record& record) {
117+
118+
v8::Local<v8::Object> result = Nan::New<v8::Object>();
119+
120+
auto js_event = event_to_js_string(isolate, record.event);
121+
122+
auto js_simulation_step_index = u64_to_appropiate_number(isolate, record.simulation_step_index);
123+
124+
auto js_tetrion_index = Nan::New<v8::Uint32>(record.tetrion_index);
125+
126+
127+
std::vector<std::pair<std::string, v8::Local<v8::Value>>> properties_vector{
128+
{ "event", js_event },
129+
{ "simulation_step_index", js_simulation_step_index },
130+
{ "tetrion_index", js_tetrion_index }
131+
};
132+
133+
for (const auto& [key, value] : properties_vector) {
134+
v8::Local<v8::String> keyValue = Nan::New<v8::String>(key).ToLocalChecked();
135+
Nan::Set(result, keyValue, value).Check();
136+
}
137+
138+
return result;
139+
}
140+
141+
static v8::Local<v8::Value> records_to_js(v8::Isolate* isolate, const std::vector<recorder::Record>& records) {
142+
v8::Local<v8::Array> array = v8::Array::New(isolate);
143+
144+
size_t i = 0;
145+
for (auto& record : records) {
146+
Nan::Set(array, i, record_to_js(isolate, record));
147+
++i;
148+
}
149+
150+
return array;
151+
}
152+
153+
154+
static v8::Local<v8::Value> header_to_js(v8::Isolate* isolate, const recorder::TetrionHeader& header) {
155+
156+
v8::Local<v8::Object> result = Nan::New<v8::Object>();
157+
158+
auto js_seed = u64_to_appropiate_number(isolate, header.seed);
159+
160+
auto js_starting_level = Nan::New<v8::Uint32>(header.starting_level);
161+
162+
std::vector<std::pair<std::string, v8::Local<v8::Value>>> properties_vector{
163+
{ "seed", js_seed },
164+
{ "starting_level", js_starting_level },
165+
};
166+
167+
for (const auto& [key, value] : properties_vector) {
168+
v8::Local<v8::String> keyValue = Nan::New<v8::String>(key).ToLocalChecked();
169+
Nan::Set(result, keyValue, value).Check();
170+
}
171+
172+
return result;
173+
}
174+
175+
176+
static v8::Local<v8::Value> headers_to_js(v8::Isolate* isolate, const std::vector<recorder::TetrionHeader>& headers) {
177+
v8::Local<v8::Array> array = v8::Array::New(isolate);
178+
179+
size_t i = 0;
180+
for (auto& header : headers) {
181+
Nan::Set(array, i, header_to_js(isolate, header));
182+
++i;
183+
}
184+
185+
return array;
186+
}
187+
188+
189+
static v8::Local<v8::Value> mino_position_to_js(v8::Isolate* isolate, const grid::GridPoint& mino_position) {
190+
191+
v8::Local<v8::Object> result = Nan::New<v8::Object>();
192+
193+
auto js_x = Nan::New<v8::Uint32>(mino_position.x);
194+
195+
auto js_y = Nan::New<v8::Uint32>(mino_position.y);
196+
197+
std::vector<std::pair<std::string, v8::Local<v8::Value>>> properties_vector{
198+
{ "x", js_x },
199+
{ "y", js_y }
200+
};
201+
202+
for (const auto& [key, value] : properties_vector) {
203+
v8::Local<v8::String> keyValue = Nan::New<v8::String>(key).ToLocalChecked();
204+
Nan::Set(result, keyValue, value).Check();
205+
}
206+
207+
return result;
208+
}
209+
210+
211+
const char* tetromino_type_to_string(helper::TetrominoType type) {
212+
switch (type) {
213+
case helper::TetrominoType::I: {
214+
return "I";
215+
}
216+
case helper::TetrominoType::J: {
217+
return "J";
218+
}
219+
case helper::TetrominoType::L: {
220+
return "L";
221+
}
222+
case helper::TetrominoType::O: {
223+
return "O";
224+
}
225+
case helper::TetrominoType::S: {
226+
return "S";
227+
}
228+
case helper::TetrominoType::T: {
229+
return "T";
230+
}
231+
case helper::TetrominoType::Z: {
232+
return "Z";
233+
}
234+
default:
235+
assert(false && "UNREACHABLE");
236+
return "<ERROR>";
237+
}
238+
}
239+
240+
static inline v8::Local<v8::Value> tetromino_type_to_js_string(v8::Isolate* isolate, helper::TetrominoType type) {
241+
return Nan::New<v8::String>(tetromino_type_to_string(type)).ToLocalChecked();
242+
}
243+
244+
245+
static v8::Local<v8::Value> mino_to_js(v8::Isolate* isolate, const Mino& mino) {
246+
247+
v8::Local<v8::Object> result = Nan::New<v8::Object>();
248+
249+
auto js_position = mino_position_to_js(isolate, mino.position());
250+
251+
auto js_type = tetromino_type_to_js_string(isolate, mino.type());
252+
253+
std::vector<std::pair<std::string, v8::Local<v8::Value>>> properties_vector{
254+
{ "position", js_position },
255+
{ "type", js_type }
256+
};
257+
258+
for (const auto& [key, value] : properties_vector) {
259+
v8::Local<v8::String> keyValue = Nan::New<v8::String>(key).ToLocalChecked();
260+
Nan::Set(result, keyValue, value).Check();
261+
}
262+
263+
return result;
264+
}
265+
266+
267+
static v8::Local<v8::Value> mino_stack_to_js(v8::Isolate* isolate, const std::vector<Mino>& mino_stack) {
268+
v8::Local<v8::Array> array = v8::Array::New(isolate);
269+
270+
size_t i = 0;
271+
for (auto& mino : mino_stack) {
272+
Nan::Set(array, i, mino_to_js(isolate, mino));
273+
++i;
274+
}
275+
276+
return array;
277+
}
278+
279+
280+
static v8::Local<v8::Value> snapshot_to_js(v8::Isolate* isolate, const TetrionSnapshot& snapshot) {
281+
282+
v8::Local<v8::Object> result = Nan::New<v8::Object>();
283+
284+
auto js_level = Nan::New<v8::Uint32>(snapshot.level());
285+
286+
auto js_lines_cleared = Nan::New<v8::Uint32>(snapshot.lines_cleared());
287+
288+
auto js_mino_stack = mino_stack_to_js(isolate, snapshot.mino_stack().minos());
289+
290+
auto js_score = u64_to_appropiate_number(isolate, snapshot.score());
291+
292+
auto js_simulation_step_index = u64_to_appropiate_number(isolate, snapshot.simulation_step_index());
293+
294+
auto js_tetrion_index = Nan::New<v8::Uint32>(snapshot.tetrion_index());
295+
296+
std::vector<std::pair<std::string, v8::Local<v8::Value>>> properties_vector{
297+
{ "level", js_level },
298+
{ "lines_cleared", js_lines_cleared },
299+
{ "mino_stack", js_mino_stack },
300+
{ "score", js_score },
301+
{ "simulation_step_index", js_simulation_step_index },
302+
{ "tetrion_index", js_tetrion_index },
303+
};
304+
305+
for (const auto& [key, value] : properties_vector) {
306+
v8::Local<v8::String> keyValue = Nan::New<v8::String>(key).ToLocalChecked();
307+
Nan::Set(result, keyValue, value).Check();
308+
}
309+
310+
return result;
311+
}
312+
313+
314+
static v8::Local<v8::Value> snapshots_to_js(v8::Isolate* isolate, const std::vector<TetrionSnapshot>& snapshots) {
315+
v8::Local<v8::Array> array = v8::Array::New(isolate);
316+
317+
size_t i = 0;
318+
for (auto& snapshot : snapshots) {
319+
Nan::Set(array, i, snapshot_to_js(isolate, snapshot));
320+
++i;
321+
}
322+
323+
return array;
324+
}
325+
326+
327+
v8::Local<v8::Value> recording_reader_to_js(v8::Isolate* isolate, const recorder::RecordingReader& reader) {
328+
329+
330+
v8::Local<v8::Object> result = Nan::New<v8::Object>();
331+
332+
auto js_version = Nan::New<v8::Uint32>(recorder::Recording::current_supported_version_number);
333+
334+
auto js_information = information_to_js(isolate, reader.information());
335+
336+
auto js_records = records_to_js(isolate, reader.records());
337+
338+
auto js_snapshots = snapshots_to_js(isolate, reader.snapshots());
339+
340+
auto js_tetrion_headers = headers_to_js(isolate, reader.tetrion_headers());
341+
342+
343+
std::vector<std::pair<std::string, v8::Local<v8::Value>>> properties_vector{
344+
{ "information", js_information },
345+
{ "records", js_records },
346+
{ "snapshots", js_snapshots },
347+
{ "tetrion_headers", js_tetrion_headers },
348+
{ "version", js_version }
349+
};
350+
351+
for (const auto& [key, value] : properties_vector) {
352+
v8::Local<v8::String> keyValue = Nan::New<v8::String>(key).ToLocalChecked();
353+
Nan::Set(result, keyValue, value).Check();
354+
}
355+
356+
return result;
357+
}

src/cpp/convert.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
3+
#pragma once
4+
5+
#if defined(__GNUC__) & !defined(__clang__)
6+
#pragma GCC diagnostic push
7+
#pragma GCC diagnostic ignored "-Wtemplate-id-cdtor"
8+
#endif
9+
10+
#include <nan.h>
11+
12+
#if defined(__GNUC__) & !defined(__clang__)
13+
#pragma GCC diagnostic pop
14+
#endif
15+
16+
17+
#include <core/core.hpp>
18+
#include <recordings/recordings.hpp>
19+
20+
21+
v8::Local<v8::Value> recording_reader_to_js(v8::Isolate* isolate, const recorder::RecordingReader& reader);

0 commit comments

Comments
 (0)