1/*
2 * Copyright (C) 2018 Igalia S.L.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2,1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21
22// Include JSCContextPrivate.h to be able to run garbage collector for testing.
23#define JSC_COMPILATION 1
24#include "jsc/JSCContextPrivate.h"
25#undef JSC_COMPILATION
26
27#include <jsc/jsc.h>
28#include <wtf/HashSet.h>
29#include <wtf/Threading.h>
30#include <wtf/Vector.h>
31#include <wtf/glib/GRefPtr.h>
32#include <wtf/glib/GUniquePtr.h>
33
34class LeakChecker {
35public:
36 LeakChecker() = default;
37
38 ~LeakChecker()
39 {
40 if (m_watchedObjects.isEmpty())
41 return;
42
43 g_print("Leaked objects:");
44 for (auto* object : m_watchedObjects)
45 g_print(" [%s(%p) %u refs]", g_type_name_from_instance(reinterpret_cast<GTypeInstance*>(object)), object, G_OBJECT(object)->ref_count);
46 g_print("\n");
47 g_assert_true(m_watchedObjects.isEmpty());
48 }
49
50 void watch(void* object)
51 {
52 g_assert_true(G_IS_OBJECT(object));
53 m_watchedObjects.add(object);
54 g_object_weak_ref(G_OBJECT(object), [](gpointer userData, GObject* object) {
55 static_cast<LeakChecker*>(userData)->m_watchedObjects.remove(object);
56 }, this);
57 }
58
59private:
60 HashSet<void*> m_watchedObjects;
61};
62
63class ExceptionHandler {
64public:
65 ExceptionHandler(JSCContext* context)
66 : m_context(context)
67 {
68 jsc_context_push_exception_handler(m_context, [](JSCContext*, JSCException* exception, gpointer) {
69 g_error("UNEXPECTED EXCEPTION: %s", jsc_exception_get_message(exception));
70 }, nullptr, nullptr);
71 }
72
73 ~ExceptionHandler()
74 {
75 pop();
76 }
77
78 void push(JSCExceptionHandler handler, gpointer userData, GDestroyNotify destroyFunction = nullptr)
79 {
80 jsc_context_push_exception_handler(m_context, handler, userData, destroyFunction);
81 }
82
83 void pop()
84 {
85 jsc_context_pop_exception_handler(m_context);
86 }
87
88private:
89 JSCContext* m_context;
90};
91
92#define g_assert_throw_begin(handler, didThrow) \
93 didThrow = false; \
94 handler.push([](JSCContext*, JSCException*, gpointer userData) { \
95 *static_cast<bool*>(userData) = true; \
96 }, &didThrow, nullptr);
97
98#define g_assert_did_throw(handler, didThrow) \
99 handler.pop(); \
100 g_assert_true(didThrow); \
101 didThrow = false;
102
103extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
104
105static void jscContextGarbageCollect(JSCContext* context)
106{
107 JSSynchronousGarbageCollectForDebugging(jscContextGetJSContext(context));
108}
109
110static void testJSCBasic()
111{
112 {
113 LeakChecker checker;
114 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
115 checker.watch(context.get());
116 ExceptionHandler exceptionHandler(context.get());
117
118 auto* defaultVM = jsc_context_get_virtual_machine(context.get());
119 g_assert_nonnull(defaultVM);
120 checker.watch(defaultVM);
121
122 GRefPtr<JSCVirtualMachine> vm = adoptGRef(jsc_virtual_machine_new());
123 checker.watch(vm.get());
124 g_assert_false(vm.get() == defaultVM);
125
126 GRefPtr<JSCContext> context2 = adoptGRef(jsc_context_new_with_virtual_machine(vm.get()));
127 checker.watch(context2.get());
128 ExceptionHandler exceptionHandler2(context.get());
129 g_assert_true(jsc_context_get_virtual_machine(context2.get()) == vm.get());
130
131 GRefPtr<JSCContext> context3 = adoptGRef(jsc_context_new_with_virtual_machine(defaultVM));
132 checker.watch(context3.get());
133 ExceptionHandler exceptionHandler3(context.get());
134 g_assert_true(jsc_context_get_virtual_machine(context3.get()) == jsc_context_get_virtual_machine(context.get()));
135
136 GRefPtr<JSCValue> value1 = adoptGRef(jsc_value_new_number(context.get(), 25));
137 checker.watch(value1.get());
138 g_assert_true(jsc_value_get_context(value1.get()) == context.get());
139 g_assert_true(jsc_value_is_number(value1.get()));
140 g_assert_cmpint(jsc_value_to_int32(value1.get()), ==, 25);
141 jsc_context_set_value(context.get(), "value1", value1.get());
142
143 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "value1", -1));
144 checker.watch(result.get());
145 g_assert_true(result.get() == value1.get());
146
147 result = adoptGRef(jsc_context_evaluate(context2.get(), "value1", -1));
148 checker.watch(result.get());
149 g_assert_true(jsc_value_get_context(result.get()) == context2.get());
150 g_assert_true(jsc_value_is_undefined(result.get()));
151
152 result = adoptGRef(jsc_context_get_value(context3.get(), "value1"));
153 checker.watch(result.get());
154 g_assert_true(jsc_value_get_context(result.get()) == context3.get());
155 g_assert_true(jsc_value_is_undefined(result.get()));
156
157 jsc_context_set_value(context3.get(), "value1", value1.get());
158 result = adoptGRef(jsc_context_evaluate(context3.get(), "value1", -1));
159 checker.watch(result.get());
160 g_assert_true(jsc_value_get_context(result.get()) == context3.get());
161 g_assert_false(result.get() == value1.get());
162 g_assert_true(jsc_value_is_number(result.get()));
163 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 25);
164 }
165
166 {
167 LeakChecker checker;
168 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
169 checker.watch(context.get());
170 ExceptionHandler exceptionHandler(context.get());
171
172 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "2 + 2", -1));
173 checker.watch(result.get());
174 g_assert_true(jsc_value_get_context(result.get()) == context.get());
175 g_assert_true(jsc_value_is_number(result.get()));
176 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 4);
177
178 GRefPtr<JSCValue> result2 = adoptGRef(jsc_context_evaluate(context.get(), "2 + 2", -1));
179 checker.watch(result2.get());
180 g_assert_true(jsc_value_get_context(result2.get()) == context.get());
181 g_assert_true(result.get() == result2.get());
182
183 GRefPtr<JSCValue> result3 = adoptGRef(jsc_context_evaluate(context.get(), "3 + 1", -1));
184 checker.watch(result3.get());
185 g_assert_true(jsc_value_get_context(result3.get()) == context.get());
186 g_assert_true(result.get() == result3.get());
187
188 auto* resultPtr = result.get();
189 result = nullptr;
190 result2 = nullptr;
191 result3 = nullptr;
192 GRefPtr<JSCValue> result4 = adoptGRef(jsc_context_evaluate(context.get(), "2 + 2", -1));
193 checker.watch(result4.get());
194 g_assert_true(jsc_value_get_context(result4.get()) == context.get());
195 g_assert_true(jsc_value_is_number(result4.get()));
196 g_assert_cmpint(jsc_value_to_int32(result4.get()), ==, 4);
197 g_assert_false(result4.get() == resultPtr);
198 }
199
200 {
201 LeakChecker checker;
202 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
203 checker.watch(context.get());
204 ExceptionHandler exceptionHandler(context.get());
205
206 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "2 + 2", -1));
207 checker.watch(result.get());
208 g_assert_true(jsc_value_get_context(result.get()) == context.get());
209 jsc_context_set_value(context.get(), "four", result.get());
210
211 GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "four"));
212 checker.watch(value.get());
213 g_assert_true(jsc_value_get_context(value.get()) == context.get());
214 g_assert_true(jsc_value_is_number(value.get()));
215 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 4);
216 g_assert_true(result.get() == value.get());
217
218 value = adoptGRef(jsc_context_evaluate(context.get(), "four", -1));
219 checker.watch(value.get());
220 g_assert_true(result.get() == value.get());
221 }
222
223 {
224 LeakChecker checker;
225 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
226 checker.watch(context.get());
227 ExceptionHandler exceptionHandler(context.get());
228
229 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "two = 2; four = two + two", -1));
230 checker.watch(result.get());
231 g_assert_true(jsc_value_get_context(result.get()) == context.get());
232 g_assert_true(jsc_value_is_number(result.get()));
233 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 4);
234
235 GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "two"));
236 checker.watch(value.get());
237 g_assert_true(jsc_value_get_context(value.get()) == context.get());
238 g_assert_true(jsc_value_is_number(value.get()));
239 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 2);
240
241 value = adoptGRef(jsc_context_get_value(context.get(), "four"));
242 checker.watch(value.get());
243 g_assert_true(result.get() == value.get());
244
245 GRefPtr<JSCValue> result2 = adoptGRef(jsc_context_evaluate(context.get(), "five = 4", -1));
246 checker.watch(result2.get());
247 g_assert_true(result2.get() == value.get());
248 }
249
250 {
251 LeakChecker checker;
252 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
253 checker.watch(context.get());
254 ExceptionHandler exceptionHandler(context.get());
255
256 GUniquePtr<char> scriptFile(g_build_filename(WEBKIT_SRC_DIR, "Tools", "TestWebKitAPI", "Tests", "JavaScriptCore", "glib", "script.js", nullptr));
257 GUniqueOutPtr<char> contents;
258 gsize contentsSize;
259 g_assert_true(g_file_get_contents(scriptFile.get(), &contents.outPtr(), &contentsSize, nullptr));
260 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), contents.get(), contentsSize));
261 checker.watch(result.get());
262 g_assert_true(jsc_value_is_undefined(result.get()));
263
264 GString* expected = g_string_new("String");
265 expected = g_string_append_c(expected, '\0');
266 expected = g_string_append(expected, "With");
267 expected = g_string_append_c(expected, '\0');
268 expected = g_string_append(expected, "Null");
269 GRefPtr<GBytes> expectedBytes = adoptGRef(g_string_free_to_bytes(expected));
270
271 GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "testStringWithNull()", -1));
272 checker.watch(value.get());
273 g_assert_true(jsc_value_is_string(value.get()));
274 GUniquePtr<char> valueString(jsc_value_to_string(value.get()));
275 g_assert_cmpstr(valueString.get(), ==, "String");
276 GRefPtr<GBytes> valueBytes = adoptGRef(jsc_value_to_string_as_bytes(value.get()));
277 g_assert_true(g_bytes_equal(valueBytes.get(), expectedBytes.get()));
278
279 value = adoptGRef(jsc_value_new_string_from_bytes(context.get(), expectedBytes.get()));
280 checker.watch(value.get());
281 g_assert_true(jsc_value_is_string(value.get()));
282 valueString.reset(jsc_value_to_string(value.get()));
283 g_assert_cmpstr(valueString.get(), ==, "String");
284 valueBytes = adoptGRef(jsc_value_to_string_as_bytes(value.get()));
285 g_assert_true(g_bytes_equal(valueBytes.get(), expectedBytes.get()));
286
287 jsc_context_set_value(context.get(), "s", value.get());
288 result = adoptGRef(jsc_context_get_value(context.get(), "s"));
289 checker.watch(result.get());
290 g_assert_true(result.get() == value.get());
291 }
292}
293
294static void testJSCTypes()
295{
296 LeakChecker checker;
297 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
298 checker.watch(context.get());
299 ExceptionHandler exceptionHandler(context.get());
300
301 GRefPtr<JSCValue> value = adoptGRef(jsc_value_new_number(context.get(), 125));
302 checker.watch(value.get());
303 g_assert_true(jsc_value_is_number(value.get()));
304 g_assert_cmpfloat(jsc_value_to_double(value.get()), ==, 125.);
305 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 125);
306 g_assert_true(jsc_value_to_boolean(value.get()) == TRUE);
307 GUniquePtr<char> valueString(jsc_value_to_string(value.get()));
308 g_assert_cmpstr(valueString.get(), ==, "125");
309 jsc_context_set_value(context.get(), "i", value.get());
310 GRefPtr<JSCValue> result = adoptGRef(jsc_context_get_value(context.get(), "i"));
311 checker.watch(result.get());
312 g_assert_true(result.get() == value.get());
313
314 value = adoptGRef(jsc_value_new_number(context.get(), 12.5));
315 checker.watch(value.get());
316 g_assert_true(jsc_value_is_number(value.get()));
317 g_assert_cmpfloat(jsc_value_to_double(value.get()), ==, 12.5);
318 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 12);
319 g_assert_true(jsc_value_to_boolean(value.get()) == TRUE);
320 valueString.reset(jsc_value_to_string(value.get()));
321 g_assert_cmpstr(valueString.get(), ==, "12.5");
322 jsc_context_set_value(context.get(), "d", value.get());
323 result = adoptGRef(jsc_context_get_value(context.get(), "d"));
324 checker.watch(result.get());
325 g_assert_true(result.get() == value.get());
326
327 value = adoptGRef(jsc_value_new_boolean(context.get(), TRUE));
328 checker.watch(value.get());
329 g_assert_true(jsc_value_is_boolean(value.get()));
330 g_assert_true(jsc_value_to_boolean(value.get()) == TRUE);
331 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 1);
332 valueString.reset(jsc_value_to_string(value.get()));
333 g_assert_cmpstr(valueString.get(), ==, "true");
334 jsc_context_set_value(context.get(), "b1", value.get());
335 result = adoptGRef(jsc_context_get_value(context.get(), "b1"));
336 checker.watch(result.get());
337 g_assert_true(result.get() == value.get());
338
339 value = adoptGRef(jsc_value_new_boolean(context.get(), FALSE));
340 checker.watch(value.get());
341 g_assert_true(jsc_value_is_boolean(value.get()));
342 g_assert_true(jsc_value_to_boolean(value.get()) == FALSE);
343 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
344 valueString.reset(jsc_value_to_string(value.get()));
345 g_assert_cmpstr(valueString.get(), ==, "false");
346 jsc_context_set_value(context.get(), "b2", value.get());
347 result = adoptGRef(jsc_context_get_value(context.get(), "b2"));
348 checker.watch(result.get());
349 g_assert_true(result.get() == value.get());
350
351 value = adoptGRef(jsc_value_new_string(context.get(), "Hello World"));
352 checker.watch(value.get());
353 g_assert_true(jsc_value_is_string(value.get()));
354 valueString.reset(jsc_value_to_string(value.get()));
355 g_assert_cmpstr(valueString.get(), ==, "Hello World");
356 g_assert_true(jsc_value_to_boolean(value.get()) == TRUE);
357 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
358 jsc_context_set_value(context.get(), "s1", value.get());
359 result = adoptGRef(jsc_context_get_value(context.get(), "s1"));
360 checker.watch(result.get());
361 g_assert_true(result.get() == value.get());
362
363 value = adoptGRef(jsc_value_new_string(context.get(), nullptr));
364 checker.watch(value.get());
365 g_assert_true(jsc_value_is_string(value.get()));
366 valueString.reset(jsc_value_to_string(value.get()));
367 g_assert_cmpstr(valueString.get(), ==, "");
368 g_assert_true(jsc_value_to_boolean(value.get()) == FALSE);
369 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
370 jsc_context_set_value(context.get(), "s2", value.get());
371 result = adoptGRef(jsc_context_get_value(context.get(), "s2"));
372 checker.watch(result.get());
373 g_assert_true(result.get() == value.get());
374
375 value = adoptGRef(jsc_value_new_string(context.get(), "12.5"));
376 checker.watch(value.get());
377 g_assert_true(jsc_value_is_string(value.get()));
378 valueString.reset(jsc_value_to_string(value.get()));
379 g_assert_cmpstr(valueString.get(), ==, "12.5");
380 g_assert_true(jsc_value_to_boolean(value.get()) == TRUE);
381 g_assert_cmpfloat(jsc_value_to_double(value.get()), ==, 12.5);
382 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 12);
383 jsc_context_set_value(context.get(), "s3", value.get());
384 result = adoptGRef(jsc_context_get_value(context.get(), "s3"));
385 checker.watch(result.get());
386 g_assert_true(result.get() == value.get());
387
388 value = adoptGRef(jsc_value_new_array(context.get(), G_TYPE_NONE));
389 checker.watch(value.get());
390 g_assert_true(jsc_value_is_array(value.get()));
391 g_assert_true(jsc_value_is_object(value.get()));
392 g_assert_true(jsc_value_to_boolean(value.get()) == TRUE);
393 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
394 valueString.reset(jsc_value_to_string(value.get()));
395 g_assert_cmpstr(valueString.get(), ==, "");
396 jsc_context_set_value(context.get(), "a1", value.get());
397 result = adoptGRef(jsc_context_get_value(context.get(), "a1"));
398 checker.watch(result.get());
399 g_assert_true(result.get() == value.get());
400 GRefPtr<JSCValue> arrayLength = adoptGRef(jsc_value_object_get_property(value.get(), "length"));
401 checker.watch(arrayLength.get());
402 g_assert_true(jsc_value_is_number(arrayLength.get()));
403 g_assert_cmpint(jsc_value_to_int32(arrayLength.get()), ==, 0);
404 GRefPtr<JSCValue> arrayItem = adoptGRef(jsc_value_new_number(context.get(), 1));
405 checker.watch(arrayItem.get());
406 jsc_value_object_set_property_at_index(value.get(), jsc_value_to_int32(arrayLength.get()), arrayItem.get());
407 arrayLength = adoptGRef(jsc_value_object_get_property(value.get(), "length"));
408 checker.watch(arrayLength.get());
409 g_assert_true(jsc_value_is_number(arrayLength.get()));
410 g_assert_cmpint(jsc_value_to_int32(arrayLength.get()), ==, 1);
411 valueString.reset(jsc_value_to_string(value.get()));
412 g_assert_cmpstr(valueString.get(), ==, "1");
413 arrayItem = adoptGRef(jsc_value_new_number(context.get(), 5));
414 checker.watch(arrayItem.get());
415 jsc_value_object_set_property_at_index(value.get(), jsc_value_to_int32(arrayLength.get()), arrayItem.get());
416 arrayLength = adoptGRef(jsc_value_object_get_property(value.get(), "length"));
417 checker.watch(arrayLength.get());
418 g_assert_true(jsc_value_is_number(arrayLength.get()));
419 g_assert_cmpint(jsc_value_to_int32(arrayLength.get()), ==, 2);
420 valueString.reset(jsc_value_to_string(value.get()));
421 g_assert_cmpstr(valueString.get(), ==, "1,5");
422 arrayItem = adoptGRef(jsc_value_new_number(context.get(), 10));
423 checker.watch(arrayItem.get());
424 jsc_value_object_set_property_at_index(value.get(), 3, arrayItem.get());
425 arrayLength = adoptGRef(jsc_value_object_get_property(value.get(), "length"));
426 checker.watch(arrayLength.get());
427 g_assert_true(jsc_value_is_number(arrayLength.get()));
428 g_assert_cmpint(jsc_value_to_int32(arrayLength.get()), ==, 4);
429 arrayItem = adoptGRef(jsc_value_object_get_property_at_index(value.get(), 0));
430 checker.watch(arrayItem.get());
431 g_assert_true(jsc_value_is_number(arrayItem.get()));
432 g_assert_cmpint(jsc_value_to_int32(arrayItem.get()), ==, 1);
433 arrayItem = adoptGRef(jsc_value_object_get_property_at_index(value.get(), 1));
434 checker.watch(arrayItem.get());
435 g_assert_true(jsc_value_is_number(arrayItem.get()));
436 g_assert_cmpint(jsc_value_to_int32(arrayItem.get()), ==, 5);
437 arrayItem = adoptGRef(jsc_value_object_get_property_at_index(value.get(), 2));
438 checker.watch(arrayItem.get());
439 g_assert_true(jsc_value_is_undefined(arrayItem.get()));
440 arrayItem = adoptGRef(jsc_value_object_get_property_at_index(value.get(), 3));
441 checker.watch(arrayItem.get());
442 g_assert_true(jsc_value_is_number(arrayItem.get()));
443 g_assert_cmpint(jsc_value_to_int32(arrayItem.get()), ==, 10);
444 arrayLength = adoptGRef(jsc_value_object_get_property(value.get(), "length"));
445 checker.watch(arrayLength.get());
446 arrayItem = adoptGRef(jsc_value_object_get_property_at_index(value.get(), jsc_value_to_int32(arrayLength.get()) + 1));
447 checker.watch(arrayItem.get());
448 g_assert_true(jsc_value_is_undefined(arrayItem.get()));
449
450 GRefPtr<JSCValue> array = adoptGRef(jsc_value_new_array(context.get(), G_TYPE_INT, 1, G_TYPE_STRING, "two", G_TYPE_BOOLEAN, TRUE, JSC_TYPE_VALUE, value.get(), G_TYPE_NONE));
451 checker.watch(array.get());
452 g_assert_true(jsc_value_is_array(array.get()));
453 g_assert_true(jsc_value_is_object(array.get()));
454 g_assert_true(jsc_value_to_boolean(array.get()) == TRUE);
455 g_assert_cmpint(jsc_value_to_int32(array.get()), ==, 0);
456 valueString.reset(jsc_value_to_string(array.get()));
457 g_assert_cmpstr(valueString.get(), ==, "1,two,true,1,5,,10");
458 arrayLength = adoptGRef(jsc_value_object_get_property(array.get(), "length"));
459 checker.watch(arrayLength.get());
460 g_assert_true(jsc_value_is_number(arrayLength.get()));
461 g_assert_cmpint(jsc_value_to_int32(arrayLength.get()), ==, 4);
462 arrayItem = adoptGRef(jsc_value_object_get_property_at_index(array.get(), 0));
463 checker.watch(arrayItem.get());
464 g_assert_true(jsc_value_is_number(arrayItem.get()));
465 g_assert_cmpint(jsc_value_to_int32(arrayItem.get()), ==, 1);
466 arrayItem = adoptGRef(jsc_value_object_get_property_at_index(array.get(), 1));
467 checker.watch(arrayItem.get());
468 g_assert_true(jsc_value_is_string(arrayItem.get()));
469 valueString.reset(jsc_value_to_string(arrayItem.get()));
470 g_assert_cmpstr(valueString.get(), ==, "two");
471 arrayItem = adoptGRef(jsc_value_object_get_property_at_index(array.get(), 2));
472 checker.watch(arrayItem.get());
473 g_assert_true(jsc_value_is_boolean(arrayItem.get()));
474 g_assert_true(jsc_value_to_boolean(arrayItem.get()) == TRUE);
475 arrayItem = adoptGRef(jsc_value_object_get_property_at_index(array.get(), 3));
476 checker.watch(arrayItem.get());
477 g_assert_true(arrayItem.get() == value.get());
478
479 GRefPtr<GPtrArray> gArray = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
480 g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 1));
481 g_ptr_array_add(gArray.get(), jsc_value_new_string(context.get(), "two"));
482 g_ptr_array_add(gArray.get(), jsc_value_new_boolean(context.get(), TRUE));
483 g_ptr_array_add(gArray.get(), g_object_ref(value.get()));
484 array = adoptGRef(jsc_value_new_array_from_garray(context.get(), gArray.get()));
485 checker.watch(array.get());
486 g_assert_true(jsc_value_is_array(array.get()));
487 g_assert_true(jsc_value_is_object(array.get()));
488 g_assert_true(jsc_value_to_boolean(array.get()) == TRUE);
489 g_assert_cmpint(jsc_value_to_int32(array.get()), ==, 0);
490 valueString.reset(jsc_value_to_string(array.get()));
491 g_assert_cmpstr(valueString.get(), ==, "1,two,true,1,5,,10");
492 arrayLength = adoptGRef(jsc_value_object_get_property(array.get(), "length"));
493 checker.watch(arrayLength.get());
494 g_assert_true(jsc_value_is_number(arrayLength.get()));
495 g_assert_cmpint(jsc_value_to_int32(arrayLength.get()), ==, gArray->len);
496
497 const char* strv[] = { "one", "two", "three", nullptr };
498 array = adoptGRef(jsc_value_new_array_from_strv(context.get(), strv));
499 checker.watch(array.get());
500 g_assert_true(jsc_value_is_array(array.get()));
501 g_assert_true(jsc_value_is_object(array.get()));
502 g_assert_true(jsc_value_to_boolean(array.get()) == TRUE);
503 g_assert_cmpint(jsc_value_to_int32(array.get()), ==, 0);
504 valueString.reset(jsc_value_to_string(array.get()));
505 g_assert_cmpstr(valueString.get(), ==, "one,two,three");
506 arrayLength = adoptGRef(jsc_value_object_get_property(array.get(), "length"));
507 checker.watch(arrayLength.get());
508 g_assert_true(jsc_value_is_number(arrayLength.get()));
509 g_assert_cmpint(jsc_value_to_int32(arrayLength.get()), ==, 3);
510
511 value = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr));
512 checker.watch(value.get());
513 g_assert_true(jsc_value_is_object(value.get()));
514 g_assert_true(jsc_value_to_boolean(value.get()) == TRUE);
515 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
516 valueString.reset(jsc_value_to_string(value.get()));
517 g_assert_cmpstr(valueString.get(), ==, "[object Object]");
518 jsc_context_set_value(context.get(), "o", value.get());
519 result = adoptGRef(jsc_context_get_value(context.get(), "o"));
520 checker.watch(result.get());
521 g_assert_true(result.get() == value.get());
522
523 value = adoptGRef(jsc_value_new_undefined(context.get()));
524 checker.watch(value.get());
525 g_assert_true(jsc_value_is_undefined(value.get()));
526 g_assert_true(jsc_value_to_boolean(value.get()) == FALSE);
527 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
528 valueString.reset(jsc_value_to_string(value.get()));
529 g_assert_cmpstr(valueString.get(), ==, "undefined");
530 jsc_context_set_value(context.get(), "u", value.get());
531 result = adoptGRef(jsc_context_get_value(context.get(), "u"));
532 checker.watch(result.get());
533 g_assert_true(result.get() == value.get());
534
535 value = adoptGRef(jsc_value_new_null(context.get()));
536 checker.watch(value.get());
537 g_assert_true(jsc_value_is_null(value.get()));
538 g_assert_true(jsc_value_to_boolean(value.get()) == FALSE);
539 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
540 valueString.reset(jsc_value_to_string(value.get()));
541 g_assert_cmpstr(valueString.get(), ==, "null");
542 jsc_context_set_value(context.get(), "n", value.get());
543 result = adoptGRef(jsc_context_get_value(context.get(), "n"));
544 checker.watch(result.get());
545 g_assert_true(result.get() == value.get());
546}
547
548static void testJSCGlobalObject()
549{
550 LeakChecker checker;
551 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
552 checker.watch(context.get());
553 ExceptionHandler exceptionHandler(context.get());
554
555 GRefPtr<JSCValue> globalObject = adoptGRef(jsc_context_get_global_object(context.get()));
556 checker.watch(globalObject.get());
557 g_assert_true(jsc_value_is_object(globalObject.get()));
558
559 GRefPtr<JSCValue> foo = adoptGRef(jsc_value_new_number(context.get(), 25));
560 checker.watch(foo.get());
561 jsc_value_object_set_property(globalObject.get(), "foo", foo.get());
562
563 GRefPtr<JSCValue> foo2 = adoptGRef(jsc_context_get_value(context.get(), "foo"));
564 checker.watch(foo2.get());
565 g_assert_true(foo.get() == foo2.get());
566
567 GRefPtr<JSCValue> bar = adoptGRef(jsc_value_new_number(context.get(), 50));
568 checker.watch(bar.get());
569 jsc_context_set_value(context.get(), "bar", bar.get());
570
571 GRefPtr<JSCValue> bar2 = adoptGRef(jsc_value_object_get_property(globalObject.get(), "bar"));
572 checker.watch(bar2.get());
573 g_assert_true(bar.get() == bar2.get());
574
575 GRefPtr<JSCValue> baz = adoptGRef(jsc_context_evaluate(context.get(), "baz = 75", -1));
576 checker.watch(baz.get());
577
578 GRefPtr<JSCValue> baz2 = adoptGRef(jsc_value_object_get_property(globalObject.get(), "baz"));
579 checker.watch(baz2.get());
580 g_assert_true(baz.get() == baz2.get());
581
582 jsc_context_set_value(context.get(), "window", globalObject.get());
583 GRefPtr<JSCValue> window = adoptGRef(jsc_context_evaluate(context.get(), "window", -1));
584 checker.watch(window.get());
585 g_assert_true(window.get() == globalObject.get());
586
587 foo2 = adoptGRef(jsc_context_evaluate(context.get(), "window.foo", -1));
588 checker.watch(foo2.get());
589 g_assert_true(foo.get() == foo2.get());
590
591 GRefPtr<JSCValue> global = adoptGRef(jsc_context_evaluate(context.get(), "window.global = 100", -1));
592 checker.watch(global.get());
593 g_assert_true(jsc_value_is_number(global.get()));
594 g_assert_cmpint(jsc_value_to_int32(global.get()), ==, 100);
595
596 GRefPtr<JSCValue> global2 = adoptGRef(jsc_context_get_value(context.get(), "global"));
597 checker.watch(global2.get());
598 g_assert_true(global.get() == global2.get());
599
600 global2 = adoptGRef(jsc_value_object_get_property(globalObject.get(), "global"));
601 checker.watch(global2.get());
602 g_assert_true(global.get() == global2.get());
603
604 jsc_value_object_define_property_data(globalObject.get(), "window2", static_cast<JSCValuePropertyFlags>(0), globalObject.get());
605 GRefPtr<JSCValue> window2 = adoptGRef(jsc_context_evaluate(context.get(), "window2", -1));
606 checker.watch(window2.get());
607 g_assert_true(window2.get() == globalObject.get());
608}
609
610typedef struct {
611 const char* name;
612 bool wasDeleted;
613} Module;
614
615static JSCClassVTable moduleVTable = {
616 // get_property
617 [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name) -> JSCValue* {
618 auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
619 checker->watch(context);
620
621 if (g_strcmp0(name, "name"))
622 return nullptr;
623
624 auto* module = static_cast<Module*>(instance);
625 auto* returnValue = jsc_value_new_string(context, module->name);
626 checker->watch(returnValue);
627 return returnValue;
628 },
629 // set_property
630 nullptr,
631 // has_property
632 nullptr,
633 // delete_property
634 nullptr,
635 // enumerate_properties
636 nullptr,
637 // padding
638 nullptr, nullptr, nullptr, nullptr
639};
640
641static void testJSCEvaluateInObject()
642{
643 Module moduleObject = { "ModuleWithClass", false };
644 {
645 LeakChecker checker;
646 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
647 checker.watch(context.get());
648 ExceptionHandler exceptionHandler(context.get());
649
650 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "g = 54; function foo() { return 42; }", -1));
651 checker.watch(result.get());
652
653 GRefPtr<JSCValue> globalObject = adoptGRef(jsc_context_get_global_object(context.get()));
654 checker.watch(globalObject.get());
655
656 GRefPtr<JSCValue> rootFoo = adoptGRef(jsc_value_object_get_property(globalObject.get(), "foo"));
657 checker.watch(rootFoo.get());
658 g_assert_true(jsc_value_is_function(rootFoo.get()));
659 result = adoptGRef(jsc_value_function_call(rootFoo.get(), G_TYPE_NONE));
660 checker.watch(result.get());
661 g_assert_true(jsc_value_is_number(result.get()));
662 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 42);
663 GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "foo()", -1));
664 checker.watch(value.get());
665 g_assert_true(value.get() == result.get());
666
667 GRefPtr<JSCValue> module;
668 result = adoptGRef(jsc_context_evaluate_in_object(context.get(), "function bar() { return g; }", -1, nullptr, nullptr, nullptr, 1, &module.outPtr()));
669 checker.watch(result.get());
670 checker.watch(module.get());
671 g_assert_true(JSC_IS_VALUE(module.get()));
672 g_assert_true(jsc_value_is_object(module.get()));
673 GUniquePtr<char> valueString(jsc_value_to_string(module.get()));
674 g_assert_cmpstr(valueString.get(), ==, "[object GlobalObject]");
675 jsc_context_set_value(context.get(), "module", module.get());
676
677 GRefPtr<JSCValue> bar = adoptGRef(jsc_value_object_get_property(module.get(), "bar"));
678 checker.watch(bar.get());
679 g_assert_true(jsc_value_is_function(bar.get()));
680 result = adoptGRef(jsc_value_function_call(bar.get(), G_TYPE_NONE));
681 checker.watch(result.get());
682 g_assert_true(jsc_value_is_number(result.get()));
683 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 54);
684 value = adoptGRef(jsc_context_evaluate(context.get(), "module.bar()", -1));
685 checker.watch(value.get());
686 g_assert_true(value.get() == result.get());
687
688 bar = adoptGRef(jsc_context_get_value(context.get(), "bar"));
689 checker.watch(bar.get());
690 g_assert_true(jsc_value_is_undefined(bar.get()));
691
692 JSCClass* jscClass = jsc_context_register_class(context.get(), "Module", nullptr, &moduleVTable, [](gpointer module) {
693 static_cast<Module*>(module)->wasDeleted = true;
694 });
695 checker.watch(jscClass);
696 g_object_set_data(G_OBJECT(jscClass), "leak-checker", &checker);
697 GRefPtr<JSCValue> moduleWithClass;
698 result = adoptGRef(jsc_context_evaluate_in_object(context.get(), "function baz() { return foo(); }", -1, &moduleObject, jscClass, nullptr, 1, &moduleWithClass.outPtr()));
699 checker.watch(result.get());
700 checker.watch(moduleWithClass.get());
701 g_assert_true(JSC_IS_VALUE(moduleWithClass.get()));
702 g_assert_true(jsc_value_is_object(moduleWithClass.get()));
703 valueString.reset(jsc_value_to_string(moduleWithClass.get()));
704 g_assert_cmpstr(valueString.get(), ==, "[object Module]");
705 jsc_context_set_value(context.get(), "moduleWithClass", moduleWithClass.get());
706
707 GRefPtr<JSCValue> name = adoptGRef(jsc_value_object_get_property(moduleWithClass.get(), "name"));
708 checker.watch(name.get());
709 g_assert_true(jsc_value_is_string(name.get()));
710 valueString.reset(jsc_value_to_string(name.get()));
711 g_assert_cmpstr(valueString.get(), ==, "ModuleWithClass");
712
713 GRefPtr<JSCValue> baz = adoptGRef(jsc_value_object_get_property(moduleWithClass.get(), "baz"));
714 checker.watch(baz.get());
715 g_assert_true(jsc_value_is_function(baz.get()));
716 result = adoptGRef(jsc_value_function_call(baz.get(), G_TYPE_NONE));
717 checker.watch(result.get());
718 g_assert_true(jsc_value_is_number(result.get()));
719 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 42);
720 value = adoptGRef(jsc_context_evaluate(context.get(), "moduleWithClass.baz()", -1));
721 checker.watch(value.get());
722 g_assert_true(value.get() == result.get());
723
724 bar = adoptGRef(jsc_value_object_get_property(moduleWithClass.get(), "bar"));
725 checker.watch(bar.get());
726 g_assert_true(jsc_value_is_undefined(bar.get()));
727
728 baz = adoptGRef(jsc_value_object_get_property(module.get(), "baz"));
729 checker.watch(baz.get());
730 g_assert_true(jsc_value_is_undefined(baz.get()));
731
732 baz = adoptGRef(jsc_context_get_value(context.get(), "baz"));
733 checker.watch(baz.get());
734 g_assert_true(jsc_value_is_undefined(baz.get()));
735
736 GRefPtr<JSCValue> jsNamespace = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr));
737 checker.watch(jsNamespace.get());
738 jsc_context_set_value(context.get(), "wk", jsNamespace.get());
739
740 GRefPtr<JSCValue> moduleInWk;
741 result = adoptGRef(jsc_context_evaluate_in_object(context.get(), "function bar() { return g; }", -1, nullptr, nullptr, nullptr, 1, &moduleInWk.outPtr()));
742 checker.watch(result.get());
743 checker.watch(moduleInWk.get());
744 g_assert_true(JSC_IS_VALUE(moduleInWk.get()));
745 g_assert_true(jsc_value_is_object(moduleInWk.get()));
746 jsc_value_object_set_property(jsNamespace.get(), "moduleInWk", moduleInWk.get());
747
748 bar = adoptGRef(jsc_value_object_get_property(moduleInWk.get(), "bar"));
749 checker.watch(bar.get());
750 g_assert_true(jsc_value_is_function(bar.get()));
751 result = adoptGRef(jsc_value_function_call(bar.get(), G_TYPE_NONE));
752 checker.watch(result.get());
753 g_assert_true(jsc_value_is_number(result.get()));
754 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 54);
755 value = adoptGRef(jsc_context_evaluate(context.get(), "wk.moduleInWk.bar()", -1));
756 checker.watch(value.get());
757 g_assert_true(value.get() == result.get());
758
759 moduleInWk = adoptGRef(jsc_context_get_value(context.get(), "moduleInWk"));
760 checker.watch(moduleInWk.get());
761 g_assert_true(jsc_value_is_undefined(moduleInWk.get()));
762 }
763 g_assert_true(moduleObject.wasDeleted);
764}
765
766static void testJSCCheckSyntax()
767{
768 LeakChecker checker;
769 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
770 checker.watch(context.get());
771 ExceptionHandler exceptionHandler(context.get());
772
773 GRefPtr<JSCException> exception;
774 g_assert_cmpuint(jsc_context_check_syntax(context.get(), "f = 42", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, nullptr, 0, &exception.outPtr()), ==, JSC_CHECK_SYNTAX_RESULT_SUCCESS);
775 g_assert_null(exception.get());
776
777 g_assert_cmpuint(jsc_context_check_syntax(context.get(), "f = 42; b =", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, nullptr, 0, &exception.outPtr()), ==, JSC_CHECK_SYNTAX_RESULT_RECOVERABLE_ERROR);
778 checker.watch(exception.get());
779 g_assert_true(JSC_IS_EXCEPTION(exception.get()));
780 g_assert_cmpstr(jsc_exception_get_name(exception.get()), ==, "SyntaxError");
781 g_assert_cmpstr(jsc_exception_get_message(exception.get()), ==, "Unexpected end of script");
782 g_assert_cmpuint(jsc_exception_get_line_number(exception.get()), ==, 1);
783 g_assert_null(jsc_exception_get_source_uri(exception.get()));
784 g_assert_null(jsc_exception_get_backtrace_string(exception.get()));
785 GRefPtr<JSCValue> globalObject = adoptGRef(jsc_context_get_global_object(context.get()));
786 checker.watch(globalObject.get());
787 g_assert_false(jsc_value_object_has_property(globalObject.get(), "f"));
788 exception = nullptr;
789
790 // Only syntax errors are checked.
791 bool didThrow = false;
792 g_assert_throw_begin(exceptionHandler, didThrow);
793 GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "f", -1));
794 checker.watch(value.get());
795 g_assert_true(jsc_value_is_undefined(value.get()));
796 g_assert_did_throw(exceptionHandler, didThrow);
797 g_assert_cmpuint(jsc_context_check_syntax(context.get(), "f", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, nullptr, 0, &exception.outPtr()), ==, JSC_CHECK_SYNTAX_RESULT_SUCCESS);
798 g_assert_null(exception.get());
799
800 g_assert_cmpuint(jsc_context_check_syntax(context.get(), "f ==== 42", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, "file:///foo/script.js", 2, &exception.outPtr()), ==, JSC_CHECK_SYNTAX_RESULT_IRRECOVERABLE_ERROR);
801 checker.watch(exception.get());
802 g_assert_true(JSC_IS_EXCEPTION(exception.get()));
803 g_assert_cmpstr(jsc_exception_get_name(exception.get()), ==, "SyntaxError");
804 g_assert_cmpstr(jsc_exception_get_message(exception.get()), ==, "Unexpected token '='");
805 g_assert_cmpstr(jsc_exception_get_source_uri(exception.get()), ==, "file:///foo/script.js");
806
807 g_assert_cmpuint(jsc_context_check_syntax(context.get(), "f := 42", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, nullptr, 0, nullptr), ==, JSC_CHECK_SYNTAX_RESULT_IRRECOVERABLE_ERROR);
808 g_assert_cmpuint(jsc_context_check_syntax(context.get(), "f '42;", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, nullptr, 0, nullptr), ==, JSC_CHECK_SYNTAX_RESULT_UNTERMINATED_LITERAL_ERROR);
809
810 g_assert_cmpuint(jsc_context_check_syntax(context.get(), "import foo from '/foo.js'", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, nullptr, 0, nullptr), ==, JSC_CHECK_SYNTAX_RESULT_IRRECOVERABLE_ERROR);
811 g_assert_cmpuint(jsc_context_check_syntax(context.get(), "import foo from '/foo.js'", -1, JSC_CHECK_SYNTAX_MODE_MODULE, nullptr, 0, nullptr), ==, JSC_CHECK_SYNTAX_RESULT_SUCCESS);
812}
813
814static int foo(int n)
815{
816 return n * 2;
817}
818
819static void callback(JSCValue* function)
820{
821 GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function, G_TYPE_INT, 200, G_TYPE_NONE));
822 g_assert_true(jsc_value_is_undefined(value.get()));
823}
824
825static void doubleAndSetInResult(int n)
826{
827 GRefPtr<JSCValue> value = adoptGRef(jsc_value_new_number(jsc_context_get_current(), n * 2));
828 jsc_context_set_value(jsc_context_get_current(), "result", value.get());
829}
830
831static int sumFunction(GPtrArray* array)
832{
833 int retval = 0;
834
835 g_ptr_array_foreach(array, [](gpointer data, gpointer userData) {
836 g_assert_true(JSC_IS_VALUE(data));
837 JSCValue* item = JSC_VALUE(data);
838 g_assert_true(jsc_value_is_number(item));
839 *static_cast<int*>(userData) += jsc_value_to_int32(item);
840 }, &retval);
841
842 return retval;
843}
844
845static char* joinFunction(const char* const* strv, const char* sep)
846{
847 return g_strjoinv(sep, const_cast<char**>(strv));
848}
849
850static gboolean checkUserData(GFile* file)
851{
852 return G_IS_FILE(file);
853}
854
855static void testJSCFunction()
856{
857 {
858 LeakChecker checker;
859 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
860 checker.watch(context.get());
861 ExceptionHandler exceptionHandler(context.get());
862
863 GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "foo", G_CALLBACK(foo), nullptr, nullptr, G_TYPE_INT, 1, G_TYPE_INT));
864 checker.watch(function.get());
865 jsc_context_set_value(context.get(), "foo", function.get());
866 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "foo(200)", -1));
867 checker.watch(result.get());
868 g_assert_true(jsc_value_is_number(result.get()));
869 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
870
871 GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_INT, 200, G_TYPE_NONE));
872 checker.watch(value.get());
873 g_assert_true(value.get() == result.get());
874
875 GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
876 auto* parameter = jsc_value_new_number(context.get(), 200);
877 checker.watch(parameter);
878 g_ptr_array_add(parameters.get(), parameter);
879 value = adoptGRef(jsc_value_function_callv(function.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
880 checker.watch(value.get());
881 g_assert_true(value.get() == result.get());
882 }
883
884 {
885 LeakChecker checker;
886 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
887 checker.watch(context.get());
888 ExceptionHandler exceptionHandler(context.get());
889
890 GType parameterTypes[] = { G_TYPE_INT };
891 GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_functionv(context.get(), "foo", G_CALLBACK(foo), nullptr, nullptr, G_TYPE_INT, 1, parameterTypes));
892 checker.watch(function.get());
893 jsc_context_set_value(context.get(), "foo", function.get());
894 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "foo(200)", -1));
895 checker.watch(result.get());
896 g_assert_true(jsc_value_is_number(result.get()));
897 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
898
899 GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_INT, 200, G_TYPE_NONE));
900 checker.watch(value.get());
901 g_assert_true(value.get() == result.get());
902
903 GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
904 auto* parameter = jsc_value_new_number(context.get(), 200);
905 checker.watch(parameter);
906 g_ptr_array_add(parameters.get(), parameter);
907 value = adoptGRef(jsc_value_function_callv(function.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
908 checker.watch(value.get());
909 g_assert_true(value.get() == result.get());
910 }
911
912 {
913 LeakChecker checker;
914 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
915 checker.watch(context.get());
916 ExceptionHandler exceptionHandler(context.get());
917
918 GRefPtr<JSCValue> function = adoptGRef(jsc_context_evaluate(context.get(), "foo = function(n) { return n * 2; }", -1));
919 checker.watch(function.get());
920
921 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "foo(200)", -1));
922 checker.watch(result.get());
923 g_assert_true(jsc_value_is_number(result.get()));
924 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
925
926 GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_INT, 200, G_TYPE_NONE));
927 checker.watch(value.get());
928 g_assert_true(value.get() == result.get());
929
930 GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
931 auto* parameter = jsc_value_new_number(context.get(), 200);
932 checker.watch(parameter);
933 g_ptr_array_add(parameters.get(), parameter);
934 value = adoptGRef(jsc_value_function_callv(function.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
935 checker.watch(value.get());
936 g_assert_true(value.get() == result.get());
937 }
938
939 {
940 LeakChecker checker;
941 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
942 checker.watch(context.get());
943 ExceptionHandler exceptionHandler(context.get());
944
945 GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "callback", G_CALLBACK(callback), nullptr, nullptr, G_TYPE_NONE, 1, JSC_TYPE_VALUE));
946 checker.watch(function.get());
947 jsc_context_set_value(context.get(), "callback", function.get());
948 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "var result = 0; callback(function(n){ result = n * 2; }); result", -1));
949 checker.watch(result.get());
950 g_assert_true(jsc_value_is_number(result.get()));
951 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
952
953 result = adoptGRef(jsc_context_evaluate(context.get(), "result = 0", -1));
954 checker.watch(result.get());
955 result = adoptGRef(jsc_context_evaluate(context.get(), "result", -1));
956 checker.watch(result.get());
957 g_assert_true(jsc_value_is_number(result.get()));
958 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 0);
959
960 GRefPtr<JSCValue> dbl = adoptGRef(jsc_value_new_function(context.get(), "doubleAndSetInResult", G_CALLBACK(doubleAndSetInResult), nullptr, nullptr, G_TYPE_NONE, 1, G_TYPE_INT));
961 checker.watch(dbl.get());
962 GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), JSC_TYPE_VALUE, dbl.get(), G_TYPE_NONE));
963 checker.watch(value.get());
964 result = adoptGRef(jsc_context_evaluate(context.get(), "result", -1));
965 checker.watch(result.get());
966 g_assert_true(jsc_value_is_number(result.get()));
967 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
968
969 GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new());
970 g_ptr_array_add(parameters.get(), dbl.get());
971 value = adoptGRef(jsc_value_function_callv(function.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
972 checker.watch(value.get());
973 result = adoptGRef(jsc_context_evaluate(context.get(), "result", -1));
974 checker.watch(result.get());
975 g_assert_true(jsc_value_is_number(result.get()));
976 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
977 }
978
979 {
980 LeakChecker checker;
981 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
982 checker.watch(context.get());
983 ExceptionHandler exceptionHandler(context.get());
984
985 GRefPtr<JSCValue> function = adoptGRef(jsc_context_evaluate(context.get(),
986 "sumFunction = function(array) {\n"
987 " var result = 0;\n"
988 " for (var i in array) { result += array[i]; }\n"
989 " return result;\n"
990 "}", -1));
991 checker.watch(function.get());
992 g_assert_true(jsc_value_is_object(function.get()));
993
994 GRefPtr<JSCValue> array = adoptGRef(jsc_value_new_array(context.get(), G_TYPE_INT, 2, G_TYPE_INT, 4, G_TYPE_INT, 6, G_TYPE_NONE));
995 checker.watch(array.get());
996
997 GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), JSC_TYPE_VALUE, array.get(), G_TYPE_NONE));
998 checker.watch(value.get());
999 g_assert_true(jsc_value_is_number(value.get()));
1000 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 12);
1001
1002 GRefPtr<GPtrArray> gArray = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
1003 g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 1));
1004 g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 3));
1005 g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 5));
1006
1007 value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_PTR_ARRAY, gArray.get(), G_TYPE_NONE));
1008 checker.watch(value.get());
1009 g_assert_true(jsc_value_is_number(value.get()));
1010 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 9);
1011 }
1012
1013 {
1014 LeakChecker checker;
1015 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
1016 checker.watch(context.get());
1017 ExceptionHandler exceptionHandler(context.get());
1018
1019 GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "sumFunction", G_CALLBACK(sumFunction), nullptr, nullptr, G_TYPE_INT, 1, G_TYPE_PTR_ARRAY));
1020 checker.watch(function.get());
1021 jsc_context_set_value(context.get(), "sumFunction", function.get());
1022 GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "sumFunction([2,4,6])", -1));
1023 checker.watch(value.get());
1024 g_assert_true(jsc_value_is_number(value.get()));
1025 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 12);
1026
1027 GRefPtr<GPtrArray> gArray = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
1028 g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 1));
1029 g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 3));
1030 g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 5));
1031
1032 value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_PTR_ARRAY, gArray.get(), G_TYPE_NONE));
1033 checker.watch(value.get());
1034 g_assert_true(jsc_value_is_number(value.get()));
1035 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 9);
1036 }
1037
1038 {
1039 LeakChecker checker;
1040 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
1041 checker.watch(context.get());
1042 ExceptionHandler exceptionHandler(context.get());
1043
1044 GRefPtr<JSCValue> function = adoptGRef(jsc_context_evaluate(context.get(),
1045 "joinFunction = function(array, sep) {\n"
1046 " var result = '';\n"
1047 " for (var i in array) {\n"
1048 " result += array[i];\n"
1049 " if (i != array.length - 1) { result += sep; }\n"
1050 " }\n"
1051 " return result;\n"
1052 "}", -1));
1053 checker.watch(function.get());
1054 g_assert_true(jsc_value_is_object(function.get()));
1055
1056 const char* strv[] = { "one", "two", "three", nullptr };
1057 GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_STRV, strv, G_TYPE_STRING, " ", G_TYPE_NONE));
1058 checker.watch(value.get());
1059 g_assert_true(jsc_value_is_string(value.get()));
1060 GUniquePtr<char> valueString(jsc_value_to_string(value.get()));
1061 g_assert_cmpstr(valueString.get(), ==, "one two three");
1062
1063 function = adoptGRef(jsc_value_new_function(context.get(), "joinFunction2", G_CALLBACK(joinFunction), nullptr, nullptr, G_TYPE_STRING, 2, G_TYPE_STRV, G_TYPE_STRING));
1064 checker.watch(function.get());
1065 jsc_context_set_value(context.get(), "joinFunction2", function.get());
1066 value = adoptGRef(jsc_context_evaluate(context.get(), "joinFunction2(['one','two','three'], ' ')", -1));
1067 checker.watch(value.get());
1068 g_assert_true(jsc_value_is_string(value.get()));
1069 GUniquePtr<char> valueString2(jsc_value_to_string(value.get()));
1070 g_assert_cmpstr(valueString2.get(), ==, valueString.get());
1071
1072 bool didThrow = false;
1073 g_assert_throw_begin(exceptionHandler, didThrow);
1074 value = adoptGRef(jsc_context_evaluate(context.get(), "joinFunction2(['one',2,'three'], ' ')", -1));
1075 checker.watch(value.get());
1076 g_assert_true(jsc_value_is_undefined(value.get()));
1077 g_assert_did_throw(exceptionHandler, didThrow);
1078 }
1079
1080 {
1081 LeakChecker checker;
1082 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
1083 checker.watch(context.get());
1084 ExceptionHandler exceptionHandler(context.get());
1085
1086 GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function_variadic(context.get(), "sumFunction", G_CALLBACK(sumFunction), nullptr, nullptr, G_TYPE_INT));
1087 checker.watch(function.get());
1088 jsc_context_set_value(context.get(), "sumFunction", function.get());
1089
1090 GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "sumFunction(2,4,6)", -1));
1091 checker.watch(value.get());
1092 g_assert_true(jsc_value_is_number(value.get()));
1093 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 12);
1094
1095 value = adoptGRef(jsc_context_evaluate(context.get(), "sumFunction()", -1));
1096 checker.watch(value.get());
1097 g_assert_true(jsc_value_is_number(value.get()));
1098 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
1099 }
1100
1101 {
1102 LeakChecker checker;
1103 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
1104 checker.watch(context.get());
1105 ExceptionHandler exceptionHandler(context.get());
1106
1107 GFile* file = g_file_new_for_path(".");
1108 checker.watch(file);
1109 GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "checkUserData", G_CALLBACK(checkUserData),
1110 file, g_object_unref, G_TYPE_BOOLEAN, 0, G_TYPE_NONE));
1111 checker.watch(function.get());
1112 jsc_context_set_value(context.get(), "checkUserData", function.get());
1113
1114 GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "checkUserData()", -1));
1115 checker.watch(value.get());
1116 g_assert_true(jsc_value_is_boolean(value.get()));
1117 g_assert_true(jsc_value_to_boolean(value.get()));
1118
1119 value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_NONE));
1120 checker.watch(value.get());
1121 g_assert_true(jsc_value_is_boolean(value.get()));
1122 g_assert_true(jsc_value_to_boolean(value.get()));
1123 }
1124}
1125
1126static void testJSCObject()
1127{
1128 {
1129 LeakChecker checker;
1130 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
1131 checker.watch(context.get());
1132 ExceptionHandler exceptionHandler(context.get());
1133
1134 GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "class Foo { foo(n) { return n * 2; } }; foo = new Foo;", -1));
1135 checker.watch(foo.get());
1136 g_assert_true(jsc_value_is_object(foo.get()));
1137 g_assert_true(jsc_value_object_is_instance_of(foo.get(), "Foo"));
1138 g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
1139
1140 GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
1141 g_assert_null(properties.get());
1142
1143 GRefPtr<JSCValue> result = adoptGRef(jsc_value_object_invoke_method(foo.get(), "foo", G_TYPE_INT, 200, G_TYPE_NONE));
1144 checker.watch(result.get());
1145 g_assert_true(jsc_value_is_number(result.get()));
1146 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
1147
1148 GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
1149 auto* parameter = jsc_value_new_number(context.get(), 200);
1150 checker.watch(parameter);
1151 g_ptr_array_add(parameters.get(), parameter);
1152 result = adoptGRef(jsc_value_object_invoke_methodv(foo.get(), "foo", parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
1153 checker.watch(result.get());
1154 g_assert_true(jsc_value_is_number(result.get()));
1155 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
1156
1157 g_assert_false(jsc_value_object_has_property(foo.get(), "bar"));
1158 bool didThrow = false;
1159 g_assert_throw_begin(exceptionHandler, didThrow);
1160 result = adoptGRef(jsc_value_object_invoke_method(foo.get(), "bar", G_TYPE_INT, 200, G_TYPE_NONE));
1161 checker.watch(result.get());
1162 g_assert_true(jsc_value_is_undefined(result.get()));
1163 g_assert_did_throw(exceptionHandler, didThrow);
1164
1165 g_assert_throw_begin(exceptionHandler, didThrow);
1166 result = adoptGRef(jsc_value_object_invoke_methodv(foo.get(), "bar", parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
1167 checker.watch(result.get());
1168 g_assert_true(jsc_value_is_undefined(result.get()));
1169 g_assert_did_throw(exceptionHandler, didThrow);
1170
1171 GRefPtr<JSCValue> constructor = adoptGRef(jsc_context_evaluate(context.get(), "Foo", -1));
1172 checker.watch(constructor.get());
1173 g_assert_true(jsc_value_is_constructor(constructor.get()));
1174
1175 foo = adoptGRef(jsc_value_constructor_call(constructor.get(), G_TYPE_NONE));
1176 checker.watch(foo.get());
1177 g_assert_true(jsc_value_is_object(foo.get()));
1178 g_assert_true(jsc_value_object_is_instance_of(foo.get(), "Foo"));
1179
1180 result = adoptGRef(jsc_value_object_invoke_method(foo.get(), "foo", G_TYPE_INT, 300, G_TYPE_NONE));
1181 checker.watch(result.get());
1182 g_assert_true(jsc_value_is_number(result.get()));
1183 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 600);
1184
1185 jsc_context_set_value(context.get(), "foo2", foo.get());
1186 result = adoptGRef(jsc_context_evaluate(context.get(), "foo2 instanceof Foo", -1));
1187 checker.watch(result.get());
1188 g_assert_true(jsc_value_is_boolean(result.get()));
1189 g_assert_true(jsc_value_to_boolean(result.get()));
1190
1191 result = adoptGRef(jsc_context_evaluate(context.get(), "foo2.foo(500)", -1));
1192 checker.watch(result.get());
1193 g_assert_true(jsc_value_is_number(result.get()));
1194 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 1000);
1195
1196 GRefPtr<JSCValue> foo2 = adoptGRef(jsc_value_constructor_callv(constructor.get(), 0, nullptr));
1197 checker.watch(foo2.get());
1198 g_assert_true(jsc_value_is_object(foo2.get()));
1199 g_assert_true(jsc_value_object_is_instance_of(foo2.get(), "Foo"));
1200 g_assert_false(foo.get() == foo2.get());
1201 }
1202
1203 {
1204 LeakChecker checker;
1205 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
1206 checker.watch(context.get());
1207 ExceptionHandler exceptionHandler(context.get());
1208
1209 GRefPtr<JSCValue> object = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr));
1210 checker.watch(object.get());
1211 g_assert_true(jsc_value_is_object(object.get()));
1212 g_assert_true(jsc_value_object_is_instance_of(object.get(), "Object"));
1213
1214 GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(object.get()));
1215 g_assert_null(properties.get());
1216
1217 GRefPtr<JSCValue> property = adoptGRef(jsc_value_new_number(context.get(), 25));
1218 checker.watch(property.get());
1219 g_assert_false(jsc_value_object_has_property(object.get(), "val"));
1220 jsc_value_object_define_property_data(object.get(), "val", static_cast<JSCValuePropertyFlags>(0), property.get());
1221 g_assert_true(jsc_value_object_has_property(object.get(), "val"));
1222 properties.reset(jsc_value_object_enumerate_properties(object.get()));
1223 g_assert_null(properties.get());
1224 jsc_context_set_value(context.get(), "f", object.get());
1225
1226 GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "f.val;", -1));
1227 checker.watch(value.get());
1228 g_assert_true(jsc_value_is_number(value.get()));
1229 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 25);
1230
1231 bool didThrow = false;
1232 g_assert_throw_begin(exceptionHandler, didThrow);
1233 value = adoptGRef(jsc_context_evaluate(context.get(), "'use strict'; f.val = 32;", -1));
1234 checker.watch(value.get());
1235 g_assert_true(jsc_value_is_undefined(value.get()));
1236 g_assert_did_throw(exceptionHandler, didThrow);
1237
1238 value = adoptGRef(jsc_context_evaluate(context.get(), "f.propertyIsEnumerable('val');", -1));
1239 checker.watch(value.get());
1240 g_assert_true(jsc_value_is_boolean(value.get()));
1241 g_assert_true(jsc_value_to_boolean(value.get()) == FALSE);
1242
1243 value = adoptGRef(jsc_context_evaluate(context.get(), "delete f.val;", -1));
1244 checker.watch(value.get());
1245 g_assert_true(jsc_value_object_has_property(object.get(), "val"));
1246 value = adoptGRef(jsc_context_evaluate(context.get(), "f.val;", -1));
1247 checker.watch(value.get());
1248 g_assert_true(jsc_value_is_number(value.get()));
1249 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 25);
1250
1251 g_assert_false(jsc_value_object_delete_property(object.get(), "val"));
1252 g_assert_true(jsc_value_object_has_property(object.get(), "val"));
1253
1254 property = adoptGRef(jsc_value_new_number(context.get(), 52));
1255 checker.watch(property.get());
1256 g_assert_throw_begin(exceptionHandler, didThrow);
1257 jsc_value_object_define_property_data(object.get(), "val", static_cast<JSCValuePropertyFlags>(0), property.get());
1258 g_assert_did_throw(exceptionHandler, didThrow);
1259
1260 property = adoptGRef(jsc_value_new_number(context.get(), 32));
1261 checker.watch(property.get());
1262 g_assert_false(jsc_value_object_has_property(object.get(), "val2"));
1263 jsc_value_object_define_property_data(object.get(), "val2", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_ENUMERABLE | JSC_VALUE_PROPERTY_WRITABLE), property.get());
1264 g_assert_true(jsc_value_object_has_property(object.get(), "val2"));
1265 value = adoptGRef(jsc_context_evaluate(context.get(), "f.val2;", -1));
1266 checker.watch(value.get());
1267 g_assert_true(jsc_value_is_number(value.get()));
1268 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 32);
1269
1270 properties.reset(jsc_value_object_enumerate_properties(object.get()));
1271 g_assert_cmpuint(g_strv_length(properties.get()), ==, 1);
1272 g_assert_cmpstr(properties.get()[0], ==, "val2");
1273 g_assert_null(properties.get()[1]);
1274
1275 value = adoptGRef(jsc_context_evaluate(context.get(), "'use strict'; f.val2 = 45;", -1));
1276 checker.watch(value.get());
1277 value = adoptGRef(jsc_context_evaluate(context.get(), "f.val2;", -1));
1278 checker.watch(value.get());
1279 g_assert_true(jsc_value_is_number(value.get()));
1280 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 45);
1281
1282 value = adoptGRef(jsc_context_evaluate(context.get(), "f.propertyIsEnumerable('val2');", -1));
1283 checker.watch(value.get());
1284 g_assert_true(jsc_value_is_boolean(value.get()));
1285 g_assert_true(jsc_value_to_boolean(value.get()) == TRUE);
1286
1287 g_assert_false(jsc_value_object_delete_property(object.get(), "val2"));
1288 g_assert_true(jsc_value_object_has_property(object.get(), "val2"));
1289
1290 property = adoptGRef(jsc_value_new_number(context.get(), 125));
1291 checker.watch(property.get());
1292 g_assert_false(jsc_value_object_has_property(object.get(), "val3"));
1293 jsc_value_object_define_property_data(object.get(), "val3", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE | JSC_VALUE_PROPERTY_WRITABLE), property.get());
1294 g_assert_true(jsc_value_object_has_property(object.get(), "val3"));
1295 value = adoptGRef(jsc_context_evaluate(context.get(), "f.val3;", -1));
1296 checker.watch(value.get());
1297 g_assert_true(jsc_value_is_number(value.get()));
1298 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 125);
1299
1300 properties.reset(jsc_value_object_enumerate_properties(object.get()));
1301 g_assert_cmpuint(g_strv_length(properties.get()), ==, 1);
1302 g_assert_cmpstr(properties.get()[0], ==, "val2");
1303 g_assert_null(properties.get()[1]);
1304
1305 property = adoptGRef(jsc_value_new_number(context.get(), 150));
1306 checker.watch(property.get());
1307 jsc_value_object_define_property_data(object.get(), "val3", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE), property.get());
1308 g_assert_true(jsc_value_object_has_property(object.get(), "val3"));
1309 value = adoptGRef(jsc_context_evaluate(context.get(), "f.val3;", -1));
1310 checker.watch(value.get());
1311 g_assert_true(jsc_value_is_number(value.get()));
1312 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 150);
1313
1314 properties.reset(jsc_value_object_enumerate_properties(object.get()));
1315 g_assert_cmpuint(g_strv_length(properties.get()), ==, 1);
1316 g_assert_cmpstr(properties.get()[0], ==, "val2");
1317 g_assert_null(properties.get()[1]);
1318
1319 value = adoptGRef(jsc_context_evaluate(context.get(), "delete f.val3;", -1));
1320 checker.watch(value.get());
1321 g_assert_false(jsc_value_object_has_property(object.get(), "val3"));
1322 value = adoptGRef(jsc_context_evaluate(context.get(), "f.val3;", -1));
1323 checker.watch(value.get());
1324 g_assert_true(jsc_value_is_undefined(value.get()));
1325
1326 property = adoptGRef(jsc_value_new_number(context.get(), 250));
1327 checker.watch(property.get());
1328 g_assert_false(jsc_value_object_has_property(object.get(), "val4"));
1329 jsc_value_object_define_property_data(object.get(), "val4", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE | JSC_VALUE_PROPERTY_ENUMERABLE), property.get());
1330 g_assert_true(jsc_value_object_has_property(object.get(), "val4"));
1331 value = adoptGRef(jsc_context_evaluate(context.get(), "f.val4;", -1));
1332 checker.watch(value.get());
1333 g_assert_true(jsc_value_is_number(value.get()));
1334 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 250);
1335
1336 properties.reset(jsc_value_object_enumerate_properties(object.get()));
1337 g_assert_cmpuint(g_strv_length(properties.get()), ==, 2);
1338 g_assert_cmpstr(properties.get()[0], ==, "val2");
1339 g_assert_cmpstr(properties.get()[1], ==, "val4");
1340 g_assert_null(properties.get()[2]);
1341
1342 g_assert_true(jsc_value_object_delete_property(object.get(), "val4"));
1343 g_assert_false(jsc_value_object_has_property(object.get(), "val4"));
1344
1345 properties.reset(jsc_value_object_enumerate_properties(object.get()));
1346 g_assert_cmpuint(g_strv_length(properties.get()), ==, 1);
1347 g_assert_cmpstr(properties.get()[0], ==, "val2");
1348 g_assert_null(properties.get()[1]);
1349
1350 GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "foo", G_CALLBACK(foo), nullptr, nullptr, G_TYPE_INT, 1, G_TYPE_INT));
1351 checker.watch(function.get());
1352
1353 g_assert_false(jsc_value_object_has_property(object.get(), "foo"));
1354 jsc_value_object_define_property_data(object.get(), "foo", static_cast<JSCValuePropertyFlags>(0), function.get());
1355 g_assert_true(jsc_value_object_has_property(object.get(), "foo"));
1356
1357 properties.reset(jsc_value_object_enumerate_properties(object.get()));
1358 g_assert_cmpuint(g_strv_length(properties.get()), ==, 1);
1359 g_assert_cmpstr(properties.get()[0], ==, "val2");
1360 g_assert_null(properties.get()[1]);
1361
1362 GRefPtr<JSCValue> result = adoptGRef(jsc_value_object_invoke_method(object.get(), "foo", G_TYPE_INT, 200, G_TYPE_NONE));
1363 checker.watch(result.get());
1364 g_assert_true(jsc_value_is_number(result.get()));
1365 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
1366
1367 g_assert_throw_begin(exceptionHandler, didThrow);
1368 result = adoptGRef(jsc_value_object_invoke_method(object.get(), "foo", G_TYPE_POINTER, "200", G_TYPE_NONE));
1369 checker.watch(result.get());
1370 g_assert_true(jsc_value_is_undefined(result.get()));
1371 g_assert_did_throw(exceptionHandler, didThrow);
1372 }
1373
1374 {
1375 LeakChecker checker;
1376 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
1377 checker.watch(context.get());
1378 ExceptionHandler exceptionHandler(context.get());
1379
1380 GRefPtr<JSCValue> value = adoptGRef(jsc_value_new_number(context.get(), 125));
1381 checker.watch(value.get());
1382 g_assert_true(jsc_value_is_number(value.get()));
1383 g_assert_false(jsc_value_is_object(value.get()));
1384 g_assert_false(jsc_value_object_is_instance_of(value.get(), "Object"));
1385
1386 bool didThrow = false;
1387 g_assert_throw_begin(exceptionHandler, didThrow);
1388 GRefPtr<JSCValue> result = adoptGRef(jsc_value_object_invoke_method(value.get(), "foo", G_TYPE_INT, 200, G_TYPE_NONE));
1389 checker.watch(result.get());
1390 g_assert_true(jsc_value_is_undefined(result.get()));
1391 g_assert_throw_begin(exceptionHandler, didThrow);
1392 }
1393}
1394
1395typedef struct _Foo Foo;
1396struct _Foo {
1397 int foo;
1398 HashMap<CString, int> properties;
1399 Foo* sibling;
1400};
1401
1402static Foo* fooCreate()
1403{
1404 Foo* foo = g_new0(Foo, 1);
1405 new (foo) Foo();
1406 return foo;
1407}
1408
1409static Foo* fooCreateWithFoo(int value)
1410{
1411 auto* f = fooCreate();
1412 f->foo = value;
1413 return f;
1414}
1415
1416static Foo* fooCreateWithFooV(GPtrArray* values)
1417{
1418 auto* f = fooCreate();
1419 g_ptr_array_foreach(values, [](gpointer data, gpointer userData) {
1420 g_assert_true(JSC_IS_VALUE(data));
1421 JSCValue* item = JSC_VALUE(data);
1422 g_assert_true(jsc_value_is_number(item));
1423 auto* foo = static_cast<Foo*>(userData);
1424 foo->foo += jsc_value_to_int32(item);
1425 }, f);
1426 return f;
1427}
1428
1429static Foo* fooCreateWithUserData(GFile* file)
1430{
1431 g_assert_true(G_IS_FILE(file));
1432 return fooCreate();
1433}
1434
1435static void fooFree(Foo* foo)
1436{
1437 foo->~Foo();
1438 g_free(foo);
1439}
1440
1441static void setFoo(Foo* foo, int value)
1442{
1443 foo->foo = value;
1444}
1445
1446static int getFoo(Foo* foo)
1447{
1448 return foo->foo;
1449}
1450
1451static void setSibling(Foo* foo, Foo* sibling)
1452{
1453 foo->sibling = sibling;
1454}
1455
1456static Foo* getSibling(Foo* foo)
1457{
1458 return foo->sibling;
1459}
1460
1461static void multiplyFoo(Foo* foo, int multiplier)
1462{
1463 foo->foo *= multiplier;
1464}
1465
1466static void multiplyFooV(Foo* foo, GPtrArray* multipliers)
1467{
1468 g_ptr_array_foreach(multipliers, [](gpointer data, gpointer userData) {
1469 g_assert_true(JSC_IS_VALUE(data));
1470 JSCValue* item = JSC_VALUE(data);
1471 g_assert_true(jsc_value_is_number(item));
1472 auto* foo = static_cast<Foo*>(userData);
1473 foo->foo *= jsc_value_to_int32(item);
1474 }, foo);
1475}
1476
1477static int fooGetProperty(Foo* foo, const char* name)
1478{
1479 auto addResult = foo->properties.add(name, 0);
1480 return addResult.iterator->value;
1481}
1482
1483static void fooSetProperty(Foo* foo, const char* name, int value)
1484{
1485 auto addResult = foo->properties.add(name, value);
1486 if (!addResult.isNewEntry)
1487 addResult.iterator->value = value;
1488}
1489
1490struct PromiseData {
1491 Foo* foo;
1492 int multiplier;
1493 LeakChecker* checker;
1494};
1495
1496static void getMultiplyFoo(JSCValue* resolve, JSCValue* reject, PromiseData* data)
1497{
1498 data->checker->watch(resolve);
1499 g_assert_true(JSC_IS_VALUE(resolve));
1500 g_assert_true(jsc_value_is_function(resolve));
1501 data->checker->watch(reject);
1502 g_assert_true(JSC_IS_VALUE(reject));
1503 g_assert_true(jsc_value_is_function(reject));
1504
1505 GRefPtr<JSCValue> result;
1506 if (data->multiplier > 0)
1507 result = adoptGRef(jsc_value_function_call(resolve, G_TYPE_INT, data->foo->foo * data->multiplier, G_TYPE_NONE));
1508 else {
1509 GRefPtr<JSCException> exception = adoptGRef(jsc_exception_new(jsc_context_get_current(), "Multiplier must be positive greater than 0"));
1510 result = adoptGRef(jsc_value_function_call(reject, JSC_TYPE_EXCEPTION, exception.get(), G_TYPE_NONE));
1511 }
1512 data->checker->watch(result.get());
1513 g_assert_true(jsc_value_is_undefined(result.get()));
1514}
1515
1516static JSCValue* getMultiplyFooAsync(Foo* foo, int multiplier, LeakChecker* checker)
1517{
1518 auto* jsContext = jsc_context_get_current();
1519 g_assert_true(JSC_IS_CONTEXT(jsContext));
1520
1521 GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(jsContext, nullptr, G_CALLBACK(getMultiplyFoo), new PromiseData { foo, multiplier, checker},
1522 [](gpointer data) { delete static_cast<PromiseData*>(data); }, G_TYPE_NONE, 2, JSC_TYPE_VALUE, JSC_TYPE_VALUE));
1523 checker->watch(function.get());
1524 GRefPtr<JSCValue> promise = adoptGRef(jsc_context_get_value(jsContext, "Promise"));
1525 checker->watch(promise.get());
1526 g_assert_true(jsc_value_is_constructor(promise.get()));
1527 auto* returnValue = jsc_value_constructor_call(promise.get(), JSC_TYPE_VALUE, function.get(), G_TYPE_NONE);
1528 checker->watch(returnValue);
1529 return returnValue;
1530}
1531
1532typedef struct {
1533 int baz;
1534} Baz;
1535
1536static Baz* bazCreate()
1537{
1538 return g_new0(Baz, 1);
1539}
1540
1541static JSCClassVTable fooVTable = {
1542 // get_property
1543 [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name) -> JSCValue* {
1544 auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
1545 checker->watch(context);
1546
1547 if (!g_str_has_prefix(name, "prop_"))
1548 return nullptr;
1549
1550 if (!g_strcmp0(name, "prop_throw_on_get")) {
1551 jsc_context_throw(context, "Invalid property");
1552 return jsc_value_new_undefined(context);
1553 }
1554
1555 auto* foo = static_cast<Foo*>(instance);
1556 auto* returnValue = jsc_value_new_number(context, fooGetProperty(foo, name));
1557 checker->watch(returnValue);
1558 return returnValue;
1559 },
1560 // set_property
1561 [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name, JSCValue* value) -> gboolean {
1562 auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
1563 checker->watch(context);
1564 checker->watch(value);
1565
1566 if (!g_str_has_prefix(name, "prop_"))
1567 return FALSE;
1568
1569 if (!jsc_value_is_number(value)) {
1570 jsc_context_throw(context, "Invalid value set: only numbers are allowed");
1571 return TRUE;
1572 }
1573
1574 auto* foo = static_cast<Foo*>(instance);
1575 fooSetProperty(foo, name, jsc_value_to_int32(value));
1576 return true;
1577 },
1578 // has_property
1579 [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name) -> gboolean {
1580 auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
1581 checker->watch(context);
1582 return g_str_has_prefix(name, "prop_");
1583 },
1584 // delete_property
1585 [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name) -> gboolean {
1586 auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
1587 checker->watch(context);
1588
1589 if (!g_strcmp0(name, "prop_cant_delete"))
1590 return FALSE;
1591
1592 if (!g_strcmp0(name, "prop_throw_on_delete")) {
1593 jsc_context_throw(context, "Invalid property");
1594 return TRUE;
1595 }
1596
1597 auto* foo = static_cast<Foo*>(instance);
1598 if (!foo->properties.contains(name))
1599 return FALSE;
1600
1601 foo->properties.remove(name);
1602 return TRUE;
1603 },
1604 // enumerate_properties
1605 [](JSCClass* jscClass, JSCContext* context, gpointer instance) -> char** {
1606 auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
1607 checker->watch(context);
1608
1609 auto* foo = static_cast<Foo*>(instance);
1610 GRefPtr<GPtrArray> properties = adoptGRef(g_ptr_array_new_with_free_func(g_free));
1611 Vector<CString> names = copyToVector(foo->properties.keys());
1612 std::sort(names.begin(), names.end());
1613 for (const auto& name : names) {
1614 if (g_str_has_prefix(name.data(), "prop_enum_"))
1615 g_ptr_array_add(properties.get(), g_strdup(name.data()));
1616 }
1617 if (!properties->len)
1618 return nullptr;
1619
1620 g_ptr_array_add(properties.get(), nullptr);
1621 return reinterpret_cast<char**>(g_ptr_array_free(properties.leakRef(), FALSE));
1622 },
1623 // padding
1624 nullptr, nullptr, nullptr, nullptr
1625};
1626
1627static GFile* createGFile(const char* path)
1628{
1629 GFile* file = g_file_new_for_path(path);
1630 auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jsc_context_get_current()), "leak-checker"));
1631 checker->watch(file);
1632 return file;
1633}
1634
1635static GFile* getGFile(GFile* file)
1636{
1637 return G_FILE(g_object_ref(file));
1638}
1639
1640static JSCValue* getParent(GFile* file, JSCClass* jscClass)
1641{
1642 auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jsc_context_get_current()), "leak-checker"));
1643 GFile* parent = g_file_get_parent(file);
1644 checker->watch(parent);
1645 auto* value = jsc_value_new_object(jsc_context_get_current(), parent, jscClass);
1646 checker->watch(value);
1647 return value;
1648}
1649
1650static GString* createGString(const char* str)
1651{
1652 return g_string_new(str);
1653}
1654
1655static GString* getGString(GString* str)
1656{
1657 return str;
1658}
1659
1660static GString* getGStringCopyWillRaise(GString* str)
1661{
1662 return static_cast<GString*>(g_boxed_copy(G_TYPE_GSTRING, str));
1663}
1664
1665static JSCValue* getGStringCopy(GString *str, JSCClass* jscClass)
1666{
1667 auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jsc_context_get_current()), "leak-checker"));
1668 auto* copy = getGStringCopyWillRaise(str);
1669 auto* value = jsc_value_new_object(jsc_context_get_current(), copy, jscClass);
1670 checker->watch(value);
1671 return value;
1672}
1673
1674static char* getGStringStr(GString* str)
1675{
1676 return g_strdup(str->str);
1677}
1678
1679static guint64 getGStringLen(GString* str)
1680{
1681 return str->len;
1682}
1683
1684static void freeGString(GString* str)
1685{
1686 g_string_free(str, TRUE);
1687}
1688
1689static void testJSCClass()
1690{
1691 {
1692 LeakChecker checker;
1693 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
1694 checker.watch(context.get());
1695 ExceptionHandler exceptionHandler(context.get());
1696
1697 JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
1698 checker.watch(jscClass);
1699 g_assert_false(jsc_class_get_parent(jscClass));
1700
1701 GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
1702 checker.watch(constructor.get());
1703 g_assert_true(jsc_value_is_constructor(constructor.get()));
1704 jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
1705
1706 GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1));
1707 checker.watch(foo.get());
1708 g_assert_true(jsc_value_is_object(foo.get()));
1709 g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(jscClass)));
1710 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof Foo;", -1));
1711 checker.watch(result.get());
1712 g_assert_true(jsc_value_is_boolean(result.get()));
1713 g_assert_true(jsc_value_to_boolean(result.get()));
1714
1715 GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
1716 g_assert_null(properties.get());
1717
1718 g_assert_false(jsc_value_object_has_property(foo.get(), "getFoo"));
1719 jsc_class_add_method(jscClass, "getFoo", G_CALLBACK(getFoo), nullptr, nullptr, G_TYPE_INT, 0, G_TYPE_NONE);
1720 g_assert_true(jsc_value_object_has_property(foo.get(), "getFoo"));
1721 properties.reset(jsc_value_object_enumerate_properties(foo.get()));
1722 g_assert_null(properties.get());
1723 GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_invoke_method(foo.get(), "getFoo", G_TYPE_NONE));
1724 checker.watch(value.get());
1725 g_assert_true(jsc_value_is_number(value.get()));
1726 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
1727
1728 value = adoptGRef(jsc_value_object_invoke_methodv(foo.get(), "getFoo", 0, nullptr));
1729 checker.watch(value.get());
1730 g_assert_true(jsc_value_is_number(value.get()));
1731 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
1732
1733 GRefPtr<JSCValue> value2 = adoptGRef(jsc_context_evaluate(context.get(), "f.getFoo()", -1));
1734 checker.watch(value2.get());
1735 g_assert_true(value.get() == value2.get());
1736
1737 g_assert_false(jsc_value_object_has_property(foo.get(), "setFoo"));
1738 GType parameterTypes[] = { G_TYPE_INT };
1739 jsc_class_add_methodv(jscClass, "setFoo", G_CALLBACK(setFoo), nullptr, nullptr, G_TYPE_NONE, 1, parameterTypes);
1740 g_assert_true(jsc_value_object_has_property(foo.get(), "setFoo"));
1741 properties.reset(jsc_value_object_enumerate_properties(foo.get()));
1742 g_assert_null(properties.get());
1743 result = adoptGRef(jsc_value_object_invoke_method(foo.get(), "setFoo", G_TYPE_INT, 25, G_TYPE_NONE));
1744 checker.watch(result.get());
1745 value = adoptGRef(jsc_context_evaluate(context.get(), "f.getFoo()", -1));
1746 checker.watch(value.get());
1747 g_assert_true(jsc_value_is_number(value.get()));
1748 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 25);
1749
1750 result = adoptGRef(jsc_context_evaluate(context.get(), "f.setFoo(45)", -1));
1751 checker.watch(result.get());
1752 value = adoptGRef(jsc_value_object_invoke_method(foo.get(), "getFoo", G_TYPE_NONE));
1753 checker.watch(value.get());
1754 g_assert_true(jsc_value_is_number(value.get()));
1755 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 45);
1756
1757 value = adoptGRef(jsc_value_object_invoke_methodv(foo.get(), "getFoo", 0, nullptr));
1758 checker.watch(value.get());
1759 g_assert_true(jsc_value_is_number(value.get()));
1760 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 45);
1761
1762 jsc_class_add_method_variadic(jscClass, "multiply", G_CALLBACK(multiplyFooV), nullptr, nullptr, G_TYPE_NONE);
1763 g_assert_true(jsc_value_object_has_property(foo.get(), "multiply"));
1764 value = adoptGRef(jsc_context_evaluate(context.get(), "f.setFoo(1); f.multiply(1,2,3);", -1));
1765 checker.watch(value.get());
1766 value = adoptGRef(jsc_value_object_invoke_method(foo.get(), "getFoo", G_TYPE_NONE));
1767 checker.watch(value.get());
1768 g_assert_true(jsc_value_is_number(value.get()));
1769 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 6);
1770 value = adoptGRef(jsc_context_evaluate(context.get(), "f.multiply()", -1));
1771 checker.watch(value.get());
1772 value = adoptGRef(jsc_value_object_invoke_method(foo.get(), "getFoo", G_TYPE_NONE));
1773 checker.watch(value.get());
1774 g_assert_true(jsc_value_is_number(value.get()));
1775 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 6);
1776
1777 GRefPtr<JSCValue> constructor2 = adoptGRef(jsc_class_add_constructorv(jscClass, "CreateWithFoo", G_CALLBACK(fooCreateWithFoo), nullptr, nullptr, G_TYPE_POINTER, 1, parameterTypes));
1778 checker.watch(constructor2.get());
1779 g_assert_true(jsc_value_is_constructor(constructor2.get()));
1780 jsc_value_object_set_property(constructor.get(), "CreateWithFoo", constructor2.get());
1781
1782 GRefPtr<JSCValue> foo2 = adoptGRef(jsc_context_evaluate(context.get(), "f2 = new Foo.CreateWithFoo(42);", -1));
1783 checker.watch(foo2.get());
1784 g_assert_true(jsc_value_is_object(foo2.get()));
1785 g_assert_true(jsc_value_object_is_instance_of(foo2.get(), jsc_class_get_name(jscClass)));
1786 g_assert_false(foo.get() == foo2.get());
1787 result = adoptGRef(jsc_context_evaluate(context.get(), "f2 instanceof Foo;", -1));
1788 checker.watch(result.get());
1789 g_assert_true(jsc_value_is_boolean(result.get()));
1790 g_assert_true(jsc_value_to_boolean(result.get()));
1791 result = adoptGRef(jsc_context_evaluate(context.get(), "f2 instanceof Foo.CreateWithFoo;", -1));
1792 checker.watch(result.get());
1793 g_assert_true(jsc_value_is_boolean(result.get()));
1794 g_assert_true(jsc_value_to_boolean(result.get()));
1795
1796 g_assert_false(jsc_value_object_has_property(foo.get(), "foo"));
1797 g_assert_false(jsc_value_object_has_property(foo2.get(), "foo"));
1798 jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
1799 g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
1800 g_assert_true(jsc_value_object_has_property(foo2.get(), "foo"));
1801 properties.reset(jsc_value_object_enumerate_properties(foo.get()));
1802 g_assert_null(properties.get());
1803 value = adoptGRef(jsc_context_evaluate(context.get(), "f2.foo", -1));
1804 checker.watch(value.get());
1805 g_assert_true(jsc_value_is_number(value.get()));
1806 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 42);
1807 result = adoptGRef(jsc_context_evaluate(context.get(), "f2.foo = 52", -1));
1808 checker.watch(result.get());
1809 value = adoptGRef(jsc_context_evaluate(context.get(), "f2.foo", -1));
1810 checker.watch(value.get());
1811 g_assert_true(jsc_value_is_number(value.get()));
1812 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 52);
1813
1814 GRefPtr<JSCValue> constructorV = adoptGRef(jsc_class_add_constructor_variadic(jscClass, "CreateWithFoo", G_CALLBACK(fooCreateWithFooV), nullptr, nullptr, G_TYPE_POINTER));
1815 checker.watch(constructorV.get());
1816 g_assert_true(jsc_value_is_constructor(constructorV.get()));
1817 jsc_value_object_set_property(constructor.get(), "CreateWithFooV", constructorV.get());
1818
1819 GRefPtr<JSCValue> foo3 = adoptGRef(jsc_context_evaluate(context.get(), "f3 = new Foo.CreateWithFooV(10,20,30,40);", -1));
1820 checker.watch(foo3.get());
1821 g_assert_true(jsc_value_is_object(foo3.get()));
1822 g_assert_true(jsc_value_object_is_instance_of(foo3.get(), jsc_class_get_name(jscClass)));
1823 value = adoptGRef(jsc_context_evaluate(context.get(), "f3.foo", -1));
1824 checker.watch(value.get());
1825 g_assert_true(jsc_value_is_number(value.get()));
1826 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 100);
1827
1828 GRefPtr<JSCValue> foo4 = adoptGRef(jsc_context_evaluate(context.get(), "f4 = new Foo.CreateWithFooV();", -1));
1829 checker.watch(foo4.get());
1830 g_assert_true(jsc_value_is_object(foo4.get()));
1831 g_assert_true(jsc_value_object_is_instance_of(foo3.get(), jsc_class_get_name(jscClass)));
1832 value = adoptGRef(jsc_context_evaluate(context.get(), "f4.foo", -1));
1833 checker.watch(value.get());
1834 g_assert_true(jsc_value_is_number(value.get()));
1835 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
1836
1837 GFile* file = g_file_new_for_path(".");
1838 checker.watch(file);
1839 GRefPtr<JSCValue> constructorUserData = adoptGRef(jsc_class_add_constructor(jscClass, "CreateWithUserData", G_CALLBACK(fooCreateWithUserData),
1840 file, g_object_unref, G_TYPE_POINTER, 0, G_TYPE_NONE));
1841 checker.watch(constructorUserData.get());
1842 g_assert_true(jsc_value_is_constructor(constructorUserData.get()));
1843 jsc_value_object_set_property(constructor.get(), "CreateWithUserData", constructorUserData.get());
1844
1845 GRefPtr<JSCValue> foo5 = adoptGRef(jsc_context_evaluate(context.get(), "f5 = new Foo.CreateWithUserData();", -1));
1846 checker.watch(foo5.get());
1847 g_assert_true(jsc_value_is_object(foo5.get()));
1848 g_assert_true(jsc_value_object_is_instance_of(foo5.get(), jsc_class_get_name(jscClass)));
1849
1850 JSCClass* otherClass = jsc_context_register_class(context.get(), "Baz", nullptr, nullptr, g_free);
1851 checker.watch(otherClass);
1852 g_assert_false(jsc_class_get_parent(otherClass));
1853
1854 GRefPtr<JSCValue> constructor3 = adoptGRef(jsc_class_add_constructor(otherClass, nullptr, G_CALLBACK(bazCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
1855 checker.watch(constructor3.get());
1856 g_assert_true(jsc_value_is_constructor(constructor3.get()));
1857 jsc_context_set_value(context.get(), jsc_class_get_name(otherClass), constructor3.get());
1858
1859 g_assert_false(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(otherClass)));
1860 g_assert_false(jsc_value_object_is_instance_of(foo2.get(), jsc_class_get_name(otherClass)));
1861
1862 GRefPtr<JSCValue> baz = adoptGRef(jsc_value_constructor_call(constructor3.get(), G_TYPE_NONE));
1863 checker.watch(baz.get());
1864 g_assert_true(jsc_value_is_object(baz.get()));
1865 g_assert_true(jsc_value_object_is_instance_of(baz.get(), jsc_class_get_name(otherClass)));
1866 g_assert_false(jsc_value_object_is_instance_of(baz.get(), jsc_class_get_name(jscClass)));
1867 }
1868
1869 {
1870 LeakChecker checker;
1871 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
1872 checker.watch(context.get());
1873 ExceptionHandler exceptionHandler(context.get());
1874
1875 JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
1876 checker.watch(jscClass);
1877
1878 GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
1879 checker.watch(constructor.get());
1880 g_assert_true(jsc_value_is_constructor(constructor.get()));
1881 jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
1882 jsc_class_add_property(jscClass, "sibling", G_TYPE_POINTER, G_CALLBACK(getSibling), G_CALLBACK(setSibling), nullptr, nullptr);
1883
1884 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f1 = new Foo(); f2 = new Foo(); f2.sibling = f1", -1));
1885 checker.watch(result.get());
1886
1887 GRefPtr<JSCValue> f1 = adoptGRef(jsc_context_get_value(context.get(), "f1"));
1888 checker.watch(f1.get());
1889 g_assert_true(jsc_value_is_object(f1.get()));
1890 g_assert_true(jsc_value_object_has_property(f1.get(), "sibling"));
1891 GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(f1.get()));
1892 g_assert_null(properties.get());
1893 GRefPtr<JSCValue> f2 = adoptGRef(jsc_context_get_value(context.get(), "f2"));
1894 checker.watch(f2.get());
1895 g_assert_true(jsc_value_is_object(f2.get()));
1896 g_assert_true(jsc_value_object_has_property(f2.get(), "sibling"));
1897 properties.reset(jsc_value_object_enumerate_properties(f2.get()));
1898 g_assert_null(properties.get());
1899
1900 GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "f2.sibling", -1));
1901 checker.watch(value.get());
1902 g_assert_true(value.get() == f1.get());
1903
1904 jsc_value_object_set_property(f1.get(), "sibling", f2.get());
1905 value = adoptGRef(jsc_context_evaluate(context.get(), "f1.sibling", -1));
1906 checker.watch(value.get());
1907 g_assert_true(value.get() == f2.get());
1908 }
1909
1910 {
1911 LeakChecker checker;
1912 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
1913 checker.watch(context.get());
1914 ExceptionHandler exceptionHandler(context.get());
1915
1916 JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
1917 checker.watch(jscClass);
1918
1919 GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructorv(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, nullptr));
1920 checker.watch(constructor.get());
1921 g_assert_true(jsc_value_is_constructor(constructor.get()));
1922 jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
1923 jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
1924
1925 Foo* f = fooCreateWithFoo(25);
1926 GRefPtr<JSCValue> foo = adoptGRef(jsc_value_new_object(context.get(), f, jscClass));
1927 checker.watch(foo.get());
1928 g_assert_true(jsc_value_is_object(foo.get()));
1929 g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(jscClass)));
1930 g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
1931 GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
1932 g_assert_null(properties.get());
1933
1934 jsc_context_set_value(context.get(), "f", foo.get());
1935 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof Foo;", -1));
1936 checker.watch(result.get());
1937 g_assert_true(jsc_value_is_boolean(result.get()));
1938 g_assert_true(jsc_value_to_boolean(result.get()));
1939
1940 result = adoptGRef(jsc_context_evaluate(context.get(), "f.foo", -1));
1941 checker.watch(result.get());
1942 g_assert_true(jsc_value_is_number(result.get()));
1943 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 25);
1944
1945 setFoo(f, 42);
1946 result = adoptGRef(jsc_context_evaluate(context.get(), "f.foo", -1));
1947 checker.watch(result.get());
1948 g_assert_true(jsc_value_is_number(result.get()));
1949 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 42);
1950
1951 JSCClass* bazClass = jsc_context_register_class(context.get(), "Baz", nullptr, nullptr, g_free);
1952 checker.watch(bazClass);
1953
1954 GRefPtr<JSCValue> constructor2 = adoptGRef(jsc_class_add_constructor(bazClass, nullptr, G_CALLBACK(bazCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
1955 checker.watch(constructor2.get());
1956 g_assert_true(jsc_value_is_constructor(constructor2.get()));
1957 jsc_context_set_value(context.get(), jsc_class_get_name(bazClass), constructor2.get());
1958
1959 Baz* bz = bazCreate();
1960 GRefPtr<JSCValue> baz = adoptGRef(jsc_value_new_object(context.get(), bz, bazClass));
1961 checker.watch(baz.get());
1962 g_assert_true(jsc_value_is_object(baz.get()));
1963 g_assert_true(jsc_value_object_is_instance_of(baz.get(), jsc_class_get_name(bazClass)));
1964 g_assert_false(jsc_value_object_is_instance_of(baz.get(), jsc_class_get_name(jscClass)));
1965 g_assert_false(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(bazClass)));
1966 g_assert_false(jsc_value_object_has_property(baz.get(), "foo"));
1967
1968 jsc_context_set_value(context.get(), "bz", baz.get());
1969 result = adoptGRef(jsc_context_evaluate(context.get(), "bz instanceof Baz;", -1));
1970 checker.watch(result.get());
1971 g_assert_true(jsc_value_is_boolean(result.get()));
1972 g_assert_true(jsc_value_to_boolean(result.get()));
1973
1974 result = adoptGRef(jsc_context_evaluate(context.get(), "bz instanceof Foo;", -1));
1975 checker.watch(result.get());
1976 g_assert_true(jsc_value_is_boolean(result.get()));
1977 g_assert_false(jsc_value_to_boolean(result.get()));
1978
1979 result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof Baz;", -1));
1980 checker.watch(result.get());
1981 g_assert_true(jsc_value_is_boolean(result.get()));
1982 g_assert_false(jsc_value_to_boolean(result.get()));
1983
1984 jsc_value_object_set_property(baz.get(), "foo", foo.get());
1985 result = adoptGRef(jsc_context_evaluate(context.get(), "bz.foo", -1));
1986 checker.watch(result.get());
1987 g_assert_true(result.get() == foo.get());
1988 }
1989
1990 {
1991 LeakChecker checker;
1992 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
1993 checker.watch(context.get());
1994 ExceptionHandler exceptionHandler(context.get());
1995
1996 JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
1997 checker.watch(jscClass);
1998
1999 GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
2000 checker.watch(constructor.get());
2001 g_assert_true(jsc_value_is_constructor(constructor.get()));
2002 jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
2003 jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
2004
2005 Foo* f = fooCreateWithFoo(25);
2006 GRefPtr<JSCValue> foo = adoptGRef(jsc_value_new_object(context.get(), f, jscClass));
2007 checker.watch(foo.get());
2008 g_assert_true(jsc_value_is_object(foo.get()));
2009 g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(jscClass)));
2010 g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
2011 GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
2012 g_assert_null(properties.get());
2013
2014 jsc_context_set_value(context.get(), "f1", foo.get());
2015 jsc_context_set_value(context.get(), "f2", foo.get());
2016 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f1 === f2;", -1));
2017 checker.watch(result.get());
2018 g_assert_true(jsc_value_is_boolean(result.get()));
2019 g_assert_true(jsc_value_to_boolean(result.get()));
2020
2021 GRefPtr<JSCValue> property = adoptGRef(jsc_value_new_number(context.get(), 50));
2022 checker.watch(property.get());
2023 g_assert_false(jsc_value_object_has_property(foo.get(), "n"));
2024 jsc_value_object_set_property(foo.get(), "n", property.get());
2025 g_assert_true(jsc_value_object_has_property(foo.get(), "n"));
2026 result = adoptGRef(jsc_context_evaluate(context.get(), "f1.n", -1));
2027 checker.watch(result.get());
2028 g_assert_true(jsc_value_is_number(result.get()));
2029 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 50);
2030 }
2031
2032 {
2033 LeakChecker checker;
2034 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2035 checker.watch(context.get());
2036 ExceptionHandler exceptionHandler(context.get());
2037
2038 JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
2039 checker.watch(jscClass);
2040 g_assert_false(jsc_class_get_parent(jscClass));
2041
2042 GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
2043 checker.watch(constructor.get());
2044 g_assert_true(jsc_value_is_constructor(constructor.get()));
2045 jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
2046 jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
2047 GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1));
2048 checker.watch(foo.get());
2049 g_assert_true(jsc_value_is_object(foo.get()));
2050 g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(jscClass)));
2051 g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
2052 GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
2053 g_assert_null(properties.get());
2054
2055 GRefPtr<JSCValue> value = adoptGRef(jsc_value_new_number(context.get(), 125));
2056 checker.watch(value.get());
2057 jsc_value_object_set_property(foo.get(), "foo", value.get());
2058
2059 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f.__lookupGetter__('foo').call(f)", -1));
2060 checker.watch(result.get());
2061 g_assert_true(value.get() == result.get());
2062
2063 bool didThrow = false;
2064 g_assert_throw_begin(exceptionHandler, didThrow);
2065 result = adoptGRef(jsc_context_evaluate(context.get(), "f.__lookupGetter__('foo').call({})", -1));
2066 checker.watch(result.get());
2067 g_assert_true(jsc_value_is_undefined(result.get()));
2068 g_assert_did_throw(exceptionHandler, didThrow);
2069 }
2070
2071 {
2072 LeakChecker checker;
2073 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2074 checker.watch(context.get());
2075 ExceptionHandler exceptionHandler(context.get());
2076
2077 JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
2078 checker.watch(jscClass);
2079 g_assert_false(jsc_class_get_parent(jscClass));
2080
2081 GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_NONE, 0, G_TYPE_NONE));
2082 checker.watch(constructor.get());
2083 g_assert_true(jsc_value_is_constructor(constructor.get()));
2084 jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
2085 bool didThrow = false;
2086 g_assert_throw_begin(exceptionHandler, didThrow);
2087 GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1));
2088 checker.watch(foo.get());
2089 g_assert_true(jsc_value_is_undefined(foo.get()));
2090 g_assert_did_throw(exceptionHandler, didThrow);
2091 }
2092
2093 {
2094 LeakChecker checker;
2095 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2096 checker.watch(context.get());
2097 ExceptionHandler exceptionHandler(context.get());
2098
2099 GRefPtr<JSCValue> jsNamespace = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr));
2100 checker.watch(jsNamespace.get());
2101 g_assert_true(jsc_value_is_object(jsNamespace.get()));
2102
2103 jsc_context_set_value(context.get(), "wk", jsNamespace.get());
2104
2105 JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
2106 checker.watch(jscClass);
2107
2108 GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
2109 checker.watch(constructor.get());
2110 g_assert_true(jsc_value_is_constructor(constructor.get()));
2111 jsc_value_object_set_property(jsNamespace.get(), jsc_class_get_name(jscClass), constructor.get());
2112 jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
2113
2114 bool didThrow = false;
2115 g_assert_throw_begin(exceptionHandler, didThrow);
2116 GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1));
2117 checker.watch(foo.get());
2118 g_assert_true(jsc_value_is_undefined(foo.get()));
2119 g_assert_did_throw(exceptionHandler, didThrow);
2120
2121 foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new wk.Foo();", -1));
2122 g_assert_true(jsc_value_is_object(foo.get()));
2123 g_assert_true(jsc_value_object_is_instance_of(foo.get(), "wk.Foo"));
2124 g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
2125 GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
2126 g_assert_null(properties.get());
2127 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof wk.Foo;", -1));
2128 checker.watch(result.get());
2129 g_assert_true(jsc_value_is_boolean(result.get()));
2130 g_assert_true(jsc_value_to_boolean(result.get()));
2131
2132 GRefPtr<JSCValue> constructor2 = adoptGRef(jsc_class_add_constructor(jscClass, "CreateWithFoo", G_CALLBACK(fooCreateWithFoo), nullptr, nullptr, G_TYPE_POINTER, 1, G_TYPE_INT));
2133 checker.watch(constructor2.get());
2134 g_assert_true(jsc_value_is_constructor(constructor2.get()));
2135 jsc_value_object_set_property(constructor.get(), "CreateWithFoo", constructor2.get());
2136
2137 g_assert_throw_begin(exceptionHandler, didThrow);
2138 GRefPtr<JSCValue> foo2 = adoptGRef(jsc_context_evaluate(context.get(), "f2 = new Foo.CreateWithFoo(42);", -1));
2139 checker.watch(foo2.get());
2140 g_assert_true(jsc_value_is_undefined(foo2.get()));
2141 g_assert_did_throw(exceptionHandler, didThrow);
2142
2143 foo2 = adoptGRef(jsc_context_evaluate(context.get(), "f2 = new wk.Foo.CreateWithFoo(42);", -1));
2144 checker.watch(foo2.get());
2145 g_assert_true(jsc_value_is_object(foo2.get()));
2146 g_assert_true(jsc_value_object_is_instance_of(foo2.get(), "wk.Foo"));
2147 g_assert_true(jsc_value_object_is_instance_of(foo2.get(), "wk.Foo.CreateWithFoo"));
2148 g_assert_false(foo.get() == foo2.get());
2149 result = adoptGRef(jsc_context_evaluate(context.get(), "f2 instanceof wk.Foo;", -1));
2150 checker.watch(result.get());
2151 g_assert_true(jsc_value_is_boolean(result.get()));
2152 g_assert_true(jsc_value_to_boolean(result.get()));
2153 result = adoptGRef(jsc_context_evaluate(context.get(), "f2 instanceof wk.Foo.CreateWithFoo;", -1));
2154 checker.watch(result.get());
2155 g_assert_true(jsc_value_is_boolean(result.get()));
2156 g_assert_true(jsc_value_to_boolean(result.get()));
2157 result = adoptGRef(jsc_context_evaluate(context.get(), "f2.foo", -1));
2158 checker.watch(result.get());
2159 g_assert_true(jsc_value_is_number(result.get()));
2160 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 42);
2161
2162 GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
2163 auto* parameter = jsc_value_new_number(context.get(), 62);
2164 checker.watch(parameter);
2165 g_ptr_array_add(parameters.get(), parameter);
2166
2167 GRefPtr<JSCValue> foo3 = adoptGRef(jsc_value_constructor_callv(constructor2.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
2168 checker.watch(foo3.get());
2169 g_assert_true(jsc_value_is_object(foo3.get()));
2170 g_assert_true(jsc_value_object_is_instance_of(foo3.get(), "wk.Foo"));
2171 g_assert_true(jsc_value_object_is_instance_of(foo3.get(), "wk.Foo.CreateWithFoo"));
2172 g_assert_false(foo2.get() == foo3.get());
2173 jsc_context_set_value(context.get(), "f3", foo3.get());
2174 result = adoptGRef(jsc_context_evaluate(context.get(), "f3 instanceof wk.Foo;", -1));
2175 checker.watch(result.get());
2176 g_assert_true(jsc_value_is_boolean(result.get()));
2177 g_assert_true(jsc_value_to_boolean(result.get()));
2178 result = adoptGRef(jsc_context_evaluate(context.get(), "f3 instanceof wk.Foo.CreateWithFoo;", -1));
2179 checker.watch(result.get());
2180 g_assert_true(jsc_value_is_boolean(result.get()));
2181 g_assert_true(jsc_value_to_boolean(result.get()));
2182 result = adoptGRef(jsc_context_evaluate(context.get(), "f3.foo", -1));
2183 checker.watch(result.get());
2184 g_assert_true(jsc_value_is_number(result.get()));
2185 g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 62);
2186 }
2187
2188 {
2189 LeakChecker checker;
2190 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2191 checker.watch(context.get());
2192 ExceptionHandler exceptionHandler(context.get());
2193
2194 JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, &fooVTable, reinterpret_cast<GDestroyNotify>(fooFree));
2195 checker.watch(jscClass);
2196 g_object_set_data(G_OBJECT(jscClass), "leak-checker", &checker);
2197
2198 GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
2199 checker.watch(constructor.get());
2200 g_assert_true(jsc_value_is_constructor(constructor.get()));
2201 jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
2202 jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
2203
2204 GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1));
2205 checker.watch(foo.get());
2206 g_assert_true(jsc_value_is_object(foo.get()));
2207 g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
2208
2209 g_assert_true(jsc_value_object_has_property(foo.get(), "prop_whatever"));
2210 g_assert_false(jsc_value_object_has_property(foo.get(), "whatever_prop"));
2211
2212 GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
2213 g_assert_null(properties.get());
2214
2215 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_1", -1));
2216 checker.watch(result.get());
2217 g_assert_true(jsc_value_is_number(result.get()));
2218 g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 0);
2219
2220 GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_get_property(foo.get(), "prop_1"));
2221 checker.watch(value.get());
2222 g_assert_true(value.get() == result.get());
2223
2224 result = adoptGRef(jsc_context_evaluate(context.get(), "f.foo", -1));
2225 checker.watch(result.get());
2226 g_assert_true(jsc_value_is_number(result.get()));
2227 g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 0);
2228
2229 value = adoptGRef(jsc_value_object_get_property(foo.get(), "foo"));
2230 checker.watch(value.get());
2231 g_assert_true(value.get() == result.get());
2232
2233 result = adoptGRef(jsc_context_evaluate(context.get(), "'foo' in f.__proto__", -1));
2234 checker.watch(result.get());
2235 g_assert_true(jsc_value_is_boolean(result.get()));
2236 g_assert_true(jsc_value_to_boolean(result.get()));
2237
2238 result = adoptGRef(jsc_context_evaluate(context.get(), "'foo' in f", -1));
2239 checker.watch(result.get());
2240 g_assert_true(jsc_value_is_boolean(result.get()));
2241 g_assert_true(jsc_value_to_boolean(result.get()));
2242
2243 result = adoptGRef(jsc_context_evaluate(context.get(), "'prop_1' in f.__proto__", -1));
2244 checker.watch(result.get());
2245 g_assert_true(jsc_value_is_boolean(result.get()));
2246 g_assert_false(jsc_value_to_boolean(result.get()));
2247
2248 result = adoptGRef(jsc_context_evaluate(context.get(), "'prop_1' in f", -1));
2249 checker.watch(result.get());
2250 g_assert_true(jsc_value_is_boolean(result.get()));
2251 g_assert_true(jsc_value_to_boolean(result.get()));
2252
2253 result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_1 = 25", -1));
2254 checker.watch(result.get());
2255 g_assert_true(jsc_value_is_number(result.get()));
2256 g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 25);
2257
2258 g_assert_true(jsc_value_object_delete_property(foo.get(), "prop_1"));
2259 g_assert_true(jsc_value_object_has_property(foo.get(), "prop_1"));
2260 value = adoptGRef(jsc_value_object_get_property(foo.get(), "prop_1"));
2261 checker.watch(value.get());
2262 g_assert_true(jsc_value_is_number(value.get()));
2263 g_assert_cmpuint(jsc_value_to_int32(value.get()), ==, 0);
2264
2265 result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_cant_delete = 125", -1));
2266 checker.watch(result.get());
2267 g_assert_true(jsc_value_is_number(result.get()));
2268 g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 125);
2269 jsc_value_object_delete_property(foo.get(), "prop_cant_delete");
2270 g_assert_true(jsc_value_object_has_property(foo.get(), "prop_cant_delete"));
2271 value = adoptGRef(jsc_value_object_get_property(foo.get(), "prop_cant_delete"));
2272 checker.watch(value.get());
2273 g_assert_true(jsc_value_is_number(value.get()));
2274 g_assert_cmpuint(jsc_value_to_int32(value.get()), ==, 125);
2275
2276 value = adoptGRef(jsc_value_new_number(context.get(), 42));
2277 checker.watch(value.get());
2278 jsc_value_object_set_property(foo.get(), "prop_1", value.get());
2279 result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_1", -1));
2280 checker.watch(result.get());
2281 g_assert_true(value.get() == result.get());
2282
2283 result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_2 = 35", -1));
2284 checker.watch(result.get());
2285 g_assert_true(jsc_value_is_number(result.get()));
2286 g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 35);
2287
2288 result = adoptGRef(jsc_context_evaluate(context.get(), "'prop_2' in f.__proto__", -1));
2289 checker.watch(result.get());
2290 g_assert_true(jsc_value_is_boolean(result.get()));
2291 g_assert_false(jsc_value_to_boolean(result.get()));
2292
2293 result = adoptGRef(jsc_context_evaluate(context.get(), "'prop_2' in f", -1));
2294 checker.watch(result.get());
2295 g_assert_true(jsc_value_is_boolean(result.get()));
2296 g_assert_true(jsc_value_to_boolean(result.get()));
2297
2298 result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_enum_1 = 250", -1));
2299 checker.watch(result.get());
2300 g_assert_true(jsc_value_is_number(result.get()));
2301 g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 250);
2302 result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_enum_2 = 450", -1));
2303 checker.watch(result.get());
2304 g_assert_true(jsc_value_is_number(result.get()));
2305 g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 450);
2306
2307 properties.reset(jsc_value_object_enumerate_properties(foo.get()));
2308 g_assert_cmpuint(g_strv_length(properties.get()), ==, 2);
2309 g_assert_cmpstr(properties.get()[0], ==, "prop_enum_1");
2310 g_assert_cmpstr(properties.get()[1], ==, "prop_enum_2");
2311 g_assert_null(properties.get()[2]);
2312
2313 g_assert_null(jsc_context_get_exception(context.get()));
2314 bool didThrow = false;
2315 g_assert_throw_begin(exceptionHandler, didThrow);
2316 result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_throw_on_get", -1));
2317 checker.watch(result.get());
2318 g_assert_true(jsc_value_is_undefined(result.get()));
2319 g_assert_did_throw(exceptionHandler, didThrow);
2320 g_assert_null(jsc_context_get_exception(context.get()));
2321
2322 didThrow = false;
2323 g_assert_throw_begin(exceptionHandler, didThrow);
2324 result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_3 = 'not a number'", -1));
2325 checker.watch(result.get());
2326 g_assert_true(jsc_value_is_undefined(result.get()));
2327 g_assert_did_throw(exceptionHandler, didThrow);
2328 g_assert_null(jsc_context_get_exception(context.get()));
2329
2330 didThrow = false;
2331 g_assert_throw_begin(exceptionHandler, didThrow);
2332 jsc_value_object_delete_property(foo.get(), "prop_throw_on_delete");
2333 g_assert_did_throw(exceptionHandler, didThrow);
2334 g_assert_null(jsc_context_get_exception(context.get()));
2335
2336 jsc_context_throw(context.get(), "Fake exception");
2337 GRefPtr<JSCException> previousException = jsc_context_get_exception(context.get());
2338 checker.watch(previousException.get());
2339 didThrow = false;
2340 g_assert_throw_begin(exceptionHandler, didThrow);
2341 result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_throw_on_get", -1));
2342 checker.watch(result.get());
2343 g_assert_true(jsc_value_is_undefined(result.get()));
2344 g_assert_did_throw(exceptionHandler, didThrow);
2345 g_assert_true(jsc_context_get_exception(context.get()) == previousException.get());
2346
2347 didThrow = false;
2348 g_assert_throw_begin(exceptionHandler, didThrow);
2349 result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_3 = 'not a number'", -1));
2350 checker.watch(result.get());
2351 g_assert_true(jsc_value_is_undefined(result.get()));
2352 g_assert_did_throw(exceptionHandler, didThrow);
2353 g_assert_true(jsc_context_get_exception(context.get()) == previousException.get());
2354
2355 didThrow = false;
2356 g_assert_throw_begin(exceptionHandler, didThrow);
2357 jsc_value_object_delete_property(foo.get(), "prop_throw_on_delete");
2358 g_assert_did_throw(exceptionHandler, didThrow);
2359 g_assert_true(jsc_context_get_exception(context.get()) == previousException.get());
2360 }
2361
2362 {
2363 LeakChecker checker;
2364 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2365 checker.watch(context.get());
2366 g_object_set_data(G_OBJECT(context.get()), "leak-checker", &checker);
2367 ExceptionHandler exceptionHandler(context.get());
2368
2369 JSCClass* jscClass = jsc_context_register_class(context.get(), "GFile", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(g_object_unref));
2370 checker.watch(jscClass);
2371
2372 GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(createGFile), nullptr, nullptr, G_TYPE_OBJECT, 1, G_TYPE_STRING));
2373 checker.watch(constructor.get());
2374 g_assert_true(jsc_value_is_constructor(constructor.get()));
2375 jsc_class_add_method(jscClass, "getPath", G_CALLBACK(g_file_get_path), nullptr, nullptr, G_TYPE_STRING, 0, G_TYPE_NONE);
2376
2377 jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
2378
2379 GRefPtr<JSCValue> file = adoptGRef(jsc_context_evaluate(context.get(), "f = new GFile('.');", -1));
2380 checker.watch(file.get());
2381 g_assert_true(jsc_value_is_object(file.get()));
2382 g_assert_true(jsc_value_object_is_instance_of(file.get(), jsc_class_get_name(jscClass)));
2383 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof GFile;", -1));
2384 checker.watch(result.get());
2385 g_assert_true(jsc_value_is_boolean(result.get()));
2386 g_assert_true(jsc_value_to_boolean(result.get()));
2387
2388 g_assert_true(jsc_value_object_has_property(file.get(), "getPath"));
2389 GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_invoke_method(file.get(), "getPath", G_TYPE_NONE));
2390 checker.watch(value.get());
2391 g_assert_true(jsc_value_is_string(value.get()));
2392 GUniquePtr<char> resultString(jsc_value_to_string(value.get()));
2393 GUniquePtr<char> currentDirectory(g_get_current_dir());
2394 g_assert_cmpstr(resultString.get(), ==, currentDirectory.get());
2395
2396 GRefPtr<JSCValue> value2 = adoptGRef(jsc_context_evaluate(context.get(), "f.getPath('.');", -1));
2397 checker.watch(value2.get());
2398 g_assert_true(jsc_value_is_string(value2.get()));
2399 resultString.reset(jsc_value_to_string(value2.get()));
2400 g_assert_cmpstr(resultString.get(), ==, currentDirectory.get());
2401
2402 jsc_class_add_method(jscClass, "getGFile", G_CALLBACK(getGFile), nullptr, nullptr, G_TYPE_OBJECT, 0, G_TYPE_NONE);
2403 result = adoptGRef(jsc_context_evaluate(context.get(), "f = new GFile('.'); f2 = f.getGFile(); f2.getPath()", -1));
2404 checker.watch(result.get());
2405 g_assert_true(jsc_value_is_string(result.get()));
2406 resultString.reset(jsc_value_to_string(result.get()));
2407 g_assert_cmpstr(resultString.get(), ==, currentDirectory.get());
2408
2409 value = adoptGRef(jsc_value_object_invoke_method(file.get(), "getGFile", G_TYPE_NONE));
2410 checker.watch(value.get());
2411 g_assert_true(value.get() == file.get());
2412
2413 jsc_class_add_method(jscClass, "getParent", G_CALLBACK(getParent), jscClass, nullptr, JSC_TYPE_VALUE, 0, G_TYPE_NONE);
2414 result = adoptGRef(jsc_context_evaluate(context.get(), "f = new GFile('.'); p = f.getParent(); p.getPath()", -1));
2415 checker.watch(result.get());
2416 g_assert_true(jsc_value_is_string(result.get()));
2417 resultString.reset(jsc_value_to_string(result.get()));
2418 GUniquePtr<char> parentDirectory(g_path_get_dirname(currentDirectory.get()));
2419 g_assert_cmpstr(resultString.get(), ==, parentDirectory.get());
2420
2421 jsc_class_add_method(jscClass, "equal", G_CALLBACK(g_file_equal), nullptr, nullptr, G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
2422 result = adoptGRef(jsc_context_evaluate(context.get(), "f1 = new GFile('.'); f2 = new GFile('.'); f1.equal(f2);", -1));
2423 checker.watch(result.get());
2424 g_assert_true(jsc_value_is_boolean(result.get()));
2425 g_assert_true(jsc_value_to_boolean(result.get()));
2426
2427 GFile* fileObject = g_file_new_for_path(".");
2428 checker.watch(fileObject);
2429 GRefPtr<JSCValue> fileValue = adoptGRef(jsc_value_new_object(context.get(), fileObject, jscClass));
2430 checker.watch(fileValue.get());
2431
2432 result = adoptGRef(jsc_value_object_invoke_method(file.get(), "equal", G_TYPE_OBJECT, fileObject, G_TYPE_NONE));
2433 checker.watch(result.get());
2434 g_assert_true(jsc_value_is_boolean(result.get()));
2435 g_assert_true(jsc_value_to_boolean(result.get()));
2436
2437 result = adoptGRef(jsc_value_object_invoke_method(file.get(), "equal", JSC_TYPE_VALUE, fileValue.get(), G_TYPE_NONE));
2438 checker.watch(result.get());
2439 g_assert_true(jsc_value_is_boolean(result.get()));
2440 g_assert_true(jsc_value_to_boolean(result.get()));
2441 }
2442
2443 {
2444 LeakChecker checker;
2445 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2446 checker.watch(context.get());
2447 g_object_set_data(G_OBJECT(context.get()), "leak-checker", &checker);
2448 ExceptionHandler exceptionHandler(context.get());
2449
2450 JSCClass* jscClass = jsc_context_register_class(context.get(), "GString", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(freeGString));
2451 checker.watch(jscClass);
2452
2453 GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(createGString), nullptr, nullptr, G_TYPE_GSTRING, 1, G_TYPE_STRING));
2454 checker.watch(constructor.get());
2455 g_assert_true(jsc_value_is_constructor(constructor.get()));
2456
2457 jsc_class_add_property(jscClass, "str", G_TYPE_STRING, G_CALLBACK(getGStringStr), nullptr, nullptr, nullptr);
2458 jsc_class_add_property(jscClass, "len", G_TYPE_UINT64, G_CALLBACK(getGStringLen), nullptr, nullptr, nullptr);
2459
2460 jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
2461
2462 GRefPtr<JSCValue> str = adoptGRef(jsc_context_evaluate(context.get(), "s = new GString('Foo');", -1));
2463 checker.watch(str.get());
2464 g_assert_true(jsc_value_is_object(str.get()));
2465 g_assert_true(jsc_value_object_is_instance_of(str.get(), jsc_class_get_name(jscClass)));
2466 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "s instanceof GString;", -1));
2467 checker.watch(result.get());
2468 g_assert_true(jsc_value_is_boolean(result.get()));
2469 g_assert_true(jsc_value_to_boolean(result.get()));
2470
2471 g_assert_true(jsc_value_object_has_property(str.get(), "str"));
2472 GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_get_property(str.get(), "str"));
2473 checker.watch(value.get());
2474 g_assert_true(jsc_value_is_string(value.get()));
2475 GUniquePtr<char> resultString(jsc_value_to_string(value.get()));
2476 g_assert_cmpstr(resultString.get(), ==, "Foo");
2477
2478 GRefPtr<JSCValue> value2 = adoptGRef(jsc_context_evaluate(context.get(), "s.str", -1));
2479 checker.watch(value2.get());
2480 g_assert_true(jsc_value_is_string(value2.get()));
2481 resultString.reset(jsc_value_to_string(value2.get()));
2482 g_assert_cmpstr(resultString.get(), ==, "Foo");
2483
2484 GRefPtr<JSCValue> value3 = adoptGRef(jsc_context_evaluate(context.get(), "s.len", -1));
2485 checker.watch(value3.get());
2486 g_assert_true(jsc_value_is_number(value3.get()));
2487 g_assert_cmpint(jsc_value_to_int32(value3.get()), ==, 3);
2488
2489 jsc_class_add_method(jscClass, "getGString", G_CALLBACK(getGString), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE);
2490 result = adoptGRef(jsc_context_evaluate(context.get(), "s = new GString('Self'); s2 = s.getGString(); s2.str;", -1));
2491 checker.watch(result.get());
2492 g_assert_true(jsc_value_is_string(result.get()));
2493 resultString.reset(jsc_value_to_string(result.get()));
2494 g_assert_cmpstr(resultString.get(), ==, "Self");
2495
2496 jsc_class_add_method(jscClass, "getGStringCopy", G_CALLBACK(getGStringCopy), jscClass, nullptr, JSC_TYPE_VALUE, 0, G_TYPE_NONE);
2497 result = adoptGRef(jsc_context_evaluate(context.get(), "s = new GString('Copy'); s2 = s.getGStringCopy(); s2.str;", -1));
2498 checker.watch(result.get());
2499 g_assert_true(jsc_value_is_string(result.get()));
2500 resultString.reset(jsc_value_to_string(result.get()));
2501 g_assert_cmpstr(resultString.get(), ==, "Copy");
2502
2503 jsc_class_add_method(jscClass, "getGStringCopyWillRaise", G_CALLBACK(getGStringCopyWillRaise), nullptr, nullptr, G_TYPE_GSTRING, 0, G_TYPE_NONE);
2504 bool didThrow = false;
2505 g_assert_throw_begin(exceptionHandler, didThrow);
2506 result = adoptGRef(jsc_context_evaluate(context.get(), "s = new GString('Copy'); s2 = s.getGStringCopyWillRaise(); s2.str;", -1));
2507 checker.watch(result.get());
2508 g_assert_true(jsc_value_is_undefined(result.get()));
2509 g_assert_did_throw(exceptionHandler, didThrow);
2510
2511 jsc_class_add_method(jscClass, "equal", G_CALLBACK(g_string_equal), nullptr, nullptr, G_TYPE_BOOLEAN, 1, G_TYPE_GSTRING);
2512 result = adoptGRef(jsc_context_evaluate(context.get(), "s1 = new GString('Bar'); s2 = new GString('Bar'); s1.equal(s2);", -1));
2513 checker.watch(result.get());
2514 g_assert_true(jsc_value_is_boolean(result.get()));
2515 g_assert_true(jsc_value_to_boolean(result.get()));
2516
2517 GString* strBoxed = g_string_new("Foo");
2518 GRefPtr<JSCValue> strValue = adoptGRef(jsc_value_new_object(context.get(), strBoxed, jscClass));
2519 checker.watch(strValue.get());
2520
2521 result = adoptGRef(jsc_value_object_invoke_method(str.get(), "equal", G_TYPE_GSTRING, strBoxed, G_TYPE_NONE));
2522 checker.watch(result.get());
2523 g_assert_true(jsc_value_is_boolean(result.get()));
2524 g_assert_true(jsc_value_to_boolean(result.get()));
2525
2526 result = adoptGRef(jsc_value_object_invoke_method(str.get(), "equal", JSC_TYPE_VALUE, strValue.get(), G_TYPE_NONE));
2527 checker.watch(result.get());
2528 g_assert_true(jsc_value_is_boolean(result.get()));
2529 g_assert_true(jsc_value_to_boolean(result.get()));
2530 }
2531}
2532
2533typedef struct {
2534 Foo parent;
2535 int bar;
2536} Bar;
2537
2538static Bar* barCreate()
2539{
2540 Bar* bar = g_new0(Bar, 1);
2541 new (bar) Bar();
2542 return bar;
2543}
2544
2545static void barFree(Bar* bar)
2546{
2547 bar->~Bar();
2548 g_free(bar);
2549}
2550
2551static void setBar(Bar* bar, int value)
2552{
2553 bar->bar = value;
2554}
2555
2556static int getBar(Bar* bar)
2557{
2558 return bar->bar;
2559}
2560
2561static JSCClassVTable barVTable = {
2562 // get_property
2563 nullptr,
2564 // set_property
2565 nullptr,
2566 // has_property
2567 nullptr,
2568 // delete_property
2569 nullptr,
2570 // enumerate_properties
2571 [](JSCClass* jscClass, JSCContext* context, gpointer instance) -> char** {
2572 auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
2573 checker->watch(context);
2574
2575 auto* properties = static_cast<char**>(g_malloc0(2 * sizeof(char*)));
2576 properties[0] = g_strdup("bar");
2577 return properties;
2578 },
2579 // padding
2580 nullptr, nullptr, nullptr, nullptr
2581};
2582
2583static void testJSCPrototypes()
2584{
2585 {
2586 LeakChecker checker;
2587 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2588 checker.watch(context.get());
2589 ExceptionHandler exceptionHandler(context.get());
2590
2591 JSCClass* fooClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
2592 checker.watch(fooClass);
2593 g_assert_false(jsc_class_get_parent(fooClass));
2594 GRefPtr<JSCValue> fooConstructor = adoptGRef(jsc_class_add_constructor(fooClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
2595 checker.watch(fooConstructor.get());
2596 g_assert_true(jsc_value_is_constructor(fooConstructor.get()));
2597 jsc_context_set_value(context.get(), jsc_class_get_name(fooClass), fooConstructor.get());
2598 jsc_class_add_method(fooClass, "multiply", G_CALLBACK(multiplyFoo), nullptr, nullptr, G_TYPE_NONE, 1, G_TYPE_INT);
2599 jsc_class_add_property(fooClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
2600
2601 JSCClass* barClass = jsc_context_register_class(context.get(), "Bar", fooClass, nullptr, reinterpret_cast<GDestroyNotify>(barFree));
2602 checker.watch(barClass);
2603 g_assert_true(jsc_class_get_parent(barClass) == fooClass);
2604 GRefPtr<JSCValue> barConstructor = adoptGRef(jsc_class_add_constructor(barClass, nullptr, G_CALLBACK(barCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
2605 checker.watch(barConstructor.get());
2606 g_assert_true(jsc_value_is_constructor(barConstructor.get()));
2607 jsc_context_set_value(context.get(), jsc_class_get_name(barClass), barConstructor.get());
2608 jsc_class_add_property(barClass, "bar", G_TYPE_INT, G_CALLBACK(getBar), G_CALLBACK(setBar), nullptr, nullptr);
2609
2610 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo(); b = new Bar();", -1));
2611 checker.watch(result.get());
2612
2613 result = adoptGRef(jsc_context_evaluate(context.get(), "f.__proto__ == Foo.prototype", -1));
2614 checker.watch(result.get());
2615 g_assert_true(jsc_value_is_boolean(result.get()));
2616 g_assert_true(jsc_value_to_boolean(result.get()));
2617 result = adoptGRef(jsc_context_evaluate(context.get(), "b.__proto__ == Bar.prototype", -1));
2618 checker.watch(result.get());
2619 g_assert_true(jsc_value_is_boolean(result.get()));
2620 g_assert_true(jsc_value_to_boolean(result.get()));
2621 result = adoptGRef(jsc_context_evaluate(context.get(), "b.__proto__.__proto__ == Foo.prototype", -1));
2622 checker.watch(result.get());
2623 g_assert_true(jsc_value_is_boolean(result.get()));
2624 g_assert_true(jsc_value_to_boolean(result.get()));
2625
2626 GRefPtr<JSCValue> foo = adoptGRef(jsc_context_get_value(context.get(), "f"));
2627 checker.watch(foo.get());
2628 g_assert_true(jsc_value_is_object(foo.get()));
2629 g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(fooClass)));
2630 g_assert_false(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(barClass)));
2631 result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof Foo", -1));
2632 checker.watch(result.get());
2633 g_assert_true(jsc_value_is_boolean(result.get()));
2634 g_assert_true(jsc_value_to_boolean(result.get()));
2635 result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof Bar", -1));
2636 checker.watch(result.get());
2637 g_assert_true(jsc_value_is_boolean(result.get()));
2638 g_assert_false(jsc_value_to_boolean(result.get()));
2639 g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
2640 g_assert_false(jsc_value_object_has_property(foo.get(), "bar"));
2641 GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
2642 g_assert_null(properties.get());
2643
2644 GRefPtr<JSCValue> bar = adoptGRef(jsc_context_get_value(context.get(), "b"));
2645 checker.watch(bar.get());
2646 g_assert_true(jsc_value_is_object(bar.get()));
2647 g_assert_true(jsc_value_object_is_instance_of(bar.get(), jsc_class_get_name(barClass)));
2648 g_assert_true(jsc_value_object_is_instance_of(bar.get(), jsc_class_get_name(fooClass)));
2649
2650 result = adoptGRef(jsc_context_evaluate(context.get(), "b instanceof Bar", -1));
2651 checker.watch(result.get());
2652 g_assert_true(jsc_value_is_boolean(result.get()));
2653 g_assert_true(jsc_value_to_boolean(result.get()));
2654 result = adoptGRef(jsc_context_evaluate(context.get(), "b instanceof Foo", -1));
2655 checker.watch(result.get());
2656 g_assert_true(jsc_value_is_boolean(result.get()));
2657 g_assert_true(jsc_value_to_boolean(result.get()));
2658 g_assert_true(jsc_value_object_has_property(bar.get(), "bar"));
2659 g_assert_true(jsc_value_object_has_property(bar.get(), "foo"));
2660 properties.reset(jsc_value_object_enumerate_properties(bar.get()));
2661 g_assert_null(properties.get());
2662
2663 result = adoptGRef(jsc_context_evaluate(context.get(), "b.bar = 25; b.foo = 42;", -1));
2664 checker.watch(result.get());
2665
2666 GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "b.bar", -1));
2667 checker.watch(value.get());
2668 g_assert_true(jsc_value_is_number(value.get()));
2669 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 25);
2670 value = adoptGRef(jsc_context_evaluate(context.get(), "b.foo", -1));
2671 checker.watch(value.get());
2672 g_assert_true(jsc_value_is_number(value.get()));
2673 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 42);
2674
2675 result = adoptGRef(jsc_context_evaluate(context.get(), "b.multiply(2)", -1));
2676 checker.watch(result.get());
2677 value = adoptGRef(jsc_context_evaluate(context.get(), "b.foo", -1));
2678 checker.watch(value.get());
2679 g_assert_true(jsc_value_is_number(value.get()));
2680 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 84);
2681 }
2682
2683 {
2684 LeakChecker checker;
2685 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2686 checker.watch(context.get());
2687 ExceptionHandler exceptionHandler(context.get());
2688
2689 JSCClass* fooClass = jsc_context_register_class(context.get(), "Foo", nullptr, &fooVTable, reinterpret_cast<GDestroyNotify>(fooFree));
2690 checker.watch(fooClass);
2691 g_object_set_data(G_OBJECT(fooClass), "leak-checker", &checker);
2692
2693 GRefPtr<JSCValue> fooConstructor = adoptGRef(jsc_class_add_constructor(fooClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
2694 checker.watch(fooConstructor.get());
2695 g_assert_true(jsc_value_is_constructor(fooConstructor.get()));
2696 jsc_context_set_value(context.get(), jsc_class_get_name(fooClass), fooConstructor.get());
2697 jsc_class_add_property(fooClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
2698
2699 JSCClass* barClass = jsc_context_register_class(context.get(), "Bar", fooClass, &barVTable, reinterpret_cast<GDestroyNotify>(barFree));
2700 checker.watch(barClass);
2701 g_object_set_data(G_OBJECT(barClass), "leak-checker", &checker);
2702 g_assert_true(jsc_class_get_parent(barClass) == fooClass);
2703 GRefPtr<JSCValue> barConstructor = adoptGRef(jsc_class_add_constructor(barClass, nullptr, G_CALLBACK(barCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
2704 checker.watch(barConstructor.get());
2705 g_assert_true(jsc_value_is_constructor(barConstructor.get()));
2706 jsc_context_set_value(context.get(), jsc_class_get_name(barClass), barConstructor.get());
2707 jsc_class_add_property(barClass, "bar", G_TYPE_INT, G_CALLBACK(getBar), G_CALLBACK(setBar), nullptr, nullptr);
2708
2709 GRefPtr<JSCValue> bar = adoptGRef(jsc_context_evaluate(context.get(), "b = new Bar();", -1));
2710 checker.watch(bar.get());
2711 g_assert_true(jsc_value_is_object(bar.get()));
2712 g_assert_true(jsc_value_object_has_property(bar.get(), "bar"));
2713 g_assert_true(jsc_value_object_has_property(bar.get(), "foo"));
2714
2715 g_assert_true(jsc_value_object_has_property(bar.get(), "prop_whatever"));
2716 g_assert_false(jsc_value_object_has_property(bar.get(), "whatever_prop"));
2717
2718 GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(bar.get()));
2719 g_assert_cmpuint(g_strv_length(properties.get()), ==, 1);
2720 g_assert_cmpstr(properties.get()[0], ==, "bar");
2721 g_assert_null(properties.get()[1]);
2722
2723 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "b.prop_1", -1));
2724 checker.watch(result.get());
2725 g_assert_true(jsc_value_is_number(result.get()));
2726 g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 0);
2727
2728 GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_get_property(bar.get(), "prop_1"));
2729 checker.watch(value.get());
2730 g_assert_true(value.get() == result.get());
2731
2732 g_assert_true(jsc_value_object_delete_property(bar.get(), "prop_1"));
2733 g_assert_true(jsc_value_object_has_property(bar.get(), "prop_1"));
2734 value = adoptGRef(jsc_value_object_get_property(bar.get(), "prop_1"));
2735 checker.watch(value.get());
2736 g_assert_true(jsc_value_is_number(value.get()));
2737 g_assert_cmpuint(jsc_value_to_int32(value.get()), ==, 0);
2738
2739 result = adoptGRef(jsc_context_evaluate(context.get(), "b.prop_cant_delete = 125", -1));
2740 checker.watch(result.get());
2741 g_assert_true(jsc_value_is_number(result.get()));
2742 g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 125);
2743 jsc_value_object_delete_property(bar.get(), "prop_cant_delete");
2744 g_assert_true(jsc_value_object_has_property(bar.get(), "prop_cant_delete"));
2745 value = adoptGRef(jsc_value_object_get_property(bar.get(), "prop_cant_delete"));
2746 checker.watch(value.get());
2747 g_assert_true(jsc_value_is_number(value.get()));
2748 g_assert_cmpuint(jsc_value_to_int32(value.get()), ==, 125);
2749
2750 result = adoptGRef(jsc_context_evaluate(context.get(), "b.prop_enum_1 = 250", -1));
2751 checker.watch(result.get());
2752 g_assert_true(jsc_value_is_number(result.get()));
2753 g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 250);
2754 result = adoptGRef(jsc_context_evaluate(context.get(), "b.prop_enum_2 = 450", -1));
2755 checker.watch(result.get());
2756 g_assert_true(jsc_value_is_number(result.get()));
2757 g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 450);
2758
2759 properties.reset(jsc_value_object_enumerate_properties(bar.get()));
2760 g_assert_cmpuint(g_strv_length(properties.get()), ==, 3);
2761 g_assert_cmpstr(properties.get()[0], ==, "bar");
2762 g_assert_cmpstr(properties.get()[1], ==, "prop_enum_1");
2763 g_assert_cmpstr(properties.get()[2], ==, "prop_enum_2");
2764 g_assert_null(properties.get()[3]);
2765 }
2766}
2767
2768static void createError()
2769{
2770 jsc_context_throw(jsc_context_get_current(), "API exception");
2771}
2772
2773static void createCustomError()
2774{
2775 jsc_context_throw_with_name(jsc_context_get_current(), "CustomAPIError", "API custom exception");
2776}
2777
2778static void createFormattedError(const char* details)
2779{
2780 jsc_context_throw_printf(jsc_context_get_current(), "API exception: %s", details);
2781}
2782
2783static void createCustomFormattedError(const char* details)
2784{
2785 jsc_context_throw_with_name_printf(jsc_context_get_current(), "CustomFormattedAPIError", "API custom exception: %s", details);
2786}
2787
2788static void testJSCExceptions()
2789{
2790 {
2791 LeakChecker checker;
2792 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2793 checker.watch(context.get());
2794 g_assert_false(jsc_context_get_exception(context.get()));
2795
2796 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "foo", -1));
2797 checker.watch(result.get());
2798 // By default exceptions are not caught.
2799 g_assert_true(jsc_value_is_undefined(result.get()));
2800 auto* exception = jsc_context_get_exception(context.get());
2801 g_assert_true(JSC_IS_EXCEPTION(exception));
2802 checker.watch(exception);
2803 g_assert_cmpstr(jsc_exception_get_name(exception), ==, "ReferenceError");
2804 g_assert_cmpstr(jsc_exception_get_message(exception), ==, "Can't find variable: foo");
2805 g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1);
2806 g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 4);
2807 g_assert_null(jsc_exception_get_source_uri(exception));
2808 g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "global code");
2809 GUniquePtr<char> errorString(jsc_exception_to_string(exception));
2810 g_assert_cmpstr(errorString.get(), ==, "ReferenceError: Can't find variable: foo");
2811 GUniquePtr<char> reportString(jsc_exception_report(exception));
2812 g_assert_cmpstr(reportString.get(), ==, ":1:4 ReferenceError: Can't find variable: foo\n global code\n");
2813
2814 jsc_context_clear_exception(context.get());
2815 g_assert_null(jsc_context_get_exception(context.get()));
2816 }
2817
2818 {
2819 LeakChecker checker;
2820 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2821 checker.watch(context.get());
2822 g_assert_false(jsc_context_get_exception(context.get()));
2823
2824 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f = 25;\nfoo;", -1));
2825 checker.watch(result.get());
2826
2827 g_assert_true(jsc_value_is_undefined(result.get()));
2828 auto* exception = jsc_context_get_exception(context.get());
2829 g_assert_true(JSC_IS_EXCEPTION(exception));
2830 checker.watch(exception);
2831 g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 2);
2832 g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 4);
2833
2834 jsc_context_clear_exception(context.get());
2835 g_assert_null(jsc_context_get_exception(context.get()));
2836 }
2837
2838 {
2839 LeakChecker checker;
2840 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2841 checker.watch(context.get());
2842 g_assert_false(jsc_context_get_exception(context.get()));
2843
2844 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate_with_source_uri(context.get(),
2845 "let a = 25;\n"
2846 "function foo() {\n"
2847 " let b = baz();\n"
2848 " return b;\n"
2849 "}\n"
2850 "function bar() {\n"
2851 " let c = 75;\n"
2852 " return foo();\n"
2853 "}\n"
2854 "let d = bar();\n",
2855 -1, "file:///foo/script.js", 1));
2856 checker.watch(result.get());
2857
2858 g_assert_true(jsc_value_is_undefined(result.get()));
2859 auto* exception = jsc_context_get_exception(context.get());
2860 g_assert_true(JSC_IS_EXCEPTION(exception));
2861 checker.watch(exception);
2862 g_assert_cmpstr(jsc_exception_get_name(exception), ==, "ReferenceError");
2863 g_assert_cmpstr(jsc_exception_get_message(exception), ==, "Can't find variable: baz");
2864 g_assert_cmpstr(jsc_exception_get_source_uri(exception), ==, "file:///foo/script.js");
2865 g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 3);
2866 g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 16);
2867 g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "foo@file:///foo/script.js:3:16\nbar@file:///foo/script.js:8:15\nglobal code@file:///foo/script.js:10:12");
2868 GUniquePtr<char> errorString(jsc_exception_to_string(exception));
2869 g_assert_cmpstr(errorString.get(), ==, "ReferenceError: Can't find variable: baz");
2870 GUniquePtr<char> reportString(jsc_exception_report(exception));
2871 g_assert_cmpstr(reportString.get(), ==, "file:///foo/script.js:3:16 ReferenceError: Can't find variable: baz\n foo@file:///foo/script.js:3:16\n bar@file:///foo/script.js:8:15\n global code@file:///foo/script.js:10:12\n");
2872
2873 jsc_context_clear_exception(context.get());
2874 g_assert_null(jsc_context_get_exception(context.get()));
2875 }
2876
2877 {
2878 LeakChecker checker;
2879 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2880 checker.watch(context.get());
2881 g_assert_false(jsc_context_get_exception(context.get()));
2882
2883 GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "createError", G_CALLBACK(createError), nullptr, nullptr, G_TYPE_NONE, 0, G_TYPE_NONE));
2884 checker.watch(function.get());
2885 jsc_context_set_value(context.get(), "createError", function.get());
2886
2887 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "var result; try { createError(); } catch (e) { result = 'Caught exception'; }", -1));
2888 checker.watch(result.get());
2889 g_assert_true(jsc_value_is_string(result.get()));
2890 GUniquePtr<char> resultString(jsc_value_to_string(result.get()));
2891 g_assert_cmpstr(resultString.get(), ==, "Caught exception");
2892 g_assert_false(jsc_context_get_exception(context.get()));
2893
2894 result = adoptGRef(jsc_context_evaluate(context.get(), "var result; createError(); result = 'No exception';", -1));
2895 checker.watch(result.get());
2896 g_assert_true(jsc_value_is_undefined(result.get()));
2897 auto* exception = jsc_context_get_exception(context.get());
2898 g_assert_true(JSC_IS_EXCEPTION(exception));
2899 checker.watch(exception);
2900 g_assert_cmpstr(jsc_exception_get_name(exception), ==, "Error");
2901 g_assert_cmpstr(jsc_exception_get_message(exception), ==, "API exception");
2902 g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1);
2903 g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 24);
2904 g_assert_null(jsc_exception_get_source_uri(exception));
2905 g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "createError@[native code]\nglobal code");
2906 GUniquePtr<char> errorString(jsc_exception_to_string(exception));
2907 g_assert_cmpstr(errorString.get(), ==, "Error: API exception");
2908 GUniquePtr<char> reportString(jsc_exception_report(exception));
2909 g_assert_cmpstr(reportString.get(), ==, ":1:24 Error: API exception\n createError@[native code]\n global code\n");
2910
2911 jsc_context_clear_exception(context.get());
2912 g_assert_null(jsc_context_get_exception(context.get()));
2913 }
2914
2915 {
2916 LeakChecker checker;
2917 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2918 checker.watch(context.get());
2919 g_assert_false(jsc_context_get_exception(context.get()));
2920
2921 GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "createCustomError", G_CALLBACK(createCustomError), nullptr, nullptr, G_TYPE_NONE, 0, G_TYPE_NONE));
2922 checker.watch(function.get());
2923 jsc_context_set_value(context.get(), "createCustomError", function.get());
2924
2925 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "var result; createCustomError(); result = 'No exception';", -1));
2926 checker.watch(result.get());
2927 g_assert_true(jsc_value_is_undefined(result.get()));
2928 auto* exception = jsc_context_get_exception(context.get());
2929 g_assert_true(JSC_IS_EXCEPTION(exception));
2930 checker.watch(exception);
2931 g_assert_cmpstr(jsc_exception_get_name(exception), ==, "CustomAPIError");
2932 g_assert_cmpstr(jsc_exception_get_message(exception), ==, "API custom exception");
2933 g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1);
2934 g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 30);
2935 g_assert_null(jsc_exception_get_source_uri(exception));
2936 g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "createCustomError@[native code]\nglobal code");
2937 GUniquePtr<char> errorString(jsc_exception_to_string(exception));
2938 g_assert_cmpstr(errorString.get(), ==, "CustomAPIError: API custom exception");
2939 GUniquePtr<char> reportString(jsc_exception_report(exception));
2940 g_assert_cmpstr(reportString.get(), ==, ":1:30 CustomAPIError: API custom exception\n createCustomError@[native code]\n global code\n");
2941
2942 jsc_context_clear_exception(context.get());
2943 g_assert_null(jsc_context_get_exception(context.get()));
2944 }
2945
2946 {
2947 LeakChecker checker;
2948 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2949 checker.watch(context.get());
2950 g_assert_false(jsc_context_get_exception(context.get()));
2951
2952 GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "createFormattedError", G_CALLBACK(createFormattedError), nullptr, nullptr, G_TYPE_NONE, 1, G_TYPE_STRING));
2953 checker.watch(function.get());
2954 jsc_context_set_value(context.get(), "createFormattedError", function.get());
2955
2956 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "var result; createFormattedError('error details'); result = 'No exception';", -1));
2957 checker.watch(result.get());
2958 g_assert_true(jsc_value_is_undefined(result.get()));
2959 auto* exception = jsc_context_get_exception(context.get());
2960 g_assert_true(JSC_IS_EXCEPTION(exception));
2961 checker.watch(exception);
2962 g_assert_cmpstr(jsc_exception_get_name(exception), ==, "Error");
2963 g_assert_cmpstr(jsc_exception_get_message(exception), ==, "API exception: error details");
2964 g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1);
2965 g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 33);
2966 g_assert_null(jsc_exception_get_source_uri(exception));
2967 g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "createFormattedError@[native code]\nglobal code");
2968 GUniquePtr<char> errorString(jsc_exception_to_string(exception));
2969 g_assert_cmpstr(errorString.get(), ==, "Error: API exception: error details");
2970 GUniquePtr<char> reportString(jsc_exception_report(exception));
2971 g_assert_cmpstr(reportString.get(), ==, ":1:33 Error: API exception: error details\n createFormattedError@[native code]\n global code\n");
2972
2973 jsc_context_clear_exception(context.get());
2974 g_assert_null(jsc_context_get_exception(context.get()));
2975 }
2976
2977 {
2978 LeakChecker checker;
2979 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
2980 checker.watch(context.get());
2981 g_assert_false(jsc_context_get_exception(context.get()));
2982
2983 GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "createCustomFormattedError", G_CALLBACK(createCustomFormattedError), nullptr, nullptr, G_TYPE_NONE, 1, G_TYPE_STRING));
2984 checker.watch(function.get());
2985 jsc_context_set_value(context.get(), "createCustomFormattedError", function.get());
2986
2987 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "var result; createCustomFormattedError('error details'); result = 'No exception';", -1));
2988 checker.watch(result.get());
2989 g_assert_true(jsc_value_is_undefined(result.get()));
2990 auto* exception = jsc_context_get_exception(context.get());
2991 g_assert_true(JSC_IS_EXCEPTION(exception));
2992 checker.watch(exception);
2993 g_assert_cmpstr(jsc_exception_get_name(exception), ==, "CustomFormattedAPIError");
2994 g_assert_cmpstr(jsc_exception_get_message(exception), ==, "API custom exception: error details");
2995 g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1);
2996 g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 39);
2997 g_assert_null(jsc_exception_get_source_uri(exception));
2998 g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "createCustomFormattedError@[native code]\nglobal code");
2999 GUniquePtr<char> errorString(jsc_exception_to_string(exception));
3000 g_assert_cmpstr(errorString.get(), ==, "CustomFormattedAPIError: API custom exception: error details");
3001 GUniquePtr<char> reportString(jsc_exception_report(exception));
3002 g_assert_cmpstr(reportString.get(), ==, ":1:39 CustomFormattedAPIError: API custom exception: error details\n createCustomFormattedError@[native code]\n global code\n");
3003
3004 jsc_context_clear_exception(context.get());
3005 g_assert_null(jsc_context_get_exception(context.get()));
3006 }
3007
3008 {
3009 LeakChecker checker;
3010 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
3011 checker.watch(context.get());
3012 g_assert_false(jsc_context_get_exception(context.get()));
3013
3014 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate_with_source_uri(context.get(), "foo", -1, "file:///foo/script.js", 3));
3015 checker.watch(result.get());
3016
3017 g_assert_true(jsc_value_is_undefined(result.get()));
3018 auto* exception = jsc_context_get_exception(context.get());
3019 g_assert_true(JSC_IS_EXCEPTION(exception));
3020 checker.watch(exception);
3021 g_assert_cmpstr(jsc_exception_get_source_uri(exception), ==, "file:///foo/script.js");
3022 g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 3);
3023 g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 4);
3024 g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "global code@file:///foo/script.js:3:4");
3025 GUniquePtr<char> reportString(jsc_exception_report(exception));
3026 g_assert_cmpstr(reportString.get(), ==, "file:///foo/script.js:3:4 ReferenceError: Can't find variable: foo\n global code@file:///foo/script.js:3:4\n");
3027
3028 jsc_context_clear_exception(context.get());
3029 g_assert_null(jsc_context_get_exception(context.get()));
3030 }
3031
3032 {
3033 LeakChecker checker;
3034 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
3035 checker.watch(context.get());
3036 g_assert_false(jsc_context_get_exception(context.get()));
3037
3038 struct Test {
3039 JSCContext* context;
3040 GRefPtr<JSCException> exception;
3041 bool wasDeleted;
3042 };
3043
3044 Test test = { context.get(), nullptr, false };
3045 jsc_context_push_exception_handler(context.get(), [](JSCContext* context, JSCException* exception, gpointer userData) {
3046 auto* test = static_cast<Test*>(userData);
3047 g_assert_true(context == test->context);
3048 g_assert_false(test->exception);
3049 g_assert_false(test->wasDeleted);
3050 test->exception = exception;
3051 }, &test, [](gpointer userData) {
3052 static_cast<Test*>(userData)->wasDeleted = true;
3053 });
3054
3055 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "foo", -1));
3056 checker.watch(result.get());
3057 // Exception was caught by the user handler.
3058 g_assert_false(jsc_context_get_exception(context.get()));
3059 g_assert_true(JSC_IS_EXCEPTION(test.exception.get()));
3060 checker.watch(test.exception.get());
3061 g_assert_cmpstr(jsc_exception_get_name(test.exception.get()), ==, "ReferenceError");
3062 g_assert_cmpstr(jsc_exception_get_message(test.exception.get()), ==, "Can't find variable: foo");
3063 g_assert_cmpuint(jsc_exception_get_line_number(test.exception.get()), ==, 1);
3064 g_assert_cmpuint(jsc_exception_get_column_number(test.exception.get()), ==, 4);
3065 g_assert_false(jsc_exception_get_source_uri(test.exception.get()));
3066 GUniquePtr<char> errorString(jsc_exception_to_string(test.exception.get()));
3067 g_assert_cmpstr(errorString.get(), ==, "ReferenceError: Can't find variable: foo");
3068
3069 g_assert_false(test.wasDeleted);
3070 jsc_context_pop_exception_handler(context.get());
3071 g_assert_true(test.wasDeleted);
3072
3073 test.exception = nullptr;
3074 test.wasDeleted = false;
3075 jsc_context_push_exception_handler(context.get(), [](JSCContext* context, JSCException* exception, gpointer userData) {
3076 auto* test = static_cast<Test*>(userData);
3077 g_assert_true(context == test->context);
3078 g_assert_false(test->exception);
3079 g_assert_false(test->wasDeleted);
3080 test->exception = exception;
3081 jsc_context_throw_exception(context, exception);
3082 }, &test, [](gpointer userData) {
3083 static_cast<Test*>(userData)->wasDeleted = true;
3084 });
3085
3086 result = adoptGRef(jsc_context_evaluate(context.get(), "foo", -1));
3087 checker.watch(result.get());
3088 // Exception was handled by the user handler, but not caught.
3089 auto* exception = jsc_context_get_exception(context.get());
3090 g_assert_true(JSC_IS_EXCEPTION(exception));
3091 checker.watch(exception);
3092 g_assert_true(exception == test.exception.get());
3093
3094 g_assert_false(test.wasDeleted);
3095 jsc_context_pop_exception_handler(context.get());
3096 g_assert_true(test.wasDeleted);
3097
3098 jsc_context_clear_exception(context.get());
3099 g_assert_null(jsc_context_get_exception(context.get()));
3100 }
3101}
3102
3103static void testJSCPromises()
3104{
3105 {
3106 LeakChecker checker;
3107 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
3108 checker.watch(context.get());
3109 ExceptionHandler exceptionHandler(context.get());
3110
3111 GRefPtr<JSCValue> promise = adoptGRef(jsc_context_get_value(context.get(), "Promise"));
3112 checker.watch(promise.get());
3113 g_assert_true(jsc_value_is_function(promise.get()));
3114
3115 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "typeof Promise", -1));
3116 checker.watch(result.get());
3117 g_assert_true(jsc_value_is_string(result.get()));
3118 GUniquePtr<char> resultString(jsc_value_to_string(result.get()));
3119 g_assert_cmpstr(resultString.get(), ==, "function");
3120
3121 result = adoptGRef(jsc_context_evaluate(context.get(), "result = 0; Promise.resolve(42).then(function (value) { result = value; });", -1));
3122 checker.watch(result.get());
3123
3124 GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "result"));
3125 checker.watch(value.get());
3126 g_assert_true(jsc_value_is_number(value.get()));
3127 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 42);
3128 }
3129
3130 {
3131 LeakChecker checker;
3132 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
3133 checker.watch(context.get());
3134 ExceptionHandler exceptionHandler(context.get());
3135
3136 JSCClass* fooClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
3137 checker.watch(fooClass);
3138 g_assert_false(jsc_class_get_parent(fooClass));
3139 GRefPtr<JSCValue> fooConstructor = adoptGRef(jsc_class_add_constructor(fooClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
3140 checker.watch(fooConstructor.get());
3141 g_assert_true(jsc_value_is_constructor(fooConstructor.get()));
3142 jsc_context_set_value(context.get(), jsc_class_get_name(fooClass), fooConstructor.get());
3143 jsc_class_add_method(fooClass, "getMultiplyFooAsync", G_CALLBACK(getMultiplyFooAsync), &checker, nullptr, JSC_TYPE_VALUE, 1, G_TYPE_INT);
3144 jsc_class_add_property(fooClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
3145
3146 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "result = 0; f = new Foo(); f.foo = 5; f.getMultiplyFooAsync(2).then(function (value) { result = value; }, function (error) { result = -1; });", -1));
3147 checker.watch(result.get());
3148
3149 GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "result"));
3150 checker.watch(value.get());
3151 g_assert_true(jsc_value_is_number(value.get()));
3152 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 10);
3153
3154 result = adoptGRef(jsc_context_evaluate(context.get(), "result = 0; f.getMultiplyFooAsync(0).then(function (value) { result = value; }, function (error) { result = -1; });", -1));
3155 checker.watch(result.get());
3156 value = adoptGRef(jsc_context_get_value(context.get(), "result"));
3157 checker.watch(value.get());
3158 g_assert_true(jsc_value_is_number(value.get()));
3159 g_assert_cmpint(jsc_value_to_int32(value.get()), ==, -1);
3160 }
3161}
3162
3163static bool s_fooWasFreed;
3164
3165static void fooFreeAndLog(Foo* foo)
3166{
3167 fooFree(foo);
3168 s_fooWasFreed = true;
3169}
3170
3171static void testJSCGarbageCollector()
3172{
3173 {
3174 LeakChecker checker;
3175 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
3176 checker.watch(context.get());
3177 ExceptionHandler exceptionHandler(context.get());
3178
3179 GRefPtr<JSCValue> object = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr));
3180 checker.watch(object.get());
3181
3182 GRefPtr<JSCValue> foo = adoptGRef(jsc_value_new_number(context.get(), 25));
3183 checker.watch(foo.get());
3184 g_assert_true(jsc_value_is_number(foo.get()));
3185 g_assert_cmpint(jsc_value_to_int32(foo.get()), ==, 25);
3186 jsc_value_object_set_property(object.get(), "foo", foo.get());
3187
3188 jsc_context_set_value(context.get(), "f", object.get());
3189 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f", -1));
3190 checker.watch(result.get());
3191 g_assert_true(object.get() == result.get());
3192
3193 object = nullptr;
3194 foo = nullptr;
3195 result = nullptr;
3196 jscContextGarbageCollect(context.get());
3197 object = adoptGRef(jsc_context_get_value(context.get(), "f"));
3198 checker.watch(object.get());
3199 g_assert_true(jsc_value_is_object(object.get()));
3200 foo = adoptGRef(jsc_context_evaluate(context.get(), "f.foo", -1));
3201 checker.watch(foo.get());
3202 g_assert_true(jsc_value_is_number(foo.get()));
3203 g_assert_cmpint(jsc_value_to_int32(foo.get()), ==, 25);
3204
3205 result = adoptGRef(jsc_context_evaluate(context.get(), "f = undefined", -1));
3206 checker.watch(result.get());
3207 g_assert_true(jsc_value_is_undefined(result.get()));
3208
3209 jscContextGarbageCollect(context.get());
3210
3211 g_assert_true(jsc_value_is_object(object.get()));
3212 jsc_context_set_value(context.get(), "f", object.get());
3213 result = adoptGRef(jsc_context_evaluate(context.get(), "f", -1));
3214 checker.watch(result.get());
3215 g_assert_true(object.get() == result.get());
3216 foo = adoptGRef(jsc_context_evaluate(context.get(), "f.foo", -1));
3217 checker.watch(foo.get());
3218 g_assert_true(jsc_value_is_number(foo.get()));
3219 g_assert_cmpint(jsc_value_to_int32(foo.get()), ==, 25);
3220 }
3221
3222 {
3223 LeakChecker checker;
3224 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
3225 checker.watch(context.get());
3226 ExceptionHandler exceptionHandler(context.get());
3227
3228 s_fooWasFreed = false;
3229
3230 JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFreeAndLog));
3231 checker.watch(jscClass);
3232 g_assert_false(jsc_class_get_parent(jscClass));
3233
3234 GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
3235 checker.watch(constructor.get());
3236 g_assert_true(jsc_value_is_constructor(constructor.get()));
3237 jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
3238 GRefPtr<JSCValue> object = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1));
3239 checker.watch(object.get());
3240 g_assert_true(jsc_value_is_object(object.get()));
3241 g_assert_true(jsc_value_object_is_instance_of(object.get(), jsc_class_get_name(jscClass)));
3242
3243 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f", -1));
3244 checker.watch(result.get());
3245 g_assert_true(object.get() == result.get());
3246
3247 result = adoptGRef(jsc_context_evaluate(context.get(), "f = undefined", -1));
3248 checker.watch(result.get());
3249 g_assert_true(jsc_value_is_undefined(result.get()));
3250
3251 g_assert_false(s_fooWasFreed);
3252 jscContextGarbageCollect(context.get());
3253 g_assert_false(s_fooWasFreed);
3254
3255 object = nullptr;
3256 g_assert_false(s_fooWasFreed);
3257 jscContextGarbageCollect(context.get());
3258 g_assert_true(s_fooWasFreed);
3259 }
3260}
3261
3262static void weakValueClearedCallback(JSCWeakValue* weakValue, bool* weakValueCleared)
3263{
3264 *weakValueCleared = true;
3265 g_assert_null(jsc_weak_value_get_value(weakValue));
3266}
3267
3268static void testJSCWeakValue()
3269{
3270 {
3271 LeakChecker checker;
3272 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
3273 checker.watch(context.get());
3274 ExceptionHandler exceptionHandler(context.get());
3275
3276 GRefPtr<JSCValue> object = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr));
3277 checker.watch(object.get());
3278
3279 GRefPtr<JSCWeakValue> weak = adoptGRef(jsc_weak_value_new(object.get()));
3280 checker.watch(weak.get());
3281 bool weakValueCleared = false;
3282 g_signal_connect(weak.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakValueCleared);
3283
3284 jsc_context_set_value(context.get(), "foo", object.get());
3285 jscContextGarbageCollect(context.get());
3286 g_assert_false(weakValueCleared);
3287
3288 GRefPtr<JSCValue> foo = adoptGRef(jsc_context_get_value(context.get(), "foo"));
3289 checker.watch(foo.get());
3290 g_assert_true(object.get() == foo.get());
3291
3292 GRefPtr<JSCValue> weakFoo = adoptGRef(jsc_weak_value_get_value(weak.get()));
3293 checker.watch(weakFoo.get());
3294 g_assert_true(foo.get() == weakFoo.get());
3295
3296 GRefPtr<JSCValue> undefinedValue = adoptGRef(jsc_value_new_undefined(context.get()));
3297 checker.watch(undefinedValue.get());
3298 jsc_context_set_value(context.get(), "foo", undefinedValue.get());
3299 weakFoo = nullptr;
3300 foo = nullptr;
3301 object = nullptr;
3302
3303 // The value is still reachable, but unprotected.
3304 g_assert_false(weakValueCleared);
3305 weakFoo = adoptGRef(jsc_weak_value_get_value(weak.get()));
3306 checker.watch(weakFoo.get());
3307 g_assert_true(jsc_value_is_object(weakFoo.get()));
3308 weakFoo = nullptr;
3309
3310 jscContextGarbageCollect(context.get());
3311 g_assert_true(weakValueCleared);
3312 g_assert_null(jsc_weak_value_get_value(weak.get()));
3313 }
3314
3315 {
3316 LeakChecker checker;
3317 GRefPtr<JSCWeakValue> weakObject;
3318 bool weakValueCleared = false;
3319 {
3320 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
3321 checker.watch(context.get());
3322 ExceptionHandler exceptionHandler(context.get());
3323
3324 GRefPtr<JSCValue> object = adoptGRef(jsc_context_evaluate(context.get(), "obj = {};", -1));
3325 checker.watch(object.get());
3326 g_assert_true(JSC_IS_VALUE(object.get()));
3327 g_assert_true(jsc_value_is_object(object.get()));
3328
3329 weakObject = adoptGRef(jsc_weak_value_new(object.get()));
3330 checker.watch(weakObject.get());
3331 g_signal_connect(weakObject.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakValueCleared);
3332
3333 object = adoptGRef(jsc_context_evaluate(context.get(), "obj = null", -1));
3334 checker.watch(object.get());
3335 g_assert_false(weakValueCleared);
3336 }
3337
3338 g_assert_true(weakValueCleared);
3339 g_assert_null(jsc_weak_value_get_value(weakObject.get()));
3340 }
3341
3342 {
3343 LeakChecker checker;
3344 GRefPtr<JSCWeakValue> weakObj;
3345 bool weakObjValueCleared = false;
3346 GRefPtr<JSCWeakValue> weakStr;
3347 bool weakStrValueCleared = false;
3348 GRefPtr<JSCWeakValue> weakPrimitive;
3349 bool weakPrimitiveValueCleared = false;
3350 {
3351 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
3352 checker.watch(context.get());
3353 ExceptionHandler exceptionHandler(context.get());
3354
3355 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "obj = { 'foo' : 'bar' }; str = 'Hello World'; primitive = 25;", -1));
3356 checker.watch(result.get());
3357
3358 GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "obj"));
3359 checker.watch(value.get());
3360 weakObj = adoptGRef(jsc_weak_value_new(value.get()));
3361 checker.watch(weakObj.get());
3362 g_signal_connect(weakObj.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakObjValueCleared);
3363
3364 value = adoptGRef(jsc_context_get_value(context.get(), "str"));
3365 checker.watch(value.get());
3366 weakStr = adoptGRef(jsc_weak_value_new(value.get()));
3367 checker.watch(weakStr.get());
3368 g_signal_connect(weakStr.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakStrValueCleared);
3369
3370 value = adoptGRef(jsc_context_get_value(context.get(), "primitive"));
3371 checker.watch(value.get());
3372 weakPrimitive = adoptGRef(jsc_weak_value_new(value.get()));
3373 checker.watch(weakPrimitive.get());
3374 g_signal_connect(weakPrimitive.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakPrimitiveValueCleared);
3375
3376 value = nullptr;
3377 jscContextGarbageCollect(context.get());
3378 g_assert_false(weakObjValueCleared);
3379 g_assert_false(weakStrValueCleared);
3380 g_assert_false(weakPrimitiveValueCleared);
3381
3382 value = adoptGRef(jsc_weak_value_get_value(weakObj.get()));
3383 checker.watch(value.get());
3384 g_assert_true(jsc_value_is_object(value.get()));
3385
3386 value = adoptGRef(jsc_weak_value_get_value(weakStr.get()));
3387 checker.watch(value.get());
3388 g_assert_true(jsc_value_is_string(value.get()));
3389
3390 value = adoptGRef(jsc_weak_value_get_value(weakPrimitive.get()));
3391 checker.watch(value.get());
3392 g_assert_true(jsc_value_is_number(value.get()));
3393 value = nullptr;
3394
3395 result = adoptGRef(jsc_context_evaluate(context.get(), "str = undefined", -1));
3396 checker.watch(result.get());
3397 jscContextGarbageCollect(context.get());
3398
3399 g_assert_true(weakStrValueCleared);
3400 g_assert_false(weakObjValueCleared);
3401 g_assert_false(weakPrimitiveValueCleared);
3402 g_assert_null(jsc_weak_value_get_value(weakStr.get()));
3403
3404 result = adoptGRef(jsc_context_evaluate(context.get(), "f = undefined", -1));
3405 checker.watch(result.get());
3406 jscContextGarbageCollect(context.get());
3407
3408 // Non-string primitve values are not garbage collected, the weak value
3409 // will be cleared when the global object is destroyed.
3410 g_assert_false(weakPrimitiveValueCleared);
3411 g_assert_false(weakObjValueCleared);
3412 g_assert_true(weakStrValueCleared);
3413
3414 value = adoptGRef(jsc_weak_value_get_value(weakPrimitive.get()));
3415 checker.watch(value.get());
3416 g_assert_true(jsc_value_is_number(value.get()));
3417 value = nullptr;
3418
3419 result = adoptGRef(jsc_context_evaluate(context.get(), "obj = undefined", -1));
3420 checker.watch(result.get());
3421 jscContextGarbageCollect(context.get());
3422
3423 g_assert_true(weakObjValueCleared);
3424 g_assert_true(weakStrValueCleared);
3425 g_assert_false(weakPrimitiveValueCleared);
3426 g_assert_null(jsc_weak_value_get_value(weakObj.get()));
3427 weakObjValueCleared = false;
3428 weakStrValueCleared = false;
3429 }
3430
3431 // Context is now destroyed, only the primitive value should be notified.
3432 g_assert_true(weakPrimitiveValueCleared);
3433 g_assert_false(weakObjValueCleared);
3434 g_assert_false(weakStrValueCleared);
3435 g_assert_null(jsc_weak_value_get_value(weakPrimitive.get()));
3436 }
3437}
3438
3439static void testsJSCVirtualMachine()
3440{
3441 {
3442 LeakChecker checker;
3443 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
3444 checker.watch(context.get());
3445 ExceptionHandler exceptionHandler(context.get());
3446
3447 auto thread = Thread::create("JSCVirtualMachineTest", [&] {
3448 JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFreeAndLog));
3449 checker.watch(jscClass);
3450 g_assert_false(jsc_class_get_parent(jscClass));
3451
3452 GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
3453 checker.watch(constructor.get());
3454 g_assert_true(jsc_value_is_constructor(constructor.get()));
3455 jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
3456
3457 GRefPtr<JSCValue> object = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1));
3458 checker.watch(object.get());
3459 g_assert_true(jsc_value_get_context(object.get()) == context.get());
3460 g_assert_true(jsc_value_is_object(object.get()));
3461 g_assert_true(jsc_value_object_is_instance_of(object.get(), jsc_class_get_name(jscClass)));
3462 });
3463 thread->waitForCompletion();
3464
3465 GRefPtr<JSCValue> object = adoptGRef(jsc_context_get_value(context.get(), "f"));
3466 checker.watch(object.get());
3467 g_assert_true(jsc_value_is_object(object.get()));
3468
3469 jscContextGarbageCollect(context.get());
3470 }
3471
3472 {
3473 Vector<Ref<Thread>, 5> threads;
3474 bool ok = true;
3475 for (unsigned i = 0; i < 5; ++i) {
3476 threads.append(Thread::create("JSCVirtualMachineTest", [&ok] {
3477 GRefPtr<JSCVirtualMachine> vm = adoptGRef(jsc_virtual_machine_new());
3478 GRefPtr<JSCContext> context = adoptGRef(jsc_context_new_with_virtual_machine(vm.get()));
3479 GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(),
3480 "var array = [{}];\n"
3481 "for (var i = 0; i < 20; ++i) {\n"
3482 " var newArray = new Array(array.length * 2);\n"
3483 " for (var j = 0; j < newArray.length; ++j)\n"
3484 " newArray[j] = {parent: array[j / 2]};\n"
3485 " array = newArray;\n"
3486 "}\n", -1
3487 ));
3488 g_assert_true(jsc_value_get_context(result.get()) == context.get());
3489 if (auto* exception = jsc_context_get_exception(context.get())) {
3490 g_message("Uncaught exception: %s", jsc_exception_get_message(exception));
3491 ok = false;
3492 }
3493 GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "array"));
3494 g_assert_true(jsc_value_get_context(value.get()) == context.get());
3495 if (!jsc_value_is_object(value.get())) {
3496 g_message("Did not find \"array\" variable");
3497 ok = false;
3498 }
3499 jscContextGarbageCollect(context.get());
3500 }));
3501 }
3502
3503 for (auto& thread : threads)
3504 thread->waitForCompletion();
3505
3506 g_assert_true(ok);
3507 }
3508}
3509
3510static void testsJSCOptions()
3511{
3512 gboolean useJIT;
3513 g_assert_true(jsc_options_get_boolean(JSC_OPTIONS_USE_JIT, &useJIT));
3514 g_assert_true(useJIT);
3515 g_assert_true(jsc_options_set_boolean(JSC_OPTIONS_USE_JIT, FALSE));
3516 g_assert_true(jsc_options_get_boolean(JSC_OPTIONS_USE_JIT, &useJIT));
3517 g_assert_false(useJIT);
3518 g_assert_true(jsc_options_set_boolean(JSC_OPTIONS_USE_JIT, TRUE));
3519
3520 gint thresholdForJITAfterWarmUp;
3521 g_assert_true(jsc_options_get_int("thresholdForJITAfterWarmUp", &thresholdForJITAfterWarmUp));
3522 g_assert_cmpint(thresholdForJITAfterWarmUp, ==, 500);
3523 g_assert_true(jsc_options_set_int("thresholdForJITAfterWarmUp", 1000));
3524 g_assert_true(jsc_options_get_int("thresholdForJITAfterWarmUp", &thresholdForJITAfterWarmUp));
3525 g_assert_cmpint(thresholdForJITAfterWarmUp, ==, 1000);
3526 g_assert_true(jsc_options_set_int("thresholdForJITAfterWarmUp", 500));
3527
3528 guint maxPerThreadStackUsage;
3529 g_assert_true(jsc_options_get_uint("maxPerThreadStackUsage", &maxPerThreadStackUsage));
3530 g_assert_cmpuint(maxPerThreadStackUsage, ==, 4194304);
3531 g_assert_true(jsc_options_set_uint("maxPerThreadStackUsage", 4096));
3532 g_assert_true(jsc_options_get_uint("maxPerThreadStackUsage", &maxPerThreadStackUsage));
3533 g_assert_cmpuint(maxPerThreadStackUsage, ==, 4096);
3534 g_assert_true(jsc_options_set_uint("maxPerThreadStackUsage", 4194304));
3535
3536 gsize webAssemblyPartialCompileLimit;
3537 g_assert_true(jsc_options_get_size("webAssemblyPartialCompileLimit", &webAssemblyPartialCompileLimit));
3538 g_assert_cmpuint(webAssemblyPartialCompileLimit, ==, 5000);
3539 g_assert_true(jsc_options_set_size("webAssemblyPartialCompileLimit", 6000));
3540 g_assert_true(jsc_options_get_size("webAssemblyPartialCompileLimit", &webAssemblyPartialCompileLimit));
3541 g_assert_cmpuint(webAssemblyPartialCompileLimit, ==, 6000);
3542 g_assert_true(jsc_options_set_size("webAssemblyPartialCompileLimit", 5000));
3543
3544 gdouble smallHeapRAMFraction;
3545 g_assert_true(jsc_options_get_double("smallHeapRAMFraction", &smallHeapRAMFraction));
3546 g_assert_cmpfloat(smallHeapRAMFraction, ==, 0.25);
3547 g_assert_true(jsc_options_set_double("smallHeapRAMFraction", 0.50));
3548 g_assert_true(jsc_options_get_double("smallHeapRAMFraction", &smallHeapRAMFraction));
3549 g_assert_cmpfloat(smallHeapRAMFraction, ==, 0.50);
3550 g_assert_true(jsc_options_set_double("smallHeapRAMFraction", 0.25));
3551
3552 GUniqueOutPtr<char> configFile;
3553 g_assert_true(jsc_options_get_string("configFile", &configFile.outPtr()));
3554 g_assert_null(configFile.get());
3555 g_assert_true(jsc_options_set_string("configFile", "/tmp/foo"));
3556 g_assert_true(jsc_options_get_string("configFile", &configFile.outPtr()));
3557 g_assert_cmpstr(configFile.get(), ==, "/tmp/foo");
3558 g_assert_true(jsc_options_set_string("configFile", nullptr));
3559 g_assert_true(jsc_options_get_string("configFile", &configFile.outPtr()));
3560 g_assert_null(configFile.get());
3561
3562 GUniqueOutPtr<char> bytecodeRangeToJITCompile;
3563 g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr()));
3564 g_assert_null(bytecodeRangeToJITCompile.get());
3565 g_assert_true(jsc_options_set_range_string("bytecodeRangeToJITCompile", "100"));
3566 g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr()));
3567 g_assert_cmpstr(bytecodeRangeToJITCompile.get(), ==, "100");
3568 g_assert_true(jsc_options_set_range_string("bytecodeRangeToJITCompile", "100:200"));
3569 g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr()));
3570 g_assert_cmpstr(bytecodeRangeToJITCompile.get(), ==, "100:200");
3571 g_assert_true(jsc_options_set_range_string("bytecodeRangeToJITCompile", "!100:200"));
3572 g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr()));
3573 g_assert_cmpstr(bytecodeRangeToJITCompile.get(), ==, "!100:200");
3574 g_assert_false(jsc_options_set_range_string("bytecodeRangeToJITCompile", "200:100"));
3575 g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr()));
3576 g_assert_cmpstr(bytecodeRangeToJITCompile.get(), ==, "!100:200");
3577 g_assert_true(jsc_options_set_range_string("bytecodeRangeToJITCompile", nullptr));
3578 g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr()));
3579 g_assert_null(bytecodeRangeToJITCompile.get());
3580
3581 guint logGC;
3582 g_assert_true(jsc_options_get_uint("logGC", &logGC));
3583 g_assert_cmpuint(logGC, ==, 0);
3584 g_assert_true(jsc_options_set_uint("logGC", 1));
3585 g_assert_true(jsc_options_get_uint("logGC", &logGC));
3586 g_assert_cmpuint(logGC, ==, 1);
3587 g_assert_true(jsc_options_set_uint("logGC", 2));
3588 g_assert_true(jsc_options_get_uint("logGC", &logGC));
3589 g_assert_cmpuint(logGC, ==, 2);
3590 g_assert_false(jsc_options_set_uint("logGC", 3));
3591 g_assert_true(jsc_options_get_uint("logGC", &logGC));
3592 g_assert_cmpuint(logGC, ==, 2);
3593 g_assert_true(jsc_options_set_uint("logGC", 0));
3594 g_assert_true(jsc_options_get_uint("logGC", &logGC));
3595 g_assert_cmpuint(logGC, ==, 0);
3596
3597 gboolean value = TRUE;
3598 g_assert_false(jsc_options_get_boolean("InvalidOption", &value));
3599 g_assert_true(value);
3600 g_assert_false(jsc_options_set_boolean("InvalidOption", TRUE));
3601 g_assert_false(jsc_options_get_boolean("InvalidOption", &value));
3602 g_assert_true(value);
3603
3604 // Find a particular option.
3605 bool found = false;
3606 jsc_options_foreach([](const char* option, JSCOptionType type, const char* description, gpointer userData) -> gboolean {
3607 if (!g_strcmp0(option, "useJIT")) {
3608 *static_cast<bool*>(userData) = true;
3609 return TRUE;
3610 }
3611 return FALSE;
3612 }, &found);
3613 g_assert_true(found);
3614
3615 unsigned optionsCount = 0;
3616 jsc_options_foreach([](const char* option, JSCOptionType type, const char* description, gpointer userData) -> gboolean {
3617 (*static_cast<unsigned*>(userData))++;
3618 return FALSE;
3619 }, &optionsCount);
3620 g_assert_cmpuint(optionsCount, >, 100);
3621 g_assert_cmpuint(optionsCount, <, 500);
3622
3623 optionsCount = 0;
3624 jsc_options_foreach([](const char* option, JSCOptionType type, const char* description, gpointer userData) -> gboolean {
3625 if (!g_strcmp0(option, "useJIT"))
3626 g_assert_true(type == JSC_OPTION_BOOLEAN);
3627 else if (!g_strcmp0(option, "thresholdForJITAfterWarmUp"))
3628 g_assert_true(type == JSC_OPTION_INT);
3629 else if (!g_strcmp0(option, "maxPerThreadStackUsage"))
3630 g_assert_true(type == JSC_OPTION_UINT);
3631 else if (!g_strcmp0(option, "webAssemblyPartialCompileLimit"))
3632 g_assert_true(type == JSC_OPTION_SIZE);
3633 else if (!g_strcmp0(option, "smallHeapRAMFraction"))
3634 g_assert_true(type == JSC_OPTION_DOUBLE);
3635 else if (!g_strcmp0(option, "configFile"))
3636 g_assert_true(type == JSC_OPTION_STRING);
3637 else if (!g_strcmp0(option, "bytecodeRangeToJITCompile"))
3638 g_assert_true(type == JSC_OPTION_RANGE_STRING);
3639 else
3640 return FALSE;
3641
3642 (*static_cast<unsigned*>(userData))++;
3643 return FALSE;
3644 }, &optionsCount);
3645 g_assert_cmpuint(optionsCount, ==, 7);
3646
3647 GOptionContext* context = g_option_context_new(nullptr);
3648 g_option_context_add_group(context, jsc_options_get_option_group());
3649 static const char* argv[] = {
3650 __FILE__,
3651 "--jsc-useJIT=false",
3652 "--jsc-thresholdForJITAfterWarmUp=2000",
3653 "--jsc-maxPerThreadStackUsage=1024",
3654 "--jsc-webAssemblyPartialCompileLimit=4000",
3655 "--jsc-smallHeapRAMFraction=0.75",
3656 "--jsc-configFile=/tmp/bar",
3657 "--jsc-bytecodeRangeToJITCompile=100:300",
3658 "--jsc-logGC=1",
3659 nullptr
3660 };
3661 GUniquePtr<char*> copy(g_strdupv(const_cast<char**>(argv)));
3662 int argc = g_strv_length(copy.get());
3663 auto* copyPtr = copy.get();
3664 g_assert_true(g_option_context_parse(context, &argc, &copyPtr, nullptr));
3665 g_option_context_free(context);
3666
3667 g_assert_true(jsc_options_get_boolean(JSC_OPTIONS_USE_JIT, &useJIT));
3668 g_assert_false(useJIT);
3669 g_assert_true(jsc_options_get_int("thresholdForJITAfterWarmUp", &thresholdForJITAfterWarmUp));
3670 g_assert_cmpint(thresholdForJITAfterWarmUp, ==, 2000);
3671 g_assert_true(jsc_options_get_uint("maxPerThreadStackUsage", &maxPerThreadStackUsage));
3672 g_assert_cmpuint(maxPerThreadStackUsage, ==, 1024);
3673 g_assert_true(jsc_options_get_size("webAssemblyPartialCompileLimit", &webAssemblyPartialCompileLimit));
3674 g_assert_cmpuint(webAssemblyPartialCompileLimit, ==, 4000);
3675 g_assert_true(jsc_options_get_double("smallHeapRAMFraction", &smallHeapRAMFraction));
3676 g_assert_cmpfloat(smallHeapRAMFraction, ==, 0.75);
3677 g_assert_true(jsc_options_get_string("configFile", &configFile.outPtr()));
3678 g_assert_cmpstr(configFile.get(), ==, "/tmp/bar");
3679 g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr()));
3680 g_assert_cmpstr(bytecodeRangeToJITCompile.get(), ==, "100:300");
3681 g_assert_true(jsc_options_get_uint("logGC", &logGC));
3682 g_assert_cmpuint(logGC, ==, 1);
3683
3684 // Restore options their default values.
3685 g_assert_true(jsc_options_set_boolean(JSC_OPTIONS_USE_JIT, TRUE));
3686 g_assert_true(jsc_options_set_int("thresholdForJITAfterWarmUp", 500));
3687 g_assert_true(jsc_options_set_uint("maxPerThreadStackUsage", 4194304));
3688 g_assert_true(jsc_options_set_size("webAssemblyPartialCompileLimit", 5000));
3689 g_assert_true(jsc_options_set_double("smallHeapRAMFraction", 0.25));
3690 g_assert_true(jsc_options_set_string("configFile", nullptr));
3691 g_assert_true(jsc_options_set_range_string("bytecodeRangeToJITCompile", nullptr));
3692 g_assert_true(jsc_options_set_uint("logGC", 0));
3693
3694 context = g_option_context_new(nullptr);
3695 g_option_context_add_group(context, jsc_options_get_option_group());
3696 static const char* argv2[] = { __FILE__, "--jsc-InvalidOption=true", nullptr };
3697 copy.reset(g_strdupv(const_cast<char**>(argv2)));
3698 argc = g_strv_length(copy.get());
3699 copyPtr = copy.get();
3700 g_assert_false(g_option_context_parse(context, &argc, &copyPtr, nullptr));
3701 g_option_context_free(context);
3702
3703 context = g_option_context_new(nullptr);
3704 g_option_context_add_group(context, jsc_options_get_option_group());
3705 static const char* argv3[] = { __FILE__, "--jsc-useJIT=nein", nullptr };
3706 copy.reset(g_strdupv(const_cast<char**>(argv3)));
3707 argc = g_strv_length(copy.get());
3708 copyPtr = copy.get();
3709 g_assert_false(g_option_context_parse(context, &argc, &copyPtr, nullptr));
3710 g_option_context_free(context);
3711 g_assert_true(jsc_options_get_boolean(JSC_OPTIONS_USE_JIT, &useJIT));
3712 g_assert_true(useJIT);
3713}
3714
3715#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
3716static void testsJSCAutocleanups()
3717{
3718 LeakChecker checker;
3719 g_autoptr(JSCVirtualMachine) vm = jsc_virtual_machine_new();
3720 checker.watch(vm);
3721 g_assert_true(JSC_IS_VIRTUAL_MACHINE(vm));
3722
3723 g_autoptr(JSCContext) context = jsc_context_new_with_virtual_machine(vm);
3724 checker.watch(context);
3725 g_assert_true(JSC_IS_CONTEXT(context));
3726
3727 g_autoptr(JSCValue) value = jsc_context_evaluate(context, "v = 25", -1);
3728 checker.watch(value);
3729 g_assert_true(JSC_IS_VALUE(value));
3730 g_assert_true(jsc_value_is_number(value));
3731 g_assert_cmpint(jsc_value_to_int32(value), ==, 25);
3732
3733 g_autoptr(JSCException) exception = jsc_exception_new(context, "API error");
3734 checker.watch(exception);
3735 g_assert_true(JSC_IS_EXCEPTION(exception));
3736 jsc_context_throw_exception(context, exception);
3737}
3738#endif
3739
3740int main(int argc, char** argv)
3741{
3742 g_test_init(&argc, &argv, nullptr);
3743
3744 g_test_add_func("/jsc/basic", testJSCBasic);
3745 g_test_add_func("/jsc/types", testJSCTypes);
3746 g_test_add_func("/jsc/global-object", testJSCGlobalObject);
3747 g_test_add_func("/jsc/evaluate-in-object", testJSCEvaluateInObject);
3748 g_test_add_func("/jsc/check-syntax", testJSCCheckSyntax);
3749 g_test_add_func("/jsc/function", testJSCFunction);
3750 g_test_add_func("/jsc/object", testJSCObject);
3751 g_test_add_func("/jsc/class", testJSCClass);
3752 g_test_add_func("/jsc/prototypes", testJSCPrototypes);
3753 g_test_add_func("/jsc/exceptions", testJSCExceptions);
3754 g_test_add_func("/jsc/promises", testJSCPromises);
3755 g_test_add_func("/jsc/garbage-collector", testJSCGarbageCollector);
3756 g_test_add_func("/jsc/weak-value", testJSCWeakValue);
3757 g_test_add_func("/jsc/vm", testsJSCVirtualMachine);
3758 g_test_add_func("/jsc/options", testsJSCOptions);
3759#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
3760 g_test_add_func("/jsc/autocleanups", testsJSCAutocleanups);
3761#endif
3762
3763 return g_test_run();
3764}
3765