1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-11 05:43:33 +00:00
Files
MeshAgent/microscript/ILibDuktape_HECI.c

1122 lines
39 KiB
C

/*
Copyright 2006 - 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "ILibDuktape_HECI.h"
#include "ILibDuktapeModSearch.h"
#include "ILibDuktape_Helpers.h"
#include "ILibDuktape_DuplexStream.h"
#include "ILibDuktape_EventEmitter.h"
#include "ILibDuktape_ChildProcess.h"
#include "../microstack/ILibParsers.h"
#include "../microstack/ILibProcessPipe.h"
#include "../microstack/ILibRemoteLogging.h"
#ifdef WIN32
#include <windows.h>
#include <setupapi.h>
#include <initguid.h>
#include <winioctl.h>
DEFINE_GUID(GUID_DEVINTERFACE_HECI, 0xE2D1FF34, 0x3458, 0x49A9, 0x88, 0xDA, 0x8E, 0x69, 0x15, 0xCE, 0x9B, 0xE5);
#elif defined(_POSIX)
typedef struct HECI_client
{
unsigned int max_msg_length;
unsigned char protocol_version;
unsigned char reserved[3];
}HECI_client;
typedef struct uuid_le
{
unsigned char b[16];
}uuid_le;
struct HECI_CONNECT_client_data
{
union
{
uuid_le uuid;
HECI_client properties;
};
};
#endif
#define ILibDuktape_HECI_ChainLink "\xFF_HECI_ChainLink"
#define ILibDuktape_HECI_Descriptor "\xFF_HECI_Descriptor"
#define ILibDuktape_HECI_ChildProcess "\xFF_HECI_ChildProcess"
#define ILibDuktape_HECI_Q "\xFF_HECI_Q"
#define ILibDuktape_HECI_IoctlWaitHandle "\xFF_HECI_IoctlWaitHandle"
#define ILibDuktape_HECI_Child "\xFF_HECI_Child"
#define ILibDuktape_HECI_Parent "\xFF_HECI_Parent"
#define ILibDuktape_HECI_MaxBufferSize "\xFF_HECI_MaxBufSize"
#define ILibDuktape_HECI_SessionMemPtr "\xFF_HECI_SessionMemPtr"
#define ILibDuktape_HECI_Session_NoPipeline "\xFF_HECI_Session_NoPipeline"
#ifdef __DOXY__
/*!
\implements EventEmitter
\brief JavaScript object interface for HECI calls. <b>require('heci')</b> to use;
*/
class Heci
{
public:
/*!
\brief Performs an Ioctl on the HECI device
\param code <Integer> Ioctl Code to invoke
\param inBuffer \<Buffer\> Input data for Ioctl. Can be null
\param outBuffer \<Buffer\> Optional. Output data from Ioctl. Must be specified if Ioctl code returns data
\param callback <Function> Dispatched when a response is received from the HECI device\n
status <Integer> Success Code. 0 = Success, Error code on failure\n
buffer \<Buffer\> Output Buffer\n
args <Any> Optional parameters that were passed in\n
\param args <any> Optional arguments to pass when the callback is called
*/
void doIoctl(code, inBuffer[, outBuffer], callback[, ...args]);
};
#endif
typedef struct ILibDuktape_HECI_ioctl_data
{
duk_context *ctx;
void *heciObject;
void *data;
void *Q;
void *chain;
ILibProcessPipe_Manager pipeManager;
#ifdef WIN32
OVERLAPPED v;
HANDLE device;
DWORD bytesReceived;
#elif defined(_POSIX)
int device;
#endif
int code;
char *outBuffer;
void *outBuffer_obj;
duk_size_t outBufferLen;
duk_size_t bufferLen;
char buffer[];
}ILibDuktape_HECI_ioctl_data;
typedef struct ILibDuktape_HECI_Session
{
void *chain;
int noPipelining;
ILibDuktape_DuplexStream *stream;
#ifdef WIN32
OVERLAPPED v;
OVERLAPPED wv;
ILibProcessPipe_Manager mgr;
HANDLE descriptor;
DWORD bytesRead;
#else
int descriptor;
#endif
ILibQueue PendingWrites;
duk_size_t bufferSize;
char buffer[];
}ILibDuktape_HECI_Session;
typedef struct ILibDuktape_HECI_WriteState
{
ILibDuktape_HECI_Session *session;
int returnIgnored;
#ifndef WIN32
int bufferOffset;
#endif
int bufferLen;
char buffer[];
}ILibDuktape_HECI_WriteState;
typedef struct HECI_chainLink
{
ILibChain_Link link;
duk_context *ctx;
void *Q;
ILibDuktape_HECI_Session *session;
void *heciObject;
int descriptor;
int paused;
}HECI_chainLink;
void ILibDuktape_HECI_Push(duk_context *ctx, void *chain);
ILibTransport_DoneState ILibDuktape_HECI_Session_WriteHandler_Process(ILibDuktape_HECI_Session *session);
#ifdef WIN32
HANDLE ILibDuktape_HECI_windowsInit()
{
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceDetail = NULL;
HDEVINFO hDeviceInfo;
DWORD bufferSize;
SP_DEVICE_INTERFACE_DATA interfaceData;
LONG ii = 0;
HANDLE retVal = NULL;
// Find all devices that have our interface
hDeviceInfo = SetupDiGetClassDevs((LPGUID)&GUID_DEVINTERFACE_HECI, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDeviceInfo == INVALID_HANDLE_VALUE)
{
return(NULL);
}
// Setup the interface data struct
interfaceData.cbSize = sizeof(interfaceData);
for (ii = 0;
SetupDiEnumDeviceInterfaces(hDeviceInfo, NULL, (LPGUID)&GUID_DEVINTERFACE_HECI, ii, &interfaceData);
++ii)
{
// Found our device instance
if (!SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, NULL, 0, &bufferSize, NULL))
{
DWORD err = GetLastError();
if (err != ERROR_INSUFFICIENT_BUFFER)
{
continue;
}
}
// Allocate a big enough buffer to get detail data
deviceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)ILibMemory_AllocateA(bufferSize);
if (deviceDetail == NULL) { continue; }
// Setup the device interface struct
deviceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
// Try again to get the device interface detail info
if (!SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, deviceDetail, bufferSize, NULL, NULL))
{
deviceDetail = NULL;
continue;
}
break;
}
SetupDiDestroyDeviceInfoList(hDeviceInfo);
if (deviceDetail == NULL) { return(NULL); }
retVal = CreateFile(deviceDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (retVal == INVALID_HANDLE_VALUE) { return(NULL); }
return(retVal);
}
#else
int ILibDuktape_HECI_linuxInit()
{
int fd, flags;
if ((fd = open("/dev/mei", O_RDWR)) == -1 && (fd = open("/dev/mei0", O_RDWR)) == -1)
{
return(-1);
}
else
{
flags = fcntl(fd, F_GETFL, 0);
if (fcntl(fd, F_SETFL, O_NONBLOCK | flags) == -1) { printf("Failed to set O_NONBLOCK\n"); close(fd); fd = -1; }
return(fd);
}
}
#endif
duk_ret_t ILibDuktape_HECI_SessionFinalizer(duk_context *ctx)
{
if (duk_has_prop_string(ctx, 0, ILibDuktape_HECI_SessionMemPtr))
{
duk_get_prop_string(ctx, 0, ILibDuktape_HECI_SessionMemPtr);
ILibDuktape_HECI_Session *s = (ILibDuktape_HECI_Session*)Duktape_GetBuffer(ctx, -1, NULL);
if (s != NULL && s->PendingWrites != NULL) { ILibQueue_Destroy(s->PendingWrites); } // ToDo: If there is anything pending, we need to clear that too
if (s != NULL) { s->stream = NULL; }
}
return(0);
}
void ILibDuktape_HECI_Session_EmitErrorEvent(void *chain, void *session)
{
if (ILibIsRunningOnChainThread(chain) == 0) { ILibChain_RunOnMicrostackThreadEx(chain, ILibDuktape_HECI_Session_EmitErrorEvent, session); return; }
ILibDuktape_HECI_Session *s = (ILibDuktape_HECI_Session*)session;
duk_context *ctx = s->stream->readableStream->ctx;
duk_push_heapptr(ctx, s->stream->ParentObject); // [session]
duk_get_prop_string(ctx, -1, "emit"); // [session][emit]
duk_swap_top(ctx, -2); // [emit][this]
duk_push_string(ctx, "error"); // [emit][this][error]
duk_push_error_object(ctx, DUK_ERR_ERROR, "HECI Connection Error"); // [emit][this][error][err]
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onError(): "); }
duk_pop(ctx); // ...
}
void ILibDuktape_HECI_Session_EmitStreamReady(void *chain, void *session)
{
if (ILibIsRunningOnChainThread(chain) == 0) { ILibChain_RunOnMicrostackThreadEx(chain, ILibDuktape_HECI_Session_EmitStreamReady, session); return; }
ILibDuktape_DuplexStream_Ready(((ILibDuktape_HECI_Session*)session)->stream);
}
#ifdef WIN32
BOOL ILibDuktape_HECI_Session_WriteHandler_Ready(HANDLE event, ILibWaitHandle_ErrorStatus errors, void* user)
{
if (errors != ILibWaitHandle_ErrorStatus_NONE) { return(FALSE); }
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user;
DWORD bytesWritten;
if (!ILibMemory_CanaryOK(session)) { return(FALSE); }
ILibProcessPipe_WaitHandle_Remove(session->mgr, session->wv.hEvent);
if (session->noPipelining == 0)
{
ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibQueue_DeQueue(session->PendingWrites);
free(state);
}
if (GetOverlappedResult(session->descriptor, &(session->wv), &bytesWritten, FALSE) == 0)
{
// Broken Connection
ILibDuktape_HECI_Session_EmitErrorEvent(session->chain, (void*)session);
}
else
{
if (session->noPipelining == 0)
{
// Write Completed
ILibDuktape_HECI_Session_WriteHandler_Process(session);
}
}
return(TRUE);
}
#endif
ILibTransport_DoneState ILibDuktape_HECI_Session_WriteHandler_Process(ILibDuktape_HECI_Session *session)
{
ILibTransport_DoneState retVal = ILibTransport_DoneState_ERROR;
int returnIgnored = 0;
#ifdef WIN32
DWORD bytesWritten;
BOOL result = TRUE;
#else
ssize_t bytesWritten;
#endif
while (session->noPipelining || ILibQueue_GetCount(session->PendingWrites) > 0)
{
ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibQueue_PeekQueue(session->PendingWrites);
returnIgnored = state->returnIgnored;
#ifdef WIN32
if ((result = WriteFile(state->session->descriptor, state->buffer, state->bufferLen, &bytesWritten, &(state->session->wv))) == TRUE)
{
if (session->noPipelining == 0) { ILibQueue_DeQueue(state->session->PendingWrites); free(state); }
}
else
{
break;
}
#elif defined(_POSIX)
bytesWritten = write(state->session->descriptor, state->buffer + state->bufferOffset, state->bufferLen - state->bufferOffset);
if (bytesWritten > 0)
{
state->bufferOffset += bytesWritten;
if (state->bufferOffset == state->bufferLen) { ILibQueue_DeQueue(state->session->PendingWrites); free(state); retVal = session->noPipelining == 0 ? ILibTransport_DoneState_COMPLETE:ILibTransport_DoneState_INCOMPLETE; }
}
else
{
if (errno != EAGAIN)
{
// Error Occured
retVal = ILibTransport_DoneState_ERROR;
ILibDuktape_HECI_Session_EmitErrorEvent(session->chain, (void*)session);
}
else
{
retVal = ILibTransport_DoneState_INCOMPLETE;
}
break;
}
#endif
if (session->noPipelining != 0) { break; }
}
#ifdef WIN32
if (result == FALSE)
{
if (GetLastError() == ERROR_IO_PENDING)
{
// Not done writing
retVal = ILibTransport_DoneState_INCOMPLETE;
ILibProcessPipe_WaitHandle_Add(session->mgr, session->wv.hEvent, session, ILibDuktape_HECI_Session_WriteHandler_Ready);
}
else
{
// Error Occured
retVal = ILibTransport_DoneState_ERROR;
ILibDuktape_HECI_Session_EmitErrorEvent(session->chain, (void*)session);
}
}
else
{
if (session->noPipelining == 0)
{
// No more Pending Writes
retVal = ILibTransport_DoneState_COMPLETE;
if (returnIgnored != 0) { ILibDuktape_HECI_Session_EmitStreamReady(session->chain, (void*)session); }
}
else
{
retVal = ILibTransport_DoneState_INCOMPLETE;
}
}
#else
if (ILibQueue_GetCount(session->PendingWrites) == 0 && session->noPipelining == 0)
{
// No more Pending Writes
retVal = ILibTransport_DoneState_COMPLETE;
if (returnIgnored != 0) { ILibDuktape_HECI_Session_EmitStreamReady(session->chain, (void*)session); }
}
#endif
return(retVal);
}
#ifdef WIN32
void __stdcall ILibDuktape_HECI_Session_WriteHandler(ULONG_PTR obj)
{
// This Method is always dispatched from the WindowsRunLoop APC Thread
ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)obj;
ILibQueue_EnQueue(state->session->PendingWrites, state);
if (ILibQueue_GetCount(state->session->PendingWrites) == 1)
{
// No Pending Writes, so we can go ahead and send out the first block
ILibDuktape_HECI_Session_WriteHandler_Process(state->session);
}
}
#elif defined(_POSIX)
ILibTransport_DoneState ILibDuktape_HECI_Session_WriteHandler(void *chain, ILibDuktape_HECI_WriteState* state)
{
// This Method is always dispatched from the Microstack Thread
ILibQueue_EnQueue(state->session->PendingWrites, state);
if (ILibQueue_GetCount(state->session->PendingWrites) == 1)
{
return(ILibDuktape_HECI_Session_WriteHandler_Process(state->session));
}
else
{
return(ILibTransport_DoneState_INCOMPLETE);
}
}
#endif
ILibTransport_DoneState ILibDuktape_HECI_Session_WriteSink_NoPipeline(void *chain, void *user)
{
// This is always called from the Microstack Thread
ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)user;
ILibQueue_EnQueue(state->session->PendingWrites, state);
if (ILibQueue_GetCount(state->session->PendingWrites) == 1)
{
return(ILibDuktape_HECI_Session_WriteHandler_Process(state->session));
}
else
{
return(ILibTransport_DoneState_INCOMPLETE);
}
}
ILibTransport_DoneState ILibDuktape_HECI_Session_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user)
{
if ((duk_size_t)bufferLen > ((ILibDuktape_HECI_Session*)user)->bufferSize) { return(ILibTransport_DoneState_ERROR); }
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user;
ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibMemory_Allocate(bufferLen + sizeof(ILibDuktape_HECI_WriteState), 0, NULL, NULL);
state->session = session;
state->bufferLen = bufferLen;
memcpy_s(state->buffer, bufferLen, buffer, bufferLen);
if (session->noPipelining == 0)
{
#if defined(WIN32)
state->returnIgnored = 1;
QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_Session_WriteHandler, ILibProcessPipe_Manager_GetWorkerThread(session->mgr), (ULONG_PTR)state);
#elif defined(_POSIX)
if (ILibIsRunningOnChainThread(stream->readableStream->chain) != 0)
{
return(ILibDuktape_HECI_Session_WriteHandler(NULL, state));
}
else
{
state->returnIgnored = 1;
ILibChain_RunOnMicrostackThreadEx(stream->readableStream->chain, (ILibChain_StartEvent)ILibDuktape_HECI_Session_WriteHandler, state);
}
#endif
}
else
{
// We can't pipeline write requests
if (ILibIsRunningOnChainThread(stream->readableStream->chain) != 0)
{
return(ILibDuktape_HECI_Session_WriteSink_NoPipeline(stream->readableStream->chain, state));
}
else
{
state->returnIgnored = 1;
ILibChain_RunOnMicrostackThreadEx(stream->readableStream->chain, (ILibChain_StartEvent)ILibDuktape_HECI_Session_WriteSink_NoPipeline, state);
}
}
return(ILibTransport_DoneState_INCOMPLETE);
}
void ILibDuktape_HECI_Session_EndSink(ILibDuktape_DuplexStream *stream, void *user)
{
duk_context *ctx = stream->readableStream->ctx;
duk_push_this(ctx);
duk_get_prop_string(ctx, -1, "disconnect");
duk_swap_top(ctx, -2);
if (duk_pcall_method(ctx, 0) != 0) { ILibDuktape_Process_UncaughtException(ctx); }
duk_pop(ctx);
}
void ILibDuktape_HECI_Session_PauseSink(ILibDuktape_DuplexStream *sender, void *user)
{
#ifdef WIN32
// NO-OP Because we are already PAUSED, since we context switched
UNREFERENCED_PARAMETER(sender);
UNREFERENCED_PARAMETER(user);
#else
UNREFERENCED_PARAMETER(sender);
UNREFERENCED_PARAMETER(user);
#endif
}
#ifdef WIN32
BOOL ILibDuktape_HECI_Session_ReceiveSink(HANDLE event, ILibWaitHandle_ErrorStatus errors, void* user);
void __stdcall ILibDuktape_HECI_Session_ResumeSink2(ULONG_PTR obj)
{
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)obj;
BOOL result = ReadFile(session->descriptor, session->buffer, (DWORD)session->bufferSize, &(session->bytesRead), &(session->v));
if (result == TRUE || GetLastError() == ERROR_IO_PENDING)
{
ILibProcessPipe_WaitHandle_Add(session->mgr, session->v.hEvent, session, ILibDuktape_HECI_Session_ReceiveSink);
}
}
#endif
void ILibDuktape_HECI_Session_ResumeSink_NoPipeline(void *chain, void *user)
{
// This is always called from the Microstack Thread
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user;
ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibQueue_DeQueue(session->PendingWrites);
free(state);
if (ILibQueue_GetCount(session->PendingWrites) == 0)
{
ILibDuktape_HECI_Session_EmitStreamReady(session->chain, session);
}
else
{
ILibDuktape_HECI_Session_WriteHandler_Process(session);
}
}
void ILibDuktape_HECI_Session_ResumeSink(ILibDuktape_DuplexStream *sender, void *user)
{
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user;
if (session->noPipelining != 0)
{
ILibChain_RunOnMicrostackThread(sender->readableStream->chain, ILibDuktape_HECI_Session_ResumeSink_NoPipeline, session);
// Note: DO NOT 'return' here, because we still need to QueueUserAPC, to resume the stream on Windows
}
#ifdef WIN32
// To Resume, we need to first context switch to the Windows Thread
QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_Session_ResumeSink2, ILibProcessPipe_Manager_GetWorkerThread(session->mgr), (ULONG_PTR)session);
#endif
}
#ifdef WIN32
void ILibDuktape_HECI_Session_ReceiveSink2(void *chain, void *user)
{
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user;
if (!ILibMemory_CanaryOK(session)) { return; }
ILibDuktape_DuplexStream_WriteData(session->stream, session->buffer, session->bytesRead);
if (session->stream != NULL && !session->stream->readableStream->paused)
{
ILibDuktape_HECI_Session_ResumeSink(session->stream, session->stream->user);
}
}
BOOL ILibDuktape_HECI_Session_ReceiveSink(HANDLE event, ILibWaitHandle_ErrorStatus errors, void* user)
{
if (errors != ILibWaitHandle_ErrorStatus_NONE) { return(FALSE); }
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user;
if (ILibMemory_CanaryOK(session))
{
if (GetOverlappedResult(session->descriptor, &(session->v), &(session->bytesRead), FALSE) == TRUE) { ILibChain_RunOnMicrostackThreadEx(session->chain, ILibDuktape_HECI_Session_ReceiveSink2, session); }
}
return(FALSE);
}
void __stdcall ILibDuktape_HECI_Session_Start(ULONG_PTR obj)
{
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)obj;
DWORD bytesRead;
BOOL result = ReadFile(session->descriptor, session->buffer, (DWORD)session->bufferSize, &bytesRead, &(session->v));
ILibProcessPipe_WaitHandle_Add(session->mgr, session->v.hEvent, session, ILibDuktape_HECI_Session_ReceiveSink);
}
#endif
duk_ret_t ILibDuktape_HECI_create_OnClientConnect(duk_context *ctx)
{
int statusCode = duk_require_int(ctx, 0);
ILibDuktape_HECI_Session *session = NULL;
duk_dup(ctx, 2); // [Session]
if (statusCode != 0)
{
duk_get_prop_string(ctx, -1, "emit"); // [session][emit]
duk_swap_top(ctx, -2); // [emit][this]
duk_push_string(ctx, "error"); // [emit][this][error]
duk_push_error_object(ctx, DUK_ERR_ERROR, "HECI Connection Error [%d]", statusCode); // [emit][this][error][err]
duk_push_int(ctx, statusCode); duk_put_prop_string(ctx, -2, "errno");
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onError(): "); }
duk_pop(ctx); // ...
}
else
{
duk_size_t bufferLen;
char *buffer = (char*)Duktape_GetBuffer(ctx, 1, &bufferLen);
if (bufferLen > 4)
{
duk_push_int(ctx, ((int*)buffer)[0]);
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_MaxBufferSize); // [session]
session = (ILibDuktape_HECI_Session*)Duktape_PushBuffer(ctx, sizeof(ILibDuktape_HECI_Session) + ((int*)buffer)[0]); // [session][buffer]
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_SessionMemPtr); // [session]
#ifdef WIN32
session->v.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
session->wv.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
#endif
session->chain = Duktape_GetChain(ctx);
session->bufferSize = (duk_size_t)((int*)buffer)[0];
session->stream = ILibDuktape_DuplexStream_Init(ctx,
ILibDuktape_HECI_Session_WriteSink, ILibDuktape_HECI_Session_EndSink,
ILibDuktape_HECI_Session_PauseSink, ILibDuktape_HECI_Session_ResumeSink, session);
ILibDuktape_CreateReadonlyProperty_int(ctx, "maxBufferSize", (int)session->bufferSize);
session->PendingWrites = ILibQueue_Create();
duk_push_current_function(ctx);
session->noPipelining = Duktape_GetIntPropertyValue(ctx, -1, ILibDuktape_HECI_Session_NoPipeline, 0);
duk_pop(ctx);
#ifdef _POSIX
//printf("Session: %p\n", session);
duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Child); // [session][heci]
duk_get_prop_string(ctx, -1, ILibDuktape_HECI_ChainLink); // [session][heci][link]
HECI_chainLink *link = (HECI_chainLink*)duk_get_pointer(ctx, -1);
link->session = session;
duk_pop_2(ctx); // [session]
#endif
//printf("NoPipeline: %d\n", session->noPipelining);
}
else
{
// Even tho it was a success, the result buffer is invalid
duk_get_prop_string(ctx, -1, "emit"); // [session][emit]
duk_swap_top(ctx, -2); // [emit][this]
duk_push_string(ctx, "error"); // [emit][this][error]
duk_push_error_object(ctx, DUK_ERR_ERROR, "HECI Connection Error"); // [emit][this][error][err]
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onError(): "); }
duk_pop(ctx); // ...
}
}
if (session != NULL)
{
// Hookup the Send/Receive logic
#ifdef WIN32
duk_push_this(ctx); // [HECI]
session->descriptor = (HANDLE)Duktape_GetPointerProperty(ctx, -1, ILibDuktape_HECI_Descriptor);
duk_get_prop_string(ctx, -1, ILibDuktape_HECI_ChildProcess); // [HECI][childProcess]
duk_get_prop_string(ctx, -1, ILibDuktape_ChildProcess_Manager); // [HECI][childProcess][manager]
session->mgr = (ILibProcessPipe_Manager)duk_get_pointer(ctx, -1);
QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_Session_Start, ILibProcessPipe_Manager_GetWorkerThread(session->mgr), (ULONG_PTR)session);
#else
duk_push_this(ctx); // [HECI]
session->descriptor = Duktape_GetIntPropertyValue(ctx, -1, ILibDuktape_HECI_Descriptor, -1);
ILibForceUnBlockChain(session->chain);
#endif
duk_dup(ctx, 2);
duk_get_prop_string(ctx, -1, "emit"); // [session][emit]
duk_swap_top(ctx, -2); // [emit][this]
duk_push_string(ctx, "connect"); // [emit][this][connect]
if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onConnect(): "); }
duk_pop(ctx); // ...
}
return(0);
}
duk_ret_t ILibDuktape_HECI_Session_connect(duk_context *ctx)
{
int i;
int nargs = duk_get_top(ctx);
duk_push_this(ctx); // [Session]
duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Child); // [Session][HECI]
duk_remove(ctx, -2); // [HECI]
duk_get_prop_string(ctx, -1, "doIoctl"); // [HECI][func]
duk_swap_top(ctx, -2); // [doIoctl][this]
duk_get_prop_string(ctx, -1, "IOCTL"); // [doIoctl][this][IOCTL]
duk_get_prop_string(ctx, -1, "CLIENT_CONNECT"); // [doIoctl][this][IOCTL][CLIENT_CONNECT]
duk_remove(ctx, -2); // [doIoctl][this][CLIENT_CONNECT]
duk_dup(ctx, 0); // [doIoctl][this][CLIENT_CONNECT][guid]
duk_push_fixed_buffer(ctx, 16); // [doIoctl][this][CLIENT_CONNECT][guid][outBuffer]
duk_push_c_function(ctx, ILibDuktape_HECI_create_OnClientConnect, DUK_VARARGS); // [doIoctl][this][CLIENT_CONNECT][guid][outBuffer][callback]
duk_push_int(ctx, 0);
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Session_NoPipeline);
for (i = 1; i < nargs; ++i)
{
if (duk_is_function(ctx, i)) { ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter_fromThis(ctx), "connect", duk_require_heapptr(ctx, i)); }
else if (duk_is_object(ctx, i))
{
int noPipeline = Duktape_GetIntPropertyValue(ctx, i, "noPipeline", 0);
duk_push_int(ctx, noPipeline);
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Session_NoPipeline);
}
}
duk_push_this(ctx); // [doIoctl][this][CLIENT_CONNECT][guid][outBuffer][callback][Session]
duk_call_method(ctx, 5); // [retVal]
duk_pop(ctx); // ...
return(0);
}
#ifdef WIN32
void __stdcall ILibDuktape_HECI_Session_CloseSink2(ULONG_PTR obj)
{
HANDLE h = (HANDLE)obj;
CloseHandle(h);
}
#endif
duk_ret_t ILibDuktape_HECI_Session_close(duk_context *ctx)
{
duk_push_this(ctx); // [session]
if (duk_has_prop_string(ctx, -1, ILibDuktape_HECI_Child))
{
duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Child); // [session][heci]
duk_get_prop_string(ctx, -1, "disconnect"); // [session][heci][close]
duk_swap_top(ctx, -2); // [session][close][this]
duk_call_method(ctx, 0);
}
duk_push_this(ctx);
#ifdef WIN32
ILibDuktape_HECI_Session *session = NULL;
if (duk_has_prop_string(ctx, -1, ILibDuktape_HECI_SessionMemPtr))
{
duk_get_prop_string(ctx, -1, ILibDuktape_HECI_SessionMemPtr); // [HECI][SESSION]
session = (ILibDuktape_HECI_Session*)Duktape_GetBuffer(ctx, -1, NULL);
ILibProcessPipe_WaitHandle_Remove(session->mgr, session->v.hEvent);
ILibProcessPipe_WaitHandle_Remove(session->mgr, session->wv.hEvent);
session->stream = NULL;
QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_Session_CloseSink2, ILibProcessPipe_Manager_GetWorkerThread(session->mgr), (ULONG_PTR)session->descriptor);
}
#else
int d = Duktape_GetIntPropertyValue(ctx, -1, ILibDuktape_HECI_Descriptor, -1);
HECI_chainLink *hcl = (HECI_chainLink*)Duktape_GetPointerProperty(ctx, -1, ILibDuktape_HECI_ChainLink);
if (hcl != NULL)
{
hcl->descriptor = -1;
if (d != -1) { close(d); };
duk_del_prop_string(ctx, -1, ILibDuktape_HECI_Descriptor);
}
#endif
return(0);
}
duk_ret_t ILibDuktape_HECI_create(duk_context *ctx)
{
duk_push_object(ctx); // [Session]
ILibDuktape_WriteID(ctx, "heci.session");
ILibDuktape_HECI_Push(ctx, NULL); // [Session][HECI]
duk_dup(ctx, -2); // [Session][HECI][Session]
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Parent); // [Session][HECI]
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Child); // [Session]
ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_Create(ctx);
ILibDuktape_EventEmitter_CreateEventEx(emitter, "connect");
ILibDuktape_EventEmitter_CreateEventEx(emitter, "error");
ILibDuktape_CreateProperty_InstanceMethod(ctx, "connect", ILibDuktape_HECI_Session_connect, DUK_VARARGS);
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_HECI_SessionFinalizer);
ILibDuktape_CreateInstanceMethod(ctx, "disconnect", ILibDuktape_HECI_Session_close, 0);
return(1);
}
void ILibDuktape_HECI_IoctlHandler_Dispatch(void *chain, void *user)
{
ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)user;
duk_size_t count;
int i;
duk_context *ctx = data->ctx;
duk_push_heapptr(data->ctx, data->data); // [array]
duk_push_heapptr(data->ctx, data->heciObject); // [array][heci]
duk_get_prop_index(data->ctx, -2, 2); // [array][heci][callback]
duk_swap_top(data->ctx, -2); // [array][callback][this]
count = duk_get_length(data->ctx, -3);
duk_push_int(data->ctx, data->code); // [array][callback][this][status]
duk_get_prop_index(data->ctx, -4, 1); // [array][callback][this][status][buffer]
for (i = 3; i < (int)count; ++i)
{
duk_get_prop_index(data->ctx, -i - 2, i); // [array][callback][this][status][buffer][...args...]
}
if (duk_pcall_method(data->ctx, (duk_idx_t)count - 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(data->ctx, "heci.ioctlHandler_Dispatch.callback(): "); }
duk_pop_2(data->ctx); // ...
duk_push_heapptr(data->ctx, data->heciObject); // [heci]
ILibDuktape_Push_ObjectStash(data->ctx); // [heci][stash]
duk_del_prop_string(data->ctx, -1, Duktape_GetStashKey(data->data)); // (This will free data internally)
duk_pop_2(ctx); // ...
}
#ifdef WIN32
void ILibDuktape_HECI_NextIoctl(ILibQueue q);
BOOL ILibDuktape_HECI_IoctlHandler(HANDLE h, ILibWaitHandle_ErrorStatus errors, void *user)
{
if (errors == ILibWaitHandle_ErrorStatus_INVALID_HANDLE) { return(FALSE); }
ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)user;
ILibQueue Q = data->Q;
if (errors == ILibWaitHandle_ErrorStatus_NONE)
{
BOOL result = GetOverlappedResult(data->device, &(data->v), &(data->bytesReceived), FALSE);
data->code = result == TRUE ? 0 : (int)GetLastError();
}
else
{
data->code = -1;
}
ILibQueue_DeQueue(data->Q);
ILibProcessPipe_WaitHandle_Remove(data->pipeManager, h);
ILibChain_RunOnMicrostackThread(data->chain, ILibDuktape_HECI_IoctlHandler_Dispatch, data);
if (ILibQueue_GetCount(Q) > 0)
{
void ILibDuktape_HECI_NextIoctl(Q);
}
return(TRUE);
}
void ILibDuktape_HECI_NextIoctl(ILibQueue q)
{
ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)ILibQueue_PeekQueue(q);
int res;
if (data == NULL) { return; } // This line is unnecessary, because this method is only called on a non-empty Queue, but to satisfy Klockwork...
data->bytesReceived = 0;
ResetEvent(data->v.hEvent);
res = DeviceIoControl(data->device, (DWORD)data->code, data->buffer, (DWORD)data->bufferLen, data->outBuffer, (DWORD)data->outBufferLen, &(data->bytesReceived), &(data->v));
ILibProcessPipe_WaitHandle_Add_WithNonZeroTimeout(data->pipeManager, data->v.hEvent, 2000, data, ILibDuktape_HECI_IoctlHandler);
}
void __stdcall ILibDuktape_HECI_apc_AddIoctl(ULONG_PTR obj)
{
ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)obj;
ILibQueue_EnQueue(data->Q, data);
if (ILibQueue_GetCount(data->Q) == 1)
{
ILibDuktape_HECI_NextIoctl(data->Q);
}
}
#endif
#ifdef _POSIX
void ILibDuktape_HECI_AddIoctl(ILibDuktape_HECI_ioctl_data *data)
{
ILibQueue_EnQueue(data->Q, data);
if (ILibQueue_GetCount(data->Q) == 1)
{
ILibForceUnBlockChain(data->chain);
}
}
#endif
duk_ret_t ILibDuktape_HECI_doIoctl(duk_context *ctx)
{
int code = duk_require_int(ctx, 0);
duk_size_t bufferLen = 0;
char *buffer = duk_is_null(ctx, 1) ? NULL : (char*)Duktape_GetBuffer(ctx, 1, &bufferLen);
int nargs = duk_get_top(ctx);
int i;
ILibQueue Q;
duk_size_t outBufferLen;
char *outBuffer;
int cbx;
if (duk_is_buffer(ctx, 2) || duk_is_buffer_data(ctx, 2))
{
outBuffer = (char*)Duktape_GetBuffer(ctx, 2, &outBufferLen);
cbx = 3;
}
else
{
outBuffer = NULL;
outBufferLen = 0;
cbx = 2;
}
#ifdef _POSIX
if (outBuffer == NULL)
{
outBuffer = buffer;
outBufferLen = bufferLen;
}
else
{
if (bufferLen < outBufferLen) { return(ILibDuktape_Error(ctx, "HECI.doIoctl(): Output Buffer too small")); }
memcpy_s(outBuffer, outBufferLen, buffer, bufferLen);
}
#endif
duk_require_function(ctx, cbx);
duk_push_this(ctx); // [heci]
duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Q); // [heci][q]
Q = (ILibQueue)duk_get_pointer(ctx, -1);
duk_pop(ctx); // [heci]
ILibDuktape_Push_ObjectStash(ctx); // [heci][stash]
duk_push_array(ctx); // [heci][stash][array]
ILibDuktape_HECI_ioctl_data *data;
data = (ILibDuktape_HECI_ioctl_data*)Duktape_PushBuffer(ctx, bufferLen + sizeof(ILibDuktape_HECI_ioctl_data));
duk_put_prop_index(ctx, -2, 0); // [heci][stash][array]
if (outBufferLen > 0)
{ // [heci][stash][array][buffer]
duk_dup(ctx, 2);
}
else
{
duk_push_null(ctx); // [heci][stash][array][buffer]
}
duk_put_prop_index(ctx, -2, 1); // [heci][stash][array]
data->ctx = ctx;
duk_dup(ctx, cbx); // [heci][stash][array][callback]
duk_put_prop_index(ctx, -2, 2); // [heci][stash][array]
#ifdef WIN32
duk_get_prop_string(ctx, -3, ILibDuktape_HECI_IoctlWaitHandle); // [heci][stash][array][handle]
data->v.hEvent = (HANDLE)duk_get_pointer(ctx, -1);
duk_pop(ctx); // [heci][stash][array]
#endif
duk_get_prop_string(ctx, -3, ILibDuktape_HECI_Descriptor); // [heci][stash][array][descriptor]
#ifdef WIN32
data->device = (HANDLE)duk_get_pointer(ctx, -1);
#elif defined(_POSIX)
data->device = duk_get_int(ctx, -1);
#endif
duk_pop(ctx); // [heci][stash][array]
data->chain = Duktape_GetChain(ctx);
data->Q = Q;
data->code = code;
data->outBuffer = outBuffer;
data->outBufferLen = outBufferLen;
data->heciObject = duk_get_heapptr(ctx, -3);
data->bufferLen = bufferLen;
data->data = duk_get_heapptr(ctx, -1);
memcpy_s(data->buffer, bufferLen, buffer, bufferLen);
for (i = cbx + 1; i < nargs; ++i)
{
duk_dup(ctx, i); // [heci][stash][array][object]
duk_put_prop_index(ctx, -2, i-1); // [heci][stash][array]
}
duk_put_prop_string(ctx, -2, Duktape_GetStashKey(duk_get_heapptr(ctx, -1))); // [heci][stash]
#ifdef WIN32
duk_get_prop_string(ctx, -2, ILibDuktape_HECI_ChildProcess); // [heci][stash][childProcess]
duk_get_prop_string(ctx, -1, ILibDuktape_ChildProcess_Manager); // [heci][stash][childProcess][manager]
data->pipeManager = (ILibProcessPipe_Manager)duk_get_pointer(ctx, -1);
QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_apc_AddIoctl, ILibProcessPipe_Manager_GetWorkerThread(data->pipeManager), (ULONG_PTR)data);
#elif defined(_POSIX)
ILibDuktape_HECI_AddIoctl(data);
#endif
return(0);
}
duk_ret_t ILibDuktape_HECI_Finalizer(duk_context *ctx)
{
#ifdef WIN32
HANDLE h = Duktape_GetPointerProperty(ctx, 0, ILibDuktape_HECI_IoctlWaitHandle);
if (h != NULL) { CloseHandle(h); }
#endif
if (duk_has_prop_string(ctx, 0, ILibDuktape_HECI_Q))
{
duk_get_prop_string(ctx, 0, ILibDuktape_HECI_Q);
ILibQueue_Destroy((ILibQueue)duk_get_pointer(ctx, -1));
}
#ifdef _POSIX
if (duk_has_prop_string(ctx, 0, ILibDuktape_HECI_ChainLink))
{
duk_get_prop_string(ctx, 0, ILibDuktape_HECI_ChainLink);
HECI_chainLink *h = (HECI_chainLink*)duk_get_pointer(ctx, -1);
h->ctx = NULL;
h->heciObject = NULL;
ILibChain_SafeRemove(h->link.ParentChain, h);
}
#endif
return(0);
}
#if !defined(WIN32) && !defined(__APPLE__)
void ILibDuktape_HECI_PreSelect(void* object, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime)
{
int result;
HECI_chainLink *h = (HECI_chainLink*)object;
//printf("h = %p, descriptor = %d, paused = %d, session = %p\n", (void*)h, h->descriptor, h->paused, (void*)h->session);
if (h->descriptor <= 0) { return; }
if (h->paused == 0 && h->session != NULL) { FD_SET(h->descriptor, readset); }
if (h->session != NULL && ILibQueue_GetCount(h->session->PendingWrites) > 0) { FD_SET(h->descriptor, writeset); }
while (ILibQueue_GetCount(h->Q) > 0 && h->paused == 0)
{
ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)ILibQueue_DeQueue(h->Q);
switch (data->code)
{
case 0x00:
break;
case 0x01:
case 0x02:
case 0x03:
result = ioctl(h->descriptor, _IOC(_IOC_READ | _IOC_WRITE, 'H', data->code, data->outBufferLen), data->outBuffer);
data->code = result ? errno : 0;
ILibDuktape_HECI_IoctlHandler_Dispatch(NULL, data);
break;
default:
break;
}
}
}
void ILibDuktape_HECI_PostSelect(void* object, int slct, fd_set *readset, fd_set *writeset, fd_set *errorset)
{
HECI_chainLink *h = (HECI_chainLink*)object;
if (h->descriptor <= 0) { return; }
if (FD_ISSET(h->descriptor, readset))
{
//printf("session = %p\n", (void*)h->session);
//printf("Attempting to read: %d bytes from %p\n", h->session->bufferSize, (void*)h->session->buffer);
int bytesRead = read(h->descriptor, h->session->buffer, h->session->bufferSize);
if (bytesRead >= 0)
{
ILibDuktape_DuplexStream_WriteData(h->session->stream, h->session->buffer, bytesRead);
}
else
{
ILibDuktape_DuplexStream_WriteEnd(h->session->stream);
}
}
if (h->descriptor <= 0) { return; }
if (FD_ISSET(h->descriptor, writeset))
{
ILibDuktape_HECI_Session_WriteHandler_Process(h->session);
}
}
void ILibDuktape_HECI_Destroy(void *object)
{
HECI_chainLink *h = (HECI_chainLink*)object;
if (h->ctx != NULL && h->heciObject != NULL)
{
duk_push_heapptr(h->ctx, h->heciObject); // [heci]
duk_del_prop_string(h->ctx, -1, ILibDuktape_HECI_ChainLink);
duk_pop(h->ctx); // ...
}
close(h->descriptor);
}
#endif
void ILibDuktape_HECI_Push(duk_context *ctx, void *chain)
{
duk_push_object(ctx); // [HECI]
ILibDuktape_WriteID(ctx, "heci");
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_HECI_Finalizer);
#ifdef WIN32
HANDLE h = ILibDuktape_HECI_windowsInit();
if (h == NULL) { ILibDuktape_Error(ctx, "Error initializing HECI"); return; }
duk_push_pointer(ctx, h); // [HECI][HANDLE]
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Descriptor); // [HECI]
if (duk_peval_string(ctx, "require('child_process');") != 0) // [HECI][child_process]
{
ILibDuktape_Error(ctx, "Error instantiating dependency 'child_process'");
return;
}
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_ChildProcess); // [HECI]
duk_push_pointer(ctx, CreateEvent(NULL, TRUE, FALSE, NULL));
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_IoctlWaitHandle); // [HECI]
#elif defined(_POSIX) && !defined(__APPLE__)
int h = ILibDuktape_HECI_linuxInit();
if (h < 0) { ILibDuktape_Error(ctx, "error initializing HECI"); return; }
duk_push_int(ctx, h); // [HECI][descriptor]
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Descriptor); // [HECI]
HECI_chainLink *hlink = ILibMemory_Allocate(sizeof(HECI_chainLink), 0, NULL, NULL);
hlink->ctx = ctx;
hlink->descriptor = h;
hlink->link.PreSelectHandler = ILibDuktape_HECI_PreSelect;
hlink->link.PostSelectHandler = ILibDuktape_HECI_PostSelect;
hlink->link.DestroyHandler = ILibDuktape_HECI_Destroy;
hlink->Q = ILibQueue_Create();
duk_push_pointer(ctx, hlink); // [HECI][link]
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_ChainLink); // [HECI]
ILibChain_SafeAdd(Duktape_GetChain(ctx), hlink);
#endif
if (chain != NULL) { ILibDuktape_CreateInstanceMethod(ctx, "create", ILibDuktape_HECI_create, 0); }
ILibDuktape_CreateInstanceMethod(ctx, "doIoctl", ILibDuktape_HECI_doIoctl, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "disconnect", ILibDuktape_HECI_Session_close, 0);
#if defined(_POSIX) && !defined(__APPLE__)
duk_push_pointer(ctx, hlink->Q); // [HECI][Q]
#else
duk_push_pointer(ctx, ILibQueue_Create()); // [HECI][Q]
#endif
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Q); // [HECI]
duk_push_object(ctx);
#ifdef WIN32
ILibDuktape_CreateReadonlyProperty_int(ctx, "HECI_VERSION", (int)(CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)));
ILibDuktape_CreateReadonlyProperty_int(ctx, "CLIENT_CONNECT", (int)(CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)));
#elif defined(_POSIX)
ILibDuktape_CreateReadonlyProperty_int(ctx, "HECI_VERSION", (int)0x00);
ILibDuktape_CreateReadonlyProperty_int(ctx, "CLIENT_CONNECT", (int)0x01);
#endif
ILibDuktape_CreateReadonlyProperty(ctx, "IOCTL");
duk_push_object(ctx);
duk_peval_string(ctx, "Buffer.from('DBA4336776047B4EB3AFBCFC29BEE7A7', 'hex');");
ILibDuktape_CreateReadonlyProperty(ctx, "LME");
duk_peval_string(ctx, "Buffer.from('2800F812B7B42D4BACA846E0FF65814C', 'hex');");
ILibDuktape_CreateReadonlyProperty(ctx, "AMT");
ILibDuktape_CreateReadonlyProperty(ctx, "GUIDS");
} // KLOCKWORK: We are not losing reference to created Event... It is freed in the object finalizer 'ILibDuktape_HECI_Finalizer'
void ILibDuktape_HECI_Init(duk_context *ctx)
{
ILibDuktape_ModSearch_AddHandler(ctx, "heci", ILibDuktape_HECI_Push);
}