#include "duktape.h" #include "ILibDuktape_Helpers.h" #include "ILibDuktapeModSearch.h" #include "../microstack/ILibParsers.h" #include "../microstack/ILibCrypto.h" #define ILibDuktape_Timer_Ptrs "\xFF_DuktapeTimer_PTRS" duk_ret_t ILibDuktape_Pollyfills_Buffer_slice(duk_context *ctx) { int nargs = duk_get_top(ctx); char *buffer; char *out; duk_size_t bufferLen; int offset = 0; duk_push_this(ctx); buffer = Duktape_GetBuffer(ctx, -1, &bufferLen); if (nargs >= 1) { offset = duk_require_int(ctx, 0); bufferLen -= offset; } if (nargs == 2) { bufferLen = (duk_size_t)duk_require_int(ctx, 1) - offset; } duk_push_fixed_buffer(ctx, bufferLen); out = Duktape_GetBuffer(ctx, -1, NULL); memcpy_s(out, bufferLen, buffer + offset, bufferLen); return 1; } duk_ret_t ILibDuktape_Polyfills_Buffer_toString(duk_context *ctx) { int nargs = duk_get_top(ctx); char *buffer, *tmpBuffer; duk_size_t bufferLen; char *cType; duk_push_this(ctx); // [buffer] buffer = Duktape_GetBuffer(ctx, -1, &bufferLen); if (nargs == 0) { // Just convert to a string duk_push_lstring(ctx, buffer, bufferLen); // [buffer][string] } else { cType = (char*)duk_require_string(ctx, 0); if (strcmp(cType, "base64") == 0) { duk_push_fixed_buffer(ctx, ILibBase64EncodeLength((int)bufferLen)); tmpBuffer = Duktape_GetBuffer(ctx, -1, NULL); ILibBase64Encode((unsigned char*)buffer, (int)bufferLen, (unsigned char**)&tmpBuffer); duk_push_string(ctx, tmpBuffer); } else if (strcmp(cType, "hex") == 0) { duk_push_fixed_buffer(ctx, 1 + (bufferLen * 2)); tmpBuffer = Duktape_GetBuffer(ctx, -1, NULL); util_tohex(buffer, (int)bufferLen, tmpBuffer); duk_push_string(ctx, tmpBuffer); } else { duk_push_string(ctx, "buffer.toString(): Unrecognized parameter"); duk_throw(ctx); return(DUK_RET_ERROR); } } return 1; } duk_ret_t ILibDuktape_Polyfills_Buffer_from(duk_context *ctx) { int nargs = duk_get_top(ctx); char *str; duk_size_t strlength; char *encoding; char *buffer; int bufferLen; if (!(nargs == 2 && duk_is_string(ctx, 0) && duk_is_string(ctx, 1))) { duk_push_string(ctx, "Buffer.from(): Usage not supported yet."); duk_throw(ctx); return(DUK_RET_ERROR); } str = (char*)duk_get_lstring(ctx, 0, &strlength); encoding = (char*)duk_require_string(ctx, 1); if (strcmp(encoding, "base64") == 0) { // Base64 duk_push_fixed_buffer(ctx, ILibBase64DecodeLength((int)strlength)); buffer = Duktape_GetBuffer(ctx, -1, NULL); bufferLen = ILibBase64Decode((unsigned char*)str, (int)strlength, (unsigned char**)&buffer); duk_push_buffer_object(ctx, -1, 0, bufferLen, DUK_BUFOBJ_DUKTAPE_BUFFER); } else if (strcmp(encoding, "hex") == 0) { duk_push_fixed_buffer(ctx, strlength / 2); buffer = Duktape_GetBuffer(ctx, -1, NULL); bufferLen = util_hexToBuf(str, (int)strlength, buffer); duk_push_buffer_object(ctx, -1, 0, bufferLen, DUK_BUFOBJ_DUKTAPE_BUFFER); } else { duk_push_string(ctx, "Buffer.from(): Encoding not supported yet."); duk_throw(ctx); return(DUK_RET_ERROR); } return 1; } duk_ret_t ILibDuktape_Polyfills_Buffer_readInt32BE(duk_context *ctx) { int offset = duk_require_int(ctx, 0); char *buffer; duk_size_t bufferLen; duk_push_this(ctx); buffer = Duktape_GetBuffer(ctx, -1, &bufferLen); duk_push_int(ctx, ntohl(((int*)(buffer + offset))[0])); return(1); } void ILibDuktape_Polyfills_Buffer(duk_context *ctx) { // Polyfill 'Buffer.slice' duk_get_prop_string(ctx, -1, "Duktape"); // [g][Duktape] duk_get_prop_string(ctx, -1, "Buffer"); // [g][Duktape][Buffer] duk_get_prop_string(ctx, -1, "prototype"); // [g][Duktape][Buffer][prototype] duk_push_c_function(ctx, ILibDuktape_Pollyfills_Buffer_slice, DUK_VARARGS); // [g][Duktape][Buffer][prototype][func] duk_put_prop_string(ctx, -2, "slice"); // [g][Duktape][Buffer][prototype] duk_push_c_function(ctx, ILibDuktape_Polyfills_Buffer_readInt32BE, DUK_VARARGS); duk_put_prop_string(ctx, -2, "readInt32BE"); duk_pop_3(ctx); // [g] // Polyfill 'Buffer.toString() duk_get_prop_string(ctx, -1, "Duktape"); // [g][Duktape] duk_get_prop_string(ctx, -1, "Buffer"); // [g][Duktape][Buffer] duk_get_prop_string(ctx, -1, "prototype"); // [g][Duktape][Buffer][prototype] duk_push_c_function(ctx, ILibDuktape_Polyfills_Buffer_toString, DUK_VARARGS);// [g][Duktape][Buffer][prototype][func] duk_put_prop_string(ctx, -2, "toString"); // [g][Duktape][Buffer][prototype] duk_pop_3(ctx); // [g] // Polyfill Buffer.from() duk_get_prop_string(ctx, -1, "Buffer"); // [g][Buffer] duk_push_c_function(ctx, ILibDuktape_Polyfills_Buffer_from, DUK_VARARGS); // [g][Buffer][func] duk_put_prop_string(ctx, -2, "from"); // [g][Buffer] duk_pop(ctx); } duk_ret_t ILibDuktape_Polyfills_String_startsWith(duk_context *ctx) { duk_size_t tokenLen; char *token = Duktape_GetBuffer(ctx, 0, &tokenLen); char *buffer; duk_size_t bufferLen; duk_push_this(ctx); buffer = Duktape_GetBuffer(ctx, -1, &bufferLen); if (ILibString_StartsWith(buffer, (int)bufferLen, token, (int)tokenLen) != 0) { duk_push_true(ctx); } else { duk_push_false(ctx); } return 1; } duk_ret_t ILibDuktape_Polyfills_String_endsWith(duk_context *ctx) { duk_size_t tokenLen; char *token = Duktape_GetBuffer(ctx, 0, &tokenLen); char *buffer; duk_size_t bufferLen; duk_push_this(ctx); buffer = Duktape_GetBuffer(ctx, -1, &bufferLen); if (ILibString_EndsWith(buffer, (int)bufferLen, token, (int)tokenLen) != 0) { duk_push_true(ctx); } else { duk_push_false(ctx); } return 1; } void ILibDuktape_Polyfills_String(duk_context *ctx) { // Polyfill 'String.startsWith' duk_get_prop_string(ctx, -1, "String"); // [string] duk_get_prop_string(ctx, -1, "prototype"); // [string][proto] duk_push_c_function(ctx, ILibDuktape_Polyfills_String_startsWith, DUK_VARARGS); // [string][proto][func] duk_put_prop_string(ctx, -2, "startsWith"); // [string][proto] duk_push_c_function(ctx, ILibDuktape_Polyfills_String_endsWith, DUK_VARARGS); // [string][proto][func] duk_put_prop_string(ctx, -2, "endsWith"); // [string][proto] duk_pop_2(ctx); } duk_ret_t ILibDuktape_Polyfills_Console_log(duk_context *ctx) { int numargs = duk_get_top(ctx); int i; for (i = 0; i < numargs; ++i) { if (duk_is_string(ctx, i)) { printf("%s%s", (i == 0 ? "" : ", "), duk_require_string(ctx, i)); } else { duk_dup(ctx, i); printf("%s%s", (i == 0 ? "" : ", "), duk_to_string(ctx, -1)); } } printf("\n"); return 0; } duk_ret_t ILibDuktape_Polyfills_Console_enableWebLog(duk_context *ctx) { #ifdef _REMOTELOGGING void *chain = Duktape_GetChain(ctx); int port = duk_require_int(ctx, 0); duk_size_t pLen; if (duk_peval_string(ctx, "process.argv0") != 0) { return(ILibDuktape_Error(ctx, "console.enableWebLog(): Couldn't fetch argv0")); } char *p = (char*)duk_get_lstring(ctx, -1, &pLen); if (ILibString_EndsWith(p, (int)pLen, ".js", 3) != 0) { memcpy_s(ILibScratchPad2, sizeof(ILibScratchPad2), p, pLen - 3); sprintf_s(ILibScratchPad2 + (pLen - 3), sizeof(ILibScratchPad2) - 3, ".wlg"); } else if (ILibString_EndsWith(p, (int)pLen, ".exe", 3) != 0) { memcpy_s(ILibScratchPad2, sizeof(ILibScratchPad2), p, pLen - 4); sprintf_s(ILibScratchPad2 + (pLen - 3), sizeof(ILibScratchPad2) - 4, ".wlg"); } else { sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "%s.wlg", p); } ILibStartDefaultLoggerEx(chain, (unsigned short)port, ILibScratchPad2); #endif return (0); } void ILibDuktape_Polyfills_Console(duk_context *ctx) { // Polyfill console.log() if (duk_has_prop_string(ctx, -1, "console")) { duk_get_prop_string(ctx, -1, "console"); // [g][console] } else { duk_push_object(ctx); // [g][console] duk_dup(ctx, -1); // [g][console][console] duk_put_prop_string(ctx, -3, "console"); // [g][console] } duk_push_c_function(ctx, ILibDuktape_Polyfills_Console_log, DUK_VARARGS); // [g][console][log] duk_put_prop_string(ctx, -2, "log"); // [g][console] ILibDuktape_CreateInstanceMethod(ctx, "enableWebLog", ILibDuktape_Polyfills_Console_enableWebLog, 1); duk_pop(ctx); // [g] } duk_ret_t ILibDuktape_ntohl(duk_context *ctx) { duk_size_t bufferLen; char *buffer = Duktape_GetBuffer(ctx, 0, &bufferLen); int offset = duk_require_int(ctx, 1); if ((int)bufferLen < (4 + offset)) { duk_push_string(ctx, "buffer too small"); duk_throw(ctx); return(DUK_RET_ERROR); } duk_push_int(ctx, ntohl(((unsigned int*)(buffer + offset))[0])); return 1; } duk_ret_t ILibDuktape_ntohs(duk_context *ctx) { duk_size_t bufferLen; char *buffer = Duktape_GetBuffer(ctx, 0, &bufferLen); int offset = duk_require_int(ctx, 1); if ((int)bufferLen < 2 + offset) { duk_push_string(ctx, "buffer too small"); duk_throw(ctx); return(DUK_RET_ERROR); } duk_push_int(ctx, ntohs(((unsigned short*)(buffer + offset))[0])); return 1; } duk_ret_t ILibDuktape_htonl(duk_context *ctx) { duk_size_t bufferLen; char *buffer = Duktape_GetBuffer(ctx, 0, &bufferLen); int offset = duk_require_int(ctx, 1); unsigned int val = (unsigned int)duk_require_int(ctx, 2); if ((int)bufferLen < (4 + offset)) { duk_push_string(ctx, "buffer too small"); duk_throw(ctx); return(DUK_RET_ERROR); } ((unsigned int*)(buffer + offset))[0] = htonl(val); return 0; } duk_ret_t ILibDuktape_htons(duk_context *ctx) { duk_size_t bufferLen; char *buffer = Duktape_GetBuffer(ctx, 0, &bufferLen); int offset = duk_require_int(ctx, 1); unsigned int val = (unsigned int)duk_require_int(ctx, 2); if ((int)bufferLen < (2 + offset)) { duk_push_string(ctx, "buffer too small"); duk_throw(ctx); return(DUK_RET_ERROR); } ((unsigned short*)(buffer + offset))[0] = htons(val); return 0; } void ILibDuktape_Polyfills_byte_ordering(duk_context *ctx) { ILibDuktape_CreateInstanceMethod(ctx, "ntohl", ILibDuktape_ntohl, 2); ILibDuktape_CreateInstanceMethod(ctx, "ntohs", ILibDuktape_ntohs, 2); ILibDuktape_CreateInstanceMethod(ctx, "htonl", ILibDuktape_htonl, 3); ILibDuktape_CreateInstanceMethod(ctx, "htons", ILibDuktape_htons, 3); } typedef enum ILibDuktape_Timer_Type { ILibDuktape_Timer_Type_TIMEOUT = 0, ILibDuktape_Timer_Type_INTERVAL = 1, ILibDuktape_Timer_Type_IMMEDIATE = 2 }ILibDuktape_Timer_Type; typedef struct ILibDuktape_Timer { duk_context *ctx; void *object; void *callback; void *args; int timeout; ILibDuktape_Timer_Type timerType; }ILibDuktape_Timer; duk_ret_t ILibDuktape_Polyfills_timer_finalizer(duk_context *ctx) { // Make sure we remove any timers just in case, so we don't leak resources ILibDuktape_Timer *ptrs; duk_get_prop_string(ctx, 0, ILibDuktape_Timer_Ptrs); ptrs = (ILibDuktape_Timer*)Duktape_GetBuffer(ctx, -1, NULL); ILibLifeTime_Remove(ILibGetBaseTimer(Duktape_GetChain(ctx)), ptrs); return 0; } void ILibDuktape_Polyfills_timer_elapsed(void *obj) { ILibDuktape_Timer *ptrs = (ILibDuktape_Timer*)obj; int argCount, i; if (ptrs->timerType == ILibDuktape_Timer_Type_INTERVAL) { ILibLifeTime_AddEx(ILibGetBaseTimer(Duktape_GetChain(ptrs->ctx)), ptrs, ptrs->timeout, ILibDuktape_Polyfills_timer_elapsed, NULL); } duk_push_heapptr(ptrs->ctx, ptrs->callback); // [func] duk_push_heapptr(ptrs->ctx, ptrs->object); // [func][this] duk_push_heapptr(ptrs->ctx, ptrs->args); // [func][this][argArray] argCount = (int)duk_get_length(ptrs->ctx, -1); for (i = 0; i < argCount; ++i) { duk_get_prop_index(ptrs->ctx, -1, i); // [func][this][argArray][arg] duk_swap_top(ptrs->ctx, -2); // [func][this][arg][argArray] } duk_pop(ptrs->ctx); // [func][this][...arg...] if (duk_pcall_method(ptrs->ctx, argCount) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ptrs->ctx, "timers.onElapsed() callback handler"); } duk_pop(ptrs->ctx); // ... } duk_ret_t ILibDuktape_Polyfills_timer_set(duk_context *ctx) { int nargs = duk_get_top(ctx); ILibDuktape_Timer *ptrs; ILibDuktape_Timer_Type timerType; void *chain = Duktape_GetChain(ctx); int argx; duk_push_current_function(ctx); duk_get_prop_string(ctx, -1, "type"); timerType = (ILibDuktape_Timer_Type)duk_get_int(ctx, -1); duk_push_object(ctx); //[retVal] ILibDuktape_CreateFinalizer(ctx, ILibDuktape_Polyfills_timer_finalizer); duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_Timer)); //[retVal][ptrs] ptrs = (ILibDuktape_Timer*)Duktape_GetBuffer(ctx, -1, NULL); memset(ptrs, 0, sizeof(ILibDuktape_Timer)); duk_put_prop_string(ctx, -2, ILibDuktape_Timer_Ptrs); //[retVal] ptrs->ctx = ctx; ptrs->object = duk_get_heapptr(ctx, -1); ptrs->timerType = timerType; ptrs->timeout = timerType == ILibDuktape_Timer_Type_IMMEDIATE ? 0 : (int)duk_require_int(ctx, 1); ptrs->callback = duk_require_heapptr(ctx, 0); duk_push_array(ctx); //[retVal][argArray] for (argx = ILibDuktape_Timer_Type_IMMEDIATE == timerType ? 1 : 2; argx < nargs; ++argx) { duk_dup(ctx, argx); //[retVal][argArray][arg] duk_put_prop_index(ctx, -2, argx - (ILibDuktape_Timer_Type_IMMEDIATE == timerType ? 1 : 2)); //[retVal][argArray] } ptrs->args = duk_get_heapptr(ctx, -1); duk_put_prop_string(ctx, -2, "\xFF_argArray"); duk_dup(ctx, 0); //[retVal][callback] duk_put_prop_string(ctx, -2, "\xFF_callback"); //[retVal] ILibLifeTime_AddEx(ILibGetBaseTimer(chain), ptrs, ptrs->timeout, ILibDuktape_Polyfills_timer_elapsed, NULL); return 1; } duk_ret_t ILibDuktape_Polyfills_timer_clear(duk_context *ctx) { ILibDuktape_Timer *ptrs; ILibDuktape_Timer_Type timerType; duk_push_current_function(ctx); duk_get_prop_string(ctx, -1, "type"); timerType = (ILibDuktape_Timer_Type)duk_get_int(ctx, -1); if(!duk_has_prop_string(ctx, 0, ILibDuktape_Timer_Ptrs)) { switch (timerType) { case ILibDuktape_Timer_Type_TIMEOUT: return(ILibDuktape_Error(ctx, "timers.clearTimeout(): Invalid Parameter")); case ILibDuktape_Timer_Type_INTERVAL: return(ILibDuktape_Error(ctx, "timers.clearInterval(): Invalid Parameter")); case ILibDuktape_Timer_Type_IMMEDIATE: return(ILibDuktape_Error(ctx, "timers.clearImmediate(): Invalid Parameter")); } } duk_get_prop_string(ctx, 0, ILibDuktape_Timer_Ptrs); ptrs = (ILibDuktape_Timer*)Duktape_GetBuffer(ctx, -1, NULL); ILibLifeTime_Remove(ILibGetBaseTimer(Duktape_GetChain(ctx)), ptrs); return 0; } void ILibDuktape_Polyfills_timer(duk_context *ctx) { ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "type", ILibDuktape_Timer_Type_TIMEOUT, "setTimeout", ILibDuktape_Polyfills_timer_set, DUK_VARARGS); ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "type", ILibDuktape_Timer_Type_INTERVAL, "setInterval", ILibDuktape_Polyfills_timer_set, DUK_VARARGS); ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "type", ILibDuktape_Timer_Type_IMMEDIATE, "setImmediate", ILibDuktape_Polyfills_timer_set, DUK_VARARGS); ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "type", ILibDuktape_Timer_Type_TIMEOUT, "clearTimeout", ILibDuktape_Polyfills_timer_clear, 1); ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "type", ILibDuktape_Timer_Type_INTERVAL, "clearInterval", ILibDuktape_Polyfills_timer_clear, 1); ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "type", ILibDuktape_Timer_Type_IMMEDIATE, "clearImmediate", ILibDuktape_Polyfills_timer_clear, 1); } duk_ret_t ILibDuktape_Polyfills_addModule(duk_context *ctx) { duk_size_t moduleLen; char *module = (char*)Duktape_GetBuffer(ctx, 1, &moduleLen); char *moduleName = (char*)duk_require_string(ctx, 0); if (ILibDuktape_ModSearch_AddModule(ctx, moduleName, module, (int)moduleLen) != 0) { return(ILibDuktape_Error(ctx, "Cannot add module: %s", moduleName)); } return(0); } void ILibDuktape_Polyfills_Init(duk_context *ctx) { // Global Polyfills duk_push_global_object(ctx); // [g] ILibDuktape_Polyfills_String(ctx); ILibDuktape_Polyfills_Buffer(ctx); ILibDuktape_Polyfills_Console(ctx); ILibDuktape_Polyfills_byte_ordering(ctx); ILibDuktape_Polyfills_timer(ctx); ILibDuktape_CreateInstanceMethod(ctx, "addModule", ILibDuktape_Polyfills_addModule, 2); duk_pop(ctx); // ... } #ifdef __DOXY__ /*! \brief String */ class String { public: /*! \brief Finds a String within another String \param str \ Substring to search for \return Index of where the string was found. -1 if not found */ Integer indexOf(str); /*! \brief Extracts a String from a String. \param startIndex Starting index to extract \param length Number of characters to extract \return \ extracted String */ String substr(startIndex, length); /*! \brief Extracts a String from a String. \param startIndex Starting index to extract \param endIndex Ending index to extract \return \ extracted String */ String splice(startIndex, endIndex); /*! \brief Split String into substrings \param str \ Delimiter to split on \return Array of Tokens */ Array split(str); /*! \brief Determines if a String starts with the given substring \param str \ substring \return True, if this String starts with the given substring */ boolean startsWith(str); }; /*! \brief Instances of the Buffer class are similar to arrays of integers but correspond to fixed-sized, raw memory allocations. */ class Buffer { public: /*! \brief Create a new Buffer instance of the specified number of bytes \param size \return \ new Buffer instance */ Buffer(size); /*! \brief Returns the amount of memory allocated in bytes */ integer length; /*! \brief Creates a new Buffer instance from an encoded String \param str \ encoded String \param encoding \ Encoding. Can be either 'base64' or 'hex' \return \ new Buffer instance */ static Buffer from(str, encoding); /*! \brief Decodes Buffer to a String \param encoding \ Optional. Can be either 'base64' or 'hex'. If not specified, will just encode as an ANSI string \param start Optional. Starting offset. Default: 0 \param end Optional. Ending offset (not inclusive) Default: buffer length \return \ Encoded String */ String toString([encoding[, start[, end]]]); /*! \brief Returns a new Buffer that references the same memory as the original, but offset and cropped by the start and end indices. \param start Where the new Buffer will start. Default: 0 \param end Where the new Buffer will end. (Not inclusive) Default: buffer length \return \ */ Buffer slice([start[, end]]); }; /*! \brief Console */ class Console { public: /*! \brief Serializes the input parameters to the Console Display \param args */ void log(...args); }; /*! \brief Global Timer Methods */ class Timers { public: /*! \brief Schedules the "immediate" execution of the callback after I/O events' callbacks. \param callback Function to call at the end of the event loop \param args Optional arguments to pass when the callback is called \return Immediate for use with clearImmediate(). */ Immediate setImmediate(callback[, ...args]); /*! \brief Schedules execution of a one-time callback after delay milliseconds. \param callback Function to call when the timeout elapses \param args Optional arguments to pass when the callback is called \return Timeout for use with clearTimeout(). */ Timeout setTimeout(callback, delay[, ...args]); /*! \brief Schedules repeated execution of callback every delay milliseconds. \param callback Function to call when the timer elapses \param args Optional arguments to pass when the callback is called \return Timeout for use with clearInterval(). */ Timeout setInterval(callback, delay[, ...args]); /*! \brief Cancels a Timeout returned by setTimeout() \param timeout Timeout */ void clearTimeout(timeout); /*! \brief Cancels a Timeout returned by setInterval() \param interval Timeout */ void clearInterval(interval); /*! \brief Cancels an Immediate returned by setImmediate() \param immediate Immediate */ void clearImmediate(immediate); /*! \brief Scheduled Timer */ class Timeout { public: }; /*! \implements Timeout \brief Scheduled Immediate */ class Immediate { public: }; }; /*! \brief Global methods for byte ordering manipulation */ class BytesOrdering { public: /*! \brief Converts 2 bytes from network order to host order \param buffer \ bytes to convert \param offset offset to start \return host order value */ static integer ntohs(buffer, offset); /*! \brief Converts 4 bytes from network order to host order \param buffer \ bytes to convert \param offset offset to start \return host order value */ static integer ntohl(buffer, offset); /*! \brief Writes 2 bytes in network order \param buffer \ Buffer to write to \param offset offset to start writing \param val host order value to write */ static void htons(buffer, offset, val); /*! \brief Writes 4 bytes in network order \param buffer \ Buffer to write to \param offset offset to start writing \param val host order value to write */ static void htonl(buffer, offset, val); }; #endif