OVMS3/OVMS.V3/components/ovms_script/srcduk/ovms_duktape.h

337 lines
11 KiB
C++

/*
; Project: Open Vehicle Monitor System
; Date: 14th March 2017
;
; Changes:
; 1.0 Initial release
;
; (C) 2011 Michael Stegen / Stegen Electronics
; (C) 2011-2017 Mark Webb-Johnson
; (C) 2011 Sonny Chen @ EPRO/DX
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
*/
#ifndef __OVMS_DUKTAPE_H__
#define __OVMS_DUKTAPE_H__
#include "ovms_command.h"
#include "ovms_utils.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "duktape.h"
#include <list>
#include <utility>
////////////////////////////////////////////////////////////////////////////////
// DukContext: C++ wrapper for duk_context
class DukContext
{
public:
DukContext(duk_context *ctx) { m_ctx = ctx; }
~DukContext() {}
void Push(bool val) { duk_push_boolean(m_ctx, val); }
void Push(int val) { duk_push_number(m_ctx, val); }
void Push(short val) { duk_push_number(m_ctx, val); }
void Push(long val) { duk_push_number(m_ctx, val); }
void Push(unsigned int val) { duk_push_uint(m_ctx, val); }
void Push(unsigned short val) { duk_push_uint(m_ctx, val); }
void Push(unsigned long val) { duk_push_uint(m_ctx, val); }
void Push(float val) { duk_push_number(m_ctx, float2double(val)); }
void Push(double val) { duk_push_number(m_ctx, val); }
void Push(const char* val) { duk_push_string(m_ctx, val); }
void Push(const std::string &val) { duk_push_lstring(m_ctx, val.data(), val.size()); }
void Push(const extram::string &val) { duk_push_lstring(m_ctx, val.data(), val.size()); }
// Push string as Uint8Array:
void PushBinary(const std::string &val)
{
void* p = duk_push_fixed_buffer(m_ctx, val.size());
if (p) memcpy(p, val.data(), val.size());
}
void PushBinary(const extram::string &val)
{
void* p = duk_push_fixed_buffer(m_ctx, val.size());
if (p) memcpy(p, val.data(), val.size());
}
duk_idx_t PushArray() { return duk_push_array(m_ctx); }
duk_idx_t PushObject() { return duk_push_object(m_ctx); }
duk_bool_t PutProp(duk_idx_t obj_idx, duk_uarridx_t arr_idx)
{ return duk_put_prop_index(m_ctx, obj_idx, arr_idx); }
duk_bool_t PutProp(duk_idx_t obj_idx, const char* prop_name)
{ return duk_put_prop_string(m_ctx, obj_idx, prop_name); }
public:
duk_context *m_ctx;
};
////////////////////////////////////////////////////////////////////////////////
// Duktape Task Commands
typedef enum
{
DUKTAPE_none = 0, // Do nothing
DUKTAPE_reload, // Reload DukTape engine
DUKTAPE_compact, // Compact DukTape memory
DUKTAPE_event, // Event
DUKTAPE_autoinit, // Auto init
DUKTAPE_evalnoresult, // Execute script text (without result)
DUKTAPE_evalfloatresult, // Execute script text (float result)
DUKTAPE_evalintresult, // Execute script text (int result)
DUKTAPE_callback, // DuktapeObject callback
} duktape_msg_t;
////////////////////////////////////////////////////////////////////////////////
// DuktapeObjectRegistration
typedef struct
{
duk_c_function func;
duk_idx_t nargs;
} duktape_registerfunction_t;
typedef std::map<const char*, duktape_registerfunction_t*, CmpStrOp> DuktapeFunctionMap;
typedef struct
{
const char* start;
size_t length;
} duktape_registermodule_t;
typedef std::map<const char*, duktape_registermodule_t*, CmpStrOp> DuktapeModuleMap;
class DuktapeObjectRegistration
{
public:
DuktapeObjectRegistration(const char* name);
~DuktapeObjectRegistration();
public:
const char* GetName();
void RegisterDuktapeFunction(duk_c_function func, duk_idx_t nargs, const char* name);
public:
void RegisterWithDuktape(duk_context* ctx);
protected:
const char* m_name;
DuktapeFunctionMap m_fnmap;
};
typedef std::map<const char*, DuktapeObjectRegistration*, CmpStrOp> DuktapeObjectMap;
class DuktapeObject;
class OvmsScripts;
typedef struct
{
union
{
struct
{
const char* name;
void* data;
} dt_event;
struct
{
const char* text;
const char* filename;
} dt_evalnoresult;
struct
{
const char* text;
float* result;
} dt_evalfloatresult;
struct
{
const char* text;
int* result;
} dt_evalintresult;
struct
{
DuktapeObject* instance;
const char* method;
void* data;
} dt_callback;
} body;
duktape_msg_t type;
QueueHandle_t waitcompletion;
OvmsWriter* writer;
} duktape_queue_t;
////////////////////////////////////////////////////////////////////////////////
// DuktapeObject: coupled C++ / JS object
//
// Intended for API methods to attach internal API state to a JS object and provide
// a standard callback invocation interface for JS objects in local scopes.
//
// - Override CallMethod() to implement specific method calls
// - Override Finalize() for specific destruction in JS context (garbage collection)
// - call Register() to prevent normal garbage collection (but not heap destruction)
// - call Ref() to protect against deletion (reference count)
// - call Lock() to protect concurrent access (recursive mutex)
//
// - GetInstance() retrieves the DuktapeObject associated with a JS object if any
// - Push() pushes the JS object onto the Duktape stack
//
// Note: the DuktapeObject may persist after the JS object has been finalized, e.g.
// if some callbacks are pending after the Duktape heap has been destroyed.
// Use IsCoupled() to check if the JS object is still available.
//
// Ref/Unref:
// Normal life cycle is from construction to finalization. Pending callbacks extend
// the life until the last callback has been processed. A subclass may extend the life
// by calling Ref(), which increases the reference count. Unref() deletes the instance
// if no references are left.
class DuktapeObject
{
friend OvmsScripts;
public:
DuktapeObject();
DuktapeObject(duk_context *ctx, int obj_idx);
virtual ~DuktapeObject();
public:
bool Lock(TickType_t timeout = portMAX_DELAY) { return m_mutex.Lock(timeout); }
void Unlock() { m_mutex.Unlock(); }
public:
void Ref();
bool Unref();
public:
bool Couple(duk_context *ctx, int obj_idx);
void Decouple(duk_context *ctx);
bool IsCoupled() { return m_object != NULL; }
static DuktapeObject* GetInstance(duk_context *ctx, int obj_idx);
protected:
static duk_ret_t DuktapeFinalizer(duk_context *ctx);
virtual void Finalize(duk_context *ctx, bool heapDestruct);
public:
void Register(duk_context *ctx);
void Register(duk_context *ctx, int obj_idx);
void Deregister(duk_context *ctx);
bool IsRegistered() { return m_registered; }
public:
void RequestCallback(const char* method, void* data=NULL);
duk_ret_t DuktapeCallback(duk_context *ctx, duktape_queue_t &msg);
virtual duk_ret_t CallMethod(duk_context *ctx, const char* method, void* data=NULL);
public:
duk_idx_t Push(duk_context *ctx);
protected:
OvmsRecMutex m_mutex; // concurrency lock
uint32_t m_refcnt = 0; // references to this object (1→0 = deletion)
void* m_object = NULL; // heapptr to JS object
bool m_registered = false; // registration state (true = GC prevented)
void* m_registry = NULL; // optional heapptr to specific registry JS object
};
////////////////////////////////////////////////////////////////////////////////
// DuktapeConsoleCommand
class DuktapeConsoleCommand : public DuktapeObject
{
public:
DuktapeConsoleCommand(duk_context *ctx, int obj_idx, OvmsCommand* cmd, const char* module);
~DuktapeConsoleCommand();
public:
OvmsCommand* m_cmd;
std::string m_module;
};
////////////////////////////////////////////////////////////////////////////////
// OvmsDuktape
//
// Container for object registration and the main Duktape task
class OvmsDuktape
{
public:
OvmsDuktape();
~OvmsDuktape();
public:
void RegisterDuktapeFunction(duk_c_function func, duk_idx_t nargs, const char* name);
void RegisterDuktapeModule(const char* start, size_t length, const char* name);
duktape_registermodule_t* FindDuktapeModule(const char* name);
void RegisterDuktapeObject(DuktapeObjectRegistration* ob);
void AutoInitDuktape();
protected:
bool DuktapeDispatch(duktape_queue_t* msg, TickType_t queuewait=portMAX_DELAY);
void DuktapeDispatchWait(duktape_queue_t* msg);
public:
void DuktapeEvalNoResult(const char* text, OvmsWriter* writer=NULL, const char* filename=NULL);
float DuktapeEvalFloatResult(const char* text, OvmsWriter* writer=NULL);
int DuktapeEvalIntResult(const char* text, OvmsWriter* writer=NULL);
void DuktapeReload();
void DuktapeCompact(bool wait=true);
void DuktapeRequestCallback(DuktapeObject* instance, const char* method, void* data);
OvmsWriter* GetDuktapeWriter();
void DukGetCallInfo(duk_context *ctx, std::string *filename, int *linenumber, std::string *function);
public:
void NotifyDuktapeModuleLoad(const char* filename);
void NotifyDuktapeModuleUnload(const char* filename);
void NotifyDuktapeModuleUnloadAll();
bool RegisterDuktapeConsoleCommand(duk_context *ctx, int obj_idx,
const char* filename,
const char* parent,
const char* name, const char* title,
const char *usage = "", int min = 0, int max = 0);
public:
void DukTapeInit();
void DukTapeTask();
bool DukTapeAvailable() { return m_dukctx != NULL; }
duk_context* DukTapeContext() { return m_dukctx; }
void EventScript(std::string event, void* data);
protected:
duk_context* m_dukctx;
TaskHandle_t m_duktaskid;
QueueHandle_t m_duktaskqueue;
DuktapeFunctionMap m_fnmap;
DuktapeModuleMap m_modmap;
DuktapeObjectMap m_obmap;
public:
typedef std::map<OvmsCommand*, DuktapeConsoleCommand*> DuktapeCommandMap;
DuktapeCommandMap m_cmdmap;
};
extern void DukOvmsErrorHandler(duk_context *ctx, duk_idx_t err_idx, OvmsWriter *writer=NULL, const char *filename=NULL);
extern OvmsDuktape MyDuktape;
#endif //#ifndef __OVMS_DUKTAPE_H__