tildefriends/src/util.js.c

242 lines
6.1 KiB
C
Raw Normal View History

#include "util.js.h"
#include "mem.h"
#include "task.h"
#include "trace.h"
#include "quickjs-libc.h"
#include <uv.h>
#include <string.h>
static JSValue _util_utf8_encode(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
size_t length = 0;
const char* value = JS_ToCStringLen(context, &length, argv[0]);
JSValue arrayBuffer = JS_NewArrayBufferCopy(context, (const uint8_t*)value, length);
JSValue global = JS_GetGlobalObject(context);
JSValue constructor = JS_GetPropertyStr(context, global, "Uint8Array");
JS_FreeValue(context, global);
JSValue typedArray = JS_CallConstructor(context, constructor, 1, &arrayBuffer);
JS_FreeValue(context, constructor);
JS_FreeValue(context, arrayBuffer);
JS_FreeCString(context, value);
return typedArray;
}
static JSValue _util_utf8_decode(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
JSValue result = JS_NULL;
size_t length;
if (JS_IsString(argv[0]))
{
result = JS_DupValue(context, argv[0]);
}
else
{
uint8_t* array = tf_util_try_get_array_buffer(context, &length, argv[0]);
if (array)
{
result = JS_NewStringLen(context, (const char*)array, length);
}
else
{
size_t offset;
size_t element_size;
JSValue buffer = tf_util_try_get_typed_array_buffer(context, argv[0], &offset, &length, &element_size);
size_t size;
if (!JS_IsException(buffer))
{
array = tf_util_try_get_array_buffer(context, &size, buffer);
if (array)
{
result = JS_NewStringLen(context, (const char*)array, size);
}
}
JS_FreeValue(context, buffer);
}
}
return result;
}
JSValue tf_util_utf8_decode(JSContext* context, JSValue value)
{
return _util_utf8_decode(context, JS_NULL, 1, &value);
}
uint8_t* tf_util_try_get_array_buffer(JSContext* context, size_t* psize, JSValueConst obj)
{
uint8_t* result = JS_GetArrayBuffer(context, psize, obj);
JS_FreeValue(context, JS_GetException(context));
return result;
}
JSValue tf_util_try_get_typed_array_buffer(JSContext* context, JSValueConst obj, size_t* pbyte_offset, size_t* pbyte_length, size_t* pbytes_per_element)
{
JSValue result = JS_GetTypedArrayBuffer(context, obj, pbyte_offset, pbyte_length, pbytes_per_element);
JS_FreeValue(context, JS_GetException(context));
return result;
}
JSValue _util_print(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
tf_task_t* task = JS_GetContextOpaque(context);
if (task)
{
printf("Task[%p:%s]>", task, tf_task_get_name(task));
tf_task_print(task, argc, argv);
}
for (int i = 0; i < argc; ++i)
{
if (JS_IsNull(argv[i]))
{
printf(" null");
}
else
{
const char* value = JS_ToCString(context, argv[i]);
printf(" %s", value);
JS_FreeCString(context, value);
}
}
printf("\n");
return JS_NULL;
}
bool tf_util_report_error(JSContext* context, JSValue value)
{
bool is_error = false;
if (JS_IsError(context, value))
{
const char* string = JS_ToCString(context, value);
printf("ERROR: %s\n", string);
JS_FreeCString(context, string);
JSValue stack = JS_GetPropertyStr(context, value, "stack");
if (!JS_IsUndefined(stack))
{
const char* stack_str = JS_ToCString(context, stack);
printf("%s\n", stack_str);
JS_FreeCString(context, stack_str);
}
JS_FreeValue(context, stack);
tf_task_t* task = tf_task_get(context);
if (task)
{
tf_task_send_error_to_parent(task, value);
}
is_error = true;
}
else if (JS_IsException(value))
{
tf_task_t* task = tf_task_get(context);
if (task)
{
tf_task_send_error_to_parent(task, value);
}
else
{
js_std_dump_error(context);
}
is_error = true;
}
return is_error;
}
typedef struct _timeout_t {
tf_task_t* _task;
JSValue _callback;
} timeout_t;
static void _handle_closed(uv_handle_t* handle)
{
tf_free(handle);
}
static void _util_timeoutCallback(uv_timer_t* handle)
{
timeout_t* timeout = handle->data;
tf_trace_begin(tf_task_get_trace(timeout->_task), "_util_timeoutCallback");
JSContext* context = tf_task_get_context(timeout->_task);
JSValue result = JS_Call(
context,
timeout->_callback,
JS_NULL,
0,
NULL);
tf_util_report_error(context, result);
JS_FreeValue(context, result);
JS_FreeValue(context, timeout->_callback);
tf_trace_end(tf_task_get_trace(timeout->_task));
tf_free(timeout);
uv_close((uv_handle_t*)handle, _handle_closed);
}
static JSValue _util_setTimeout(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
tf_task_t* task = JS_GetContextOpaque(context);
timeout_t* timeout = tf_malloc(sizeof(timeout_t));
*timeout = (timeout_t)
{
._task = task,
._callback = JS_DupValue(context, argv[0]),
};
uv_timer_t* timer = tf_malloc(sizeof(uv_timer_t));
memset(timer, 0, sizeof(uv_timer_t));
uv_timer_init(tf_task_get_loop(task), timer);
timer->data = timeout;
int64_t duration;
JS_ToInt64(context, &duration, argv[1]);
uv_timer_start(timer, _util_timeoutCallback, duration, 0);
return JS_NULL;
}
void tf_util_register(JSContext* context)
{
JSValue global = JS_GetGlobalObject(context);
JS_SetPropertyStr(context, global, "utf8Decode", JS_NewCFunction(context, _util_utf8_decode, "utf8Decode", 1));
JS_SetPropertyStr(context, global, "utf8Encode", JS_NewCFunction(context, _util_utf8_encode, "utf8Encode", 1));
JS_SetPropertyStr(context, global, "print", JS_NewCFunction(context, _util_print, "print", 1));
JS_SetPropertyStr(context, global, "setTimeout", JS_NewCFunction(context, _util_setTimeout, "setTimeout", 2));
JS_FreeValue(context, global);
}
int tf_util_get_length(JSContext* context, JSValue value)
{
JSValue length = JS_GetPropertyStr(context, value, "length");
int result = 0;
JS_ToInt32(context, &result, length);
JS_FreeValue(context, length);
return result;
}
int tf_util_insert_index(const void* key, const void* base, size_t count, size_t size, int (*compare)(const void*, const void*))
{
int lower = 0;
int upper = count;
while (lower < upper && lower < (int)count)
{
int guess = (lower + upper) / 2;
int result = compare(key, ((char*)base) + size * guess);
if (result < 0)
{
upper = guess;
}
else if (result > 0)
{
lower = guess + 1;
}
else
{
return guess;
}
};
return lower;
}