Branch data Line data Source code
1 : : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 : : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
3 : : // SPDX-FileCopyrightText: 2008 litl, LLC
4 : : // SPDX-FileCopyrightText: 2009 Red Hat, Inc.
5 : :
6 : : #include <config.h>
7 : :
8 : : #include <string>
9 : :
10 : : #include <glib.h>
11 : :
12 : : #include <js/CallArgs.h>
13 : : #include <js/CharacterEncoding.h> // for JS_EncodeStringToUTF8
14 : : #include <js/Conversions.h>
15 : : #include <js/Exception.h>
16 : : #include <js/PropertyAndElement.h> // for JS_DefineFunctions
17 : : #include <js/PropertySpec.h> // for JS_FN, JSFunctionSpec, JS_FS_END
18 : : #include <js/RootingAPI.h>
19 : : #include <js/TypeDecls.h>
20 : : #include <js/Utility.h> // for UniqueChars
21 : : #include <js/Value.h>
22 : : #include <jsapi.h> // for JS_NewPlainObject
23 : :
24 : : #include "gjs/global.h"
25 : : #include "gjs/jsapi-util.h"
26 : : #include "gjs/macros.h"
27 : : #include "modules/print.h"
28 : :
29 : : GJS_JSAPI_RETURN_CONVENTION
30 : 4 : static bool gjs_log(JSContext* cx, unsigned argc, JS::Value* vp) {
31 : 4 : JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
32 : :
33 [ - + ]: 4 : if (argc != 1) {
34 : 0 : gjs_throw(cx, "Must pass a single argument to log()");
35 : 0 : return false;
36 : : }
37 : :
38 : : /* JS::ToString might throw, in which case we will only log that the value
39 : : * could not be converted to string */
40 : 4 : JS::AutoSaveExceptionState exc_state(cx);
41 : 4 : JS::RootedString jstr(cx, JS::ToString(cx, argv[0]));
42 : 4 : exc_state.restore();
43 : :
44 [ - + ]: 4 : if (!jstr) {
45 : 0 : g_message("JS LOG: <cannot convert value to string>");
46 : 0 : return true;
47 : : }
48 : :
49 : 4 : JS::UniqueChars s(JS_EncodeStringToUTF8(cx, jstr));
50 [ - + ]: 4 : if (!s)
51 : 0 : return false;
52 : :
53 : 4 : g_message("JS LOG: %s", s.get());
54 : :
55 : 4 : argv.rval().setUndefined();
56 : 4 : return true;
57 : 4 : }
58 : :
59 : : GJS_JSAPI_RETURN_CONVENTION
60 : 70 : static bool gjs_log_error(JSContext* cx, unsigned argc, JS::Value* vp) {
61 : 70 : JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
62 : :
63 [ + + + - : 70 : if ((argc != 1 && argc != 2) || !argv[0].isObject()) {
- + - + ]
64 : 0 : gjs_throw(
65 : : cx,
66 : : "Must pass an exception and optionally a message to logError()");
67 : 0 : return false;
68 : : }
69 : :
70 : 70 : JS::RootedString jstr(cx);
71 : :
72 [ + + ]: 70 : if (argc == 2) {
73 : : /* JS::ToString might throw, in which case we will only log that the
74 : : * value could not be converted to string */
75 : 17 : JS::AutoSaveExceptionState exc_state(cx);
76 : 17 : jstr = JS::ToString(cx, argv[1]);
77 : 17 : exc_state.restore();
78 : 17 : }
79 : :
80 : 70 : gjs_log_exception_full(cx, argv[0], jstr, G_LOG_LEVEL_WARNING);
81 : :
82 : 70 : argv.rval().setUndefined();
83 : 70 : return true;
84 : 70 : }
85 : :
86 : : GJS_JSAPI_RETURN_CONVENTION
87 : 2253 : static bool gjs_print_parse_args(JSContext* cx, const JS::CallArgs& argv,
88 : : std::string* buffer) {
89 : 2253 : g_assert(buffer && "forgot out parameter");
90 : 2253 : buffer->clear();
91 [ + + ]: 4506 : for (unsigned n = 0; n < argv.length(); ++n) {
92 : : /* JS::ToString might throw, in which case we will only log that the
93 : : * value could not be converted to string */
94 : 2253 : JS::AutoSaveExceptionState exc_state(cx);
95 : 2253 : JS::RootedString jstr(cx, JS::ToString(cx, argv[n]));
96 : 2253 : exc_state.restore();
97 : :
98 [ + - ]: 2253 : if (jstr) {
99 : 2253 : JS::UniqueChars s(JS_EncodeStringToUTF8(cx, jstr));
100 [ - + ]: 2253 : if (!s)
101 : 0 : return false;
102 : :
103 : 2253 : *buffer += s.get();
104 [ - + ]: 2253 : if (n < (argv.length() - 1))
105 : 0 : *buffer += ' ';
106 [ + - ]: 2253 : } else {
107 : 0 : *buffer = "<invalid string>";
108 : 0 : return true;
109 : : }
110 [ + - + - ]: 2253 : }
111 : 2253 : return true;
112 : : }
113 : :
114 : : GJS_JSAPI_RETURN_CONVENTION
115 : 2253 : static bool gjs_print(JSContext* context, unsigned argc, JS::Value* vp) {
116 : 2253 : JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
117 : :
118 : 2253 : std::string buffer;
119 [ - + ]: 2253 : if (!gjs_print_parse_args(context, argv, &buffer))
120 : 0 : return false;
121 : :
122 : 2253 : g_print("%s\n", buffer.c_str());
123 : :
124 : 2253 : argv.rval().setUndefined();
125 : 2253 : return true;
126 : 2253 : }
127 : :
128 : : GJS_JSAPI_RETURN_CONVENTION
129 : 0 : static bool gjs_printerr(JSContext* context, unsigned argc, JS::Value* vp) {
130 : 0 : JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
131 : :
132 : 0 : std::string buffer;
133 [ # # ]: 0 : if (!gjs_print_parse_args(context, argv, &buffer))
134 : 0 : return false;
135 : :
136 : 0 : g_printerr("%s\n", buffer.c_str());
137 : :
138 : 0 : argv.rval().setUndefined();
139 : 0 : return true;
140 : 0 : }
141 : :
142 : : // The pretty-print functionality is best written in JS, but needs to be used
143 : : // from C++ code. This stores the prettyPrint() function in a slot on the global
144 : : // object so that it can be used internally by the Console module.
145 : : // This function is not available to user code.
146 : : GJS_JSAPI_RETURN_CONVENTION
147 : 255 : static bool set_pretty_print_function(JSContext*, unsigned argc,
148 : : JS::Value* vp) {
149 : 255 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
150 : :
151 : : // can only be called internally, so OK to assert correct arguments
152 : 255 : g_assert(args.length() == 2 && "setPrettyPrintFunction takes 2 arguments");
153 : :
154 : 255 : JS::Value v_global = args[0];
155 : 255 : JS::Value v_func = args[1];
156 : :
157 : 255 : g_assert(v_global.isObject() && "first argument must be an object");
158 : 255 : g_assert(v_func.isObject() && "second argument must be an object");
159 : :
160 : 255 : gjs_set_global_slot(&v_global.toObject(), GjsGlobalSlot::PRETTY_PRINT_FUNC,
161 : : v_func);
162 : :
163 : 255 : args.rval().setUndefined();
164 : 255 : return true;
165 : : }
166 : :
167 : : GJS_JSAPI_RETURN_CONVENTION
168 : 15 : static bool get_pretty_print_function(JSContext*, unsigned argc,
169 : : JS::Value* vp) {
170 : 15 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
171 : :
172 : 15 : g_assert(args.length() == 1 && "getPrettyPrintFunction takes 1 arguments");
173 : :
174 : 15 : JS::Value v_global = args[0];
175 : :
176 : 15 : g_assert(v_global.isObject() && "argument must be an object");
177 : :
178 : 15 : JS::Value pretty_print = gjs_get_global_slot(
179 : 15 : &v_global.toObject(), GjsGlobalSlot::PRETTY_PRINT_FUNC);
180 : :
181 : 15 : args.rval().set(pretty_print);
182 : 15 : return true;
183 : : }
184 : :
185 : : // clang-format off
186 : : static constexpr JSFunctionSpec funcs[] = {
187 : : JS_FN("log", gjs_log, 1, GJS_MODULE_PROP_FLAGS),
188 : : JS_FN("logError", gjs_log_error, 2, GJS_MODULE_PROP_FLAGS),
189 : : JS_FN("print", gjs_print, 0, GJS_MODULE_PROP_FLAGS),
190 : : JS_FN("printerr", gjs_printerr, 0, GJS_MODULE_PROP_FLAGS),
191 : : JS_FN("setPrettyPrintFunction", set_pretty_print_function, 1, GJS_MODULE_PROP_FLAGS),
192 : : JS_FN("getPrettyPrintFunction", get_pretty_print_function, 1, GJS_MODULE_PROP_FLAGS),
193 : : JS_FS_END};
194 : : // clang-format on
195 : :
196 : 279 : bool gjs_define_print_stuff(JSContext* context,
197 : : JS::MutableHandleObject module) {
198 : 279 : module.set(JS_NewPlainObject(context));
199 [ - + ]: 279 : if (!module)
200 : 0 : return false;
201 : 279 : return JS_DefineFunctions(context, module, funcs);
202 : : }
|