Files
quickjs-prj/main.c
2026-02-02 12:48:25 +08:00

459 lines
18 KiB
C

/*
* QuickJS C/JS Interaction Demo Program
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#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, "<eval>", 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), "<multiline>", 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), "<func>", 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), "<test_c_func>", JS_EVAL_TYPE_GLOBAL);
test_code = "console.log('c_greet(\"Bob\") =', c_greet('Bob'));";
JS_Eval(ctx, test_code, strlen(test_code), "<test_c_func>", JS_EVAL_TYPE_GLOBAL);
test_code = "let time = c_getCurrentTime(); console.log('Current time:', time.formatted);";
JS_Eval(ctx, test_code, strlen(test_code), "<test_c_func>", 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), "<test_c_func>", 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), "<test_c_func>", 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), "<complex_obj>", 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), "<arrays>", 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), "<typed_arrays>", 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), "<error>", 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), "<c_error>", 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), "<stdlib>", 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), "<custom_module>", 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), "<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;
}