Skip to content

Commit 695ef9f

Browse files
committed
fix(runtime): harden console formatting
1 parent 96c6839 commit 695ef9f

1 file changed

Lines changed: 135 additions & 24 deletions

File tree

NativeScript/runtime/modules/console/Console.cpp

Lines changed: 135 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <sstream>
44
#include <string>
5+
#include <vector>
56

67
#include "ffi/NativeScriptException.h"
78
#include "js_native_api.h"
@@ -34,6 +35,64 @@ extern "C" void NSLog(CFStringRef format, ...);
3435

3536
namespace nativescript {
3637

38+
namespace {
39+
40+
bool readStringValue(napi_env env, napi_value value, std::string& result) {
41+
if (value == nullptr) {
42+
return false;
43+
}
44+
45+
size_t length = 0;
46+
if (napi_get_value_string_utf8(env, value, nullptr, 0, &length) != napi_ok) {
47+
return false;
48+
}
49+
50+
std::vector<char> buffer(length + 1);
51+
size_t copied = 0;
52+
if (napi_get_value_string_utf8(env, value, buffer.data(), buffer.size(),
53+
&copied) != napi_ok) {
54+
return false;
55+
}
56+
57+
result.assign(buffer.data(), copied);
58+
return true;
59+
}
60+
61+
bool throwPendingException(napi_env env, const std::string& message) {
62+
bool isPending = false;
63+
if (napi_is_exception_pending(env, &isPending) != napi_ok || !isPending) {
64+
return false;
65+
}
66+
67+
napi_value exception = nullptr;
68+
napi_get_and_clear_last_exception(env, &exception);
69+
70+
if (exception != nullptr &&
71+
napi_util::is_of_type(env, exception, napi_object)) {
72+
throw NativeScriptException(env, exception, message);
73+
}
74+
75+
throw NativeScriptException(env, message);
76+
}
77+
78+
bool coerceToString(napi_env env, napi_value value, std::string& result) {
79+
napi_value stringValue = nullptr;
80+
if (napi_coerce_to_string(env, value, &stringValue) != napi_ok ||
81+
stringValue == nullptr) {
82+
throwPendingException(env, "Error converting console argument to string");
83+
return false;
84+
}
85+
86+
if (!readStringValue(env, stringValue, result)) {
87+
throwPendingException(env, "Error reading console argument string");
88+
return false;
89+
}
90+
91+
return true;
92+
}
93+
94+
} // namespace
95+
3796
JS_CLASS_INIT(Console::Init) {
3897
napi_value Console, console;
3998

@@ -65,17 +124,38 @@ JS_METHOD(Console::Constructor) {
65124
}
66125

67126
std::string transformJSObject(napi_env env, napi_value object) {
68-
napi_value toStringFunc;
127+
napi_value toStringFunc = nullptr;
69128
bool hasToString = false;
70129

71130
// Check if the object has a toString method
72-
napi_has_named_property(env, object, "toString", &hasToString);
131+
if (napi_has_named_property(env, object, "toString", &hasToString) !=
132+
napi_ok) {
133+
throwPendingException(env, "Error reading console object toString");
134+
return "[object Object]";
135+
}
136+
73137
if (hasToString) {
74-
napi_get_named_property(env, object, "toString", &toStringFunc);
75-
if (napi_util::is_of_type(env, toStringFunc, napi_function)) {
76-
napi_value result;
77-
napi_call_function(env, object, toStringFunc, 0, nullptr, &result);
78-
auto value = napi_util::get_cxx_string(env, result);
138+
if (napi_get_named_property(env, object, "toString", &toStringFunc) !=
139+
napi_ok) {
140+
throwPendingException(env, "Error reading console object toString");
141+
return "[object Object]";
142+
}
143+
144+
if (toStringFunc != nullptr &&
145+
napi_util::is_of_type(env, toStringFunc, napi_function)) {
146+
napi_value result = nullptr;
147+
if (napi_call_function(env, object, toStringFunc, 0, nullptr, &result) !=
148+
napi_ok ||
149+
result == nullptr) {
150+
throwPendingException(env, "Error converting console object to string");
151+
return "[object Object]";
152+
}
153+
154+
std::string value;
155+
if (!coerceToString(env, result, value)) {
156+
return "[object Object]";
157+
}
158+
79159
auto hasCustomToStringImplementation =
80160
value.find("[object Object]") == std::string::npos;
81161
if (hasCustomToStringImplementation) return value;
@@ -88,29 +168,48 @@ std::string transformJSObject(napi_env env, napi_value object) {
88168
std::string buildStringFromArg(napi_env env, napi_value val,
89169
napi_value inspectSymbol) {
90170
napi_valuetype type;
91-
napi_typeof(env, val, &type);
171+
if (napi_typeof(env, val, &type) != napi_ok) {
172+
throwPendingException(env, "Error reading console argument type");
173+
return "<unknown>";
174+
}
92175

93176
if (type == napi_function) {
94-
napi_value funcString;
95-
napi_coerce_to_string(env, val, &funcString);
96-
return napi_util::get_string_value(env, funcString);
177+
std::string funcString;
178+
if (coerceToString(env, val, funcString)) {
179+
return funcString;
180+
}
181+
return "<function>";
97182
} else if (napi_util::is_array(env, val)) {
98183
napi_value cachedSelf = val;
99184

100185
// Get array length
101-
uint32_t arrayLength;
102-
napi_get_array_length(env, val, &arrayLength);
186+
uint32_t arrayLength = 0;
187+
if (napi_get_array_length(env, val, &arrayLength) != napi_ok) {
188+
throwPendingException(env, "Error reading console array length");
189+
return "[]";
190+
}
103191

104192
std::stringstream arrayStr;
105193
arrayStr << "[";
106194

107195
for (uint32_t i = 0; i < arrayLength; i++) {
108-
napi_value propertyValue;
109-
napi_get_element(env, val, i, &propertyValue);
196+
napi_value propertyValue = nullptr;
197+
if (napi_get_element(env, val, i, &propertyValue) != napi_ok ||
198+
propertyValue == nullptr) {
199+
throwPendingException(env, "Error reading console array element");
200+
arrayStr << "<unknown>";
201+
if (i != arrayLength - 1) {
202+
arrayStr << ", ";
203+
}
204+
continue;
205+
}
110206

111207
// Check for circular reference
112208
bool isStrictEqual = false;
113-
napi_strict_equals(env, propertyValue, cachedSelf, &isStrictEqual);
209+
if (napi_strict_equals(env, propertyValue, cachedSelf, &isStrictEqual) !=
210+
napi_ok) {
211+
throwPendingException(env, "Error comparing console array element");
212+
}
114213

115214
if (isStrictEqual) {
116215
arrayStr << "[Circular]";
@@ -132,20 +231,32 @@ std::string buildStringFromArg(napi_env env, napi_value val,
132231
napi_status getInspectStatus =
133232
napi_get_property(env, val, inspectSymbol, &inspectFunc);
134233
if (getInspectStatus == napi_ok &&
234+
inspectFunc != nullptr &&
135235
napi_util::is_of_type(env, inspectFunc, napi_function)) {
136-
napi_value inspectedValue;
137-
napi_call_function(env, val, inspectFunc, 0, nullptr, &inspectedValue);
236+
napi_value inspectedValue = nullptr;
237+
if (napi_call_function(env, val, inspectFunc, 0, nullptr,
238+
&inspectedValue) != napi_ok ||
239+
inspectedValue == nullptr) {
240+
throwPendingException(env, "Error inspecting console object");
241+
return "[object Object]";
242+
}
138243
return buildStringFromArg(env, inspectedValue, inspectSymbol);
244+
} else if (getInspectStatus != napi_ok) {
245+
throwPendingException(env, "Error reading console inspect function");
139246
}
140247
return transformJSObject(env, val);
141248
} else if (type == napi_symbol) {
142-
napi_value symString;
143-
napi_coerce_to_string(env, val, &symString);
144-
return "Symbol(" + napi_util::get_cxx_string(env, symString) + ")";
249+
std::string symString;
250+
if (coerceToString(env, val, symString)) {
251+
return symString;
252+
}
253+
return "Symbol()";
145254
} else {
146-
napi_value defaultToString;
147-
napi_coerce_to_string(env, val, &defaultToString);
148-
return napi_util::get_string_value(env, defaultToString);
255+
std::string defaultToString;
256+
if (coerceToString(env, val, defaultToString)) {
257+
return defaultToString;
258+
}
259+
return "<unknown>";
149260
}
150261
}
151262

0 commit comments

Comments
 (0)