/* * QuickJS C/JS Interaction Demo Program */ #include #include #include #include #include "quickjs.h" #include "quickjs-libc.h" void print_js_value(JSContext *ctx, JSValue val, const char *name) { printf("%s: ", name); if (JS_IsUndefined(val)) { printf("undefined\n"); } else if (JS_IsNull(val)) { printf("null\n"); } else if (JS_IsBool(val)) { printf("%s\n", JS_ToBool(ctx, val) ? "true" : "false"); } else if (JS_IsNumber(val)) { double num; JS_ToFloat64(ctx, &num, val); printf("%.2f\n", num); } else if (JS_IsString(val)) { const char *str = JS_ToCString(ctx, val); printf("'%s'\n", str); JS_FreeCString(ctx, str); } else if (JS_IsObject(val)) { printf("[Object]\n"); } else if (JS_IsFunction(ctx, val)) { printf("[Function]\n"); } else { printf("[Unknown]\n"); } } void check_exception(JSContext *ctx) { JSValue exception = JS_GetException(ctx); if (!JS_IsUndefined(exception) && !JS_IsNull(exception)) { printf("Exception: "); JSValue error_message = JS_ToString(ctx, exception); const char *msg = JS_ToCString(ctx, error_message); printf("%s\n", msg); JS_FreeCString(ctx, msg); JS_FreeValue(ctx, error_message); JSValue stack = JS_GetPropertyStr(ctx, exception, "stack"); if (!JS_IsUndefined(stack) && !JS_IsNull(stack)) { const char *stack_str = JS_ToCString(ctx, stack); printf("Stack:\n%s\n", stack_str); JS_FreeCString(ctx, stack_str); } JS_FreeValue(ctx, stack); } JS_FreeValue(ctx, exception); } void example1_basic_execution(JSContext *ctx) { printf("\n========== Example 1: Basic JavaScript Execution ==========\n"); JSValue result; result = JS_Eval(ctx, "1 + 2", 5, "", JS_EVAL_TYPE_GLOBAL); print_js_value(ctx, result, "1 + 2"); JS_FreeValue(ctx, result); const char *code = "let x = 10; let y = 20; x * y;"; result = JS_Eval(ctx, code, strlen(code), "", JS_EVAL_TYPE_GLOBAL); print_js_value(ctx, result, "x * y"); JS_FreeValue(ctx, result); const char *func_code = "function add(a, b) { return a + b; } add(5, 3);"; result = JS_Eval(ctx, func_code, strlen(func_code), "", JS_EVAL_TYPE_GLOBAL); print_js_value(ctx, result, "add(5, 3)"); JS_FreeValue(ctx, result); } static JSValue js_add(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { double a, b; if (JS_ToFloat64(ctx, &a, argv[0]) || JS_ToFloat64(ctx, &b, argv[1])) { return JS_ThrowTypeError(ctx, "Parameters must be numbers"); } return JS_NewFloat64(ctx, a + b); } static JSValue js_greet(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { if (argc < 1 || !JS_IsString(argv[0])) { return JS_ThrowTypeError(ctx, "Need a string parameter"); } const char *name = JS_ToCString(ctx, argv[0]); char greeting[256]; snprintf(greeting, sizeof(greeting), "Hello from C, %s!", name); JS_FreeCString(ctx, name); return JS_NewString(ctx, greeting); } static JSValue js_get_current_time(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue obj = JS_NewObject(ctx); time_t now = time(NULL); struct tm *tm_info = localtime(&now); char time_str[64]; strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info); JS_SetPropertyStr(ctx, obj, "timestamp", JS_NewInt64(ctx, (int64_t)now)); JS_SetPropertyStr(ctx, obj, "formatted", JS_NewString(ctx, time_str)); JS_SetPropertyStr(ctx, obj, "year", JS_NewInt32(ctx, tm_info->tm_year + 1900)); JS_SetPropertyStr(ctx, obj, "month", JS_NewInt32(ctx, tm_info->tm_mon + 1)); JS_SetPropertyStr(ctx, obj, "day", JS_NewInt32(ctx, tm_info->tm_mday)); return obj; } static JSValue js_reverse_array(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { if (argc < 1 || !JS_IsArray(argv[0])) { return JS_ThrowTypeError(ctx, "Need an array parameter"); } JSValue input_arr = argv[0]; JSValue length_val = JS_GetPropertyStr(ctx, input_arr, "length"); uint32_t length; JS_ToUint32(ctx, &length, length_val); JS_FreeValue(ctx, length_val); JSValue result_arr = JS_NewArray(ctx); for (uint32_t i = 0; i < length; i++) { JSValue element = JS_GetPropertyUint32(ctx, input_arr, i); JS_SetPropertyUint32(ctx, result_arr, length - 1 - i, element); } return result_arr; } static JSValue js_sum_all(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { double sum = 0; double value; for (int i = 0; i < argc; i++) { if (JS_ToFloat64(ctx, &value, argv[i])) { return JS_ThrowTypeError(ctx, "All parameters must be numbers"); } sum += value; } return JS_NewFloat64(ctx, sum); } void example3_call_c_from_js(JSContext *ctx) { printf("\n========== Example 3: Call C Functions from JavaScript ==========\n"); JSValue global_obj = JS_GetGlobalObject(ctx); JSValue func = JS_NewCFunction(ctx, js_add, "c_add", 2); JS_SetPropertyStr(ctx, global_obj, "c_add", func); func = JS_NewCFunction(ctx, js_greet, "c_greet", 1); JS_SetPropertyStr(ctx, global_obj, "c_greet", func); func = JS_NewCFunction(ctx, js_get_current_time, "c_getCurrentTime", 0); JS_SetPropertyStr(ctx, global_obj, "c_getCurrentTime", func); func = JS_NewCFunction(ctx, js_reverse_array, "c_reverseArray", 1); JS_SetPropertyStr(ctx, global_obj, "c_reverseArray", func); func = JS_NewCFunction(ctx, js_sum_all, "c_sumAll", 0); JS_SetPropertyStr(ctx, global_obj, "c_sumAll", func); JS_FreeValue(ctx, global_obj); const char *test_code = "console.log('c_add(3, 4) =', c_add(3, 4));"; JS_Eval(ctx, test_code, strlen(test_code), "", JS_EVAL_TYPE_GLOBAL); test_code = "console.log('c_greet(\"Bob\") =', c_greet('Bob'));"; JS_Eval(ctx, test_code, strlen(test_code), "", JS_EVAL_TYPE_GLOBAL); test_code = "let time = c_getCurrentTime(); console.log('Current time:', time.formatted);"; JS_Eval(ctx, test_code, strlen(test_code), "", JS_EVAL_TYPE_GLOBAL); test_code = "let arr = [1, 2, 3, 4, 5]; let reversed = c_reverseArray(arr); console.log('Original:', arr); console.log('Reversed:', reversed);"; JS_Eval(ctx, test_code, strlen(test_code), "", JS_EVAL_TYPE_GLOBAL); test_code = "console.log('c_sumAll(1, 2, 3, 4, 5) =', c_sumAll(1, 2, 3, 4, 5));"; JS_Eval(ctx, test_code, strlen(test_code), "", JS_EVAL_TYPE_GLOBAL); check_exception(ctx); } void example4_type_conversion(JSContext *ctx) { printf("\n========== Example 4: Type Conversion ==========\n"); JSValue js_undefined = JS_UNDEFINED; JSValue js_null = JS_NULL; JSValue js_bool_true = JS_NewBool(ctx, 1); JSValue js_bool_false = JS_NewBool(ctx, 0); JSValue js_int = JS_NewInt32(ctx, 42); JSValue js_float = JS_NewFloat64(ctx, 3.14159); JSValue js_string = JS_NewString(ctx, "Hello, QuickJS!"); JSValue js_object = JS_NewObject(ctx); JSValue js_array = JS_NewArray(ctx); JS_SetPropertyUint32(ctx, js_array, 0, JS_NewInt32(ctx, 1)); JS_SetPropertyUint32(ctx, js_array, 1, JS_NewInt32(ctx, 2)); JS_SetPropertyUint32(ctx, js_array, 2, JS_NewInt32(ctx, 3)); JS_SetPropertyStr(ctx, js_object, "name", JS_NewString(ctx, "TestObject")); JS_SetPropertyStr(ctx, js_object, "value", JS_NewInt32(ctx, 123)); print_js_value(ctx, js_undefined, "undefined"); print_js_value(ctx, js_null, "null"); print_js_value(ctx, js_bool_true, "true"); print_js_value(ctx, js_bool_false, "false"); print_js_value(ctx, js_int, "42"); print_js_value(ctx, js_float, "3.14159"); print_js_value(ctx, js_string, "string"); print_js_value(ctx, js_object, "object"); print_js_value(ctx, js_array, "array"); double num_value; if (!JS_ToFloat64(ctx, &num_value, js_int)) { printf("Get integer from JS: %.0f\n", num_value); } const char *str_value = JS_ToCString(ctx, js_string); printf("Get string from JS: %s\n", str_value); JS_FreeCString(ctx, str_value); JSValue js_bigint = JS_NewBigInt64(ctx, 9007199254740993LL); print_js_value(ctx, js_bigint, "BigInt"); int64_t bigint_value; if (!JS_ToBigInt64(ctx, &bigint_value, js_bigint)) { printf("Get BigInt from JS: %lld\n", (long long)bigint_value); } JS_FreeValue(ctx, js_bool_true); JS_FreeValue(ctx, js_bool_false); JS_FreeValue(ctx, js_int); JS_FreeValue(ctx, js_float); JS_FreeValue(ctx, js_string); JS_FreeValue(ctx, js_object); JS_FreeValue(ctx, js_array); JS_FreeValue(ctx, js_bigint); } void example5_object_manipulation(JSContext *ctx) { printf("\n========== Example 5: Object Manipulation ==========\n"); JSValue obj = JS_NewObject(ctx); JS_SetPropertyStr(ctx, obj, "name", JS_NewString(ctx, "MyObject")); JS_SetPropertyStr(ctx, obj, "count", JS_NewInt32(ctx, 100)); JS_SetPropertyStr(ctx, obj, "active", JS_NewBool(ctx, 1)); JSValue nested = JS_NewObject(ctx); JS_SetPropertyStr(ctx, nested, "x", JS_NewInt32(ctx, 10)); JS_SetPropertyStr(ctx, nested, "y", JS_NewInt32(ctx, 20)); JS_SetPropertyStr(ctx, obj, "position", nested); const char *complex_obj_code = "const person = { firstName: 'John', lastName: 'Doe', age: 30, getFullName: function() { return this.firstName + ' ' + this.lastName; } };"; JSValue person = JS_Eval(ctx, complex_obj_code, strlen(complex_obj_code), "", JS_EVAL_TYPE_GLOBAL); JSValue name = JS_GetPropertyStr(ctx, person, "firstName"); JSValue age = JS_GetPropertyStr(ctx, person, "age"); print_js_value(ctx, name, "person.firstName"); print_js_value(ctx, age, "person.age"); JSValue get_full_name = JS_GetPropertyStr(ctx, person, "getFullName"); JSValue full_name = JS_Call(ctx, get_full_name, person, 0, NULL); print_js_value(ctx, full_name, "person.getFullName()"); JS_FreeValue(ctx, obj); JS_FreeValue(ctx, person); JS_FreeValue(ctx, name); JS_FreeValue(ctx, age); JS_FreeValue(ctx, get_full_name); JS_FreeValue(ctx, full_name); } void example6_array_manipulation(JSContext *ctx) { printf("\n========== Example 6: Array Manipulation ==========\n"); JSValue arr = JS_NewArray(ctx); for (int i = 0; i < 5; i++) { JS_SetPropertyUint32(ctx, arr, i, JS_NewInt32(ctx, i * 10)); } JSValue length_val = JS_GetPropertyStr(ctx, arr, "length"); uint32_t length; JS_ToUint32(ctx, &length, length_val); printf("Array length: %u\n", length); JS_FreeValue(ctx, length_val); printf("Array elements: "); for (uint32_t i = 0; i < length; i++) { JSValue element = JS_GetPropertyUint32(ctx, arr, i); int32_t value; JS_ToInt32(ctx, &value, element); printf("%d ", value); JS_FreeValue(ctx, element); } printf("\n"); const char *array_code = "const numbers = [1, 2, 3, 4, 5]; const doubled = numbers.map(x => x * 2); const sum = numbers.reduce((a, b) => a + b, 0);"; JS_Eval(ctx, array_code, strlen(array_code), "", JS_EVAL_TYPE_GLOBAL); JSValue global = JS_GetGlobalObject(ctx); JSValue doubled = JS_GetPropertyStr(ctx, global, "doubled"); JSValue sum = JS_GetPropertyStr(ctx, global, "sum"); print_js_value(ctx, doubled, "numbers.map(x => x * 2)"); print_js_value(ctx, sum, "numbers.reduce((a, b) => a + b, 0)"); const char *typed_array_code = "const int8 = new Int8Array([1, 2, 3, 127]); const float64 = new Float64Array([1.1, 2.2, 3.3]);"; JS_Eval(ctx, typed_array_code, strlen(typed_array_code), "", JS_EVAL_TYPE_GLOBAL); JSValue int8 = JS_GetPropertyStr(ctx, global, "int8"); JSValue float64_arr = JS_GetPropertyStr(ctx, global, "float64"); print_js_value(ctx, int8, "Int8Array"); print_js_value(ctx, float64_arr, "Float64Array"); JS_FreeValue(ctx, arr); JS_FreeValue(ctx, global); JS_FreeValue(ctx, doubled); JS_FreeValue(ctx, sum); JS_FreeValue(ctx, int8); JS_FreeValue(ctx, float64_arr); } static JSValue js_c_throw_error(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue error_obj = JS_NewObject(ctx); JS_SetPropertyStr(ctx, error_obj, "message", JS_NewString(ctx, "Error thrown from C!")); return JS_Throw(ctx, error_obj); } void example7_exception_handling(JSContext *ctx) { printf("\n========== Example 7: Exception Handling ==========\n"); printf("Test JavaScript exception:\n"); const char *error_code = "throw new Error('This is a test error');"; JSValue result = JS_Eval(ctx, error_code, strlen(error_code), "", JS_EVAL_TYPE_GLOBAL); check_exception(ctx); JS_FreeValue(ctx, result); printf("\nTest throw exception from C:\n"); const char *test_c_throw_code = "function testCThrow() { return c_throw_error(); } testCThrow();"; JSValue global = JS_GetGlobalObject(ctx); JSValue c_throw_func = JS_NewCFunction(ctx, js_c_throw_error, "c_throw_error", 0); JS_SetPropertyStr(ctx, global, "c_throw_error", c_throw_func); JS_FreeValue(ctx, global); result = JS_Eval(ctx, test_c_throw_code, strlen(test_c_throw_code), "", JS_EVAL_TYPE_GLOBAL); check_exception(ctx); JS_FreeValue(ctx, result); } void example8_memory_management(JSContext *ctx) { printf("\n========== Example 8: Memory Management ==========\n"); JSValue val = JS_NewString(ctx, "test"); printf("Created string: %s\n", JS_ToCString(ctx, val)); JS_FreeValue(ctx, val); val = JS_NewString(ctx, "shared"); JSValue val2 = JS_DupValue(ctx, val); printf("Shared string: %s\n", JS_ToCString(ctx, val2)); JS_FreeValue(ctx, val); JS_FreeValue(ctx, val2); printf("\nCreating large array...\n"); JSValue large_array = JS_NewArray(ctx); for (int i = 0; i < 1000; i++) { JS_SetPropertyUint32(ctx, large_array, i, JS_NewInt32(ctx, i)); } printf("Large array created\n"); JS_FreeValue(ctx, large_array); printf("Large array freed\n"); printf("\nTrigger garbage collection...\n"); JS_RunGC(JS_GetRuntime(ctx)); printf("Garbage collection completed\n"); } void example9_stdlib_modules(JSContext *ctx) { printf("\n========== Example 9: Standard Library Modules ==========\n"); js_init_module_std(ctx, "std"); js_init_module_os(ctx, "os"); js_init_module_bjson(ctx, "bjson"); const char *stdlib_code = "import * as std from 'std'; import * as os from 'os'; console.log('Current working directory:', os.getcwd());"; JSValue result = JS_Eval(ctx, stdlib_code, strlen(stdlib_code), "", JS_EVAL_TYPE_MODULE); check_exception(ctx); JS_FreeValue(ctx, result); } static JSValue js_multiply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { double a, b; if (JS_ToFloat64(ctx, &a, argv[0]) || JS_ToFloat64(ctx, &b, argv[1])) { return JS_ThrowTypeError(ctx, "Parameters must be numbers"); } return JS_NewFloat64(ctx, a * b); } static int my_math_module_init(JSContext *ctx, JSModuleDef *m) { JSValue add_func = JS_NewCFunction(ctx, js_add, "add", 2); JS_SetModuleExport(ctx, m, "add", add_func); JSValue multiply_func = JS_NewCFunction(ctx, js_multiply, "multiply", 2); JS_SetModuleExport(ctx, m, "multiply", multiply_func); return 0; } void example10_custom_module(JSContext *ctx) { printf("\n========== Example 10: Custom JavaScript Modules ==========\n"); JSModuleDef *m = JS_NewCModule(ctx, "my_math", my_math_module_init); const char *module_code = "import { add, multiply } from 'my_math'; console.log('add(5, 3) =', add(5, 3)); console.log('multiply(4, 6) =', multiply(4, 6));"; JSValue result = JS_Eval(ctx, module_code, strlen(module_code), "", JS_EVAL_TYPE_MODULE); check_exception(ctx); JS_FreeValue(ctx, result); const char *module_source = "export function factorial(n) { if (n <= 1) return 1; return n * factorial(n - 1); } export const PI = 3.14159;"; JSValue module_val = JS_Eval(ctx, module_source, strlen(module_source), "math_utils.js", JS_EVAL_TYPE_MODULE); JS_FreeValue(ctx, module_val); const char *use_math_utils = "import { factorial, PI } from 'math_utils.js'; console.log('5! =', factorial(5)); console.log('PI =', PI);"; result = JS_Eval(ctx, use_math_utils, strlen(use_math_utils), "", JS_EVAL_TYPE_MODULE); check_exception(ctx); JS_FreeValue(ctx, result); } int main(int argc, char **argv) { JSRuntime *rt = JS_NewRuntime(); if (!rt) { fprintf(stderr, "Failed to create QuickJS runtime\n"); return 1; } JSContext *ctx = JS_NewContext(rt); if (!ctx) { fprintf(stderr, "Failed to create QuickJS context\n"); JS_FreeRuntime(rt); return 1; } printf("======================================================\n"); printf(" QuickJS C/JS Interaction Demo Program\n"); printf("======================================================\n"); js_std_add_helpers(ctx, argc, argv); example1_basic_execution(ctx); example3_call_c_from_js(ctx); example4_type_conversion(ctx); example5_object_manipulation(ctx); example6_array_manipulation(ctx); example7_exception_handling(ctx); example8_memory_management(ctx); example9_stdlib_modules(ctx); example10_custom_module(ctx); printf("\n======================================================\n"); printf(" All examples completed!\n"); printf("======================================================\n"); JS_FreeContext(ctx); JS_FreeRuntime(rt); return 0; }