380 lines
16 KiB
C++
380 lines
16 KiB
C++
/*
|
|
* mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
* Copyright (C) 2013-2014, Peter Olsson <peter@olssononline.se>
|
|
*
|
|
* Version: MPL 1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (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.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Peter Olsson <peter@olssononline.se>
|
|
* Portions created by the Initial Developer are Copyright (C)
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Peter Olsson <peter@olssononline.se>
|
|
*
|
|
* javascript.hpp -- Header file for main JavaScript classes
|
|
*
|
|
*/
|
|
|
|
#ifndef V8_JAVASCRIPT_H
|
|
#define V8_JAVASCRIPT_H
|
|
|
|
#include <stdint.h>
|
|
#include <v8.h>
|
|
#if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
|
|
#include <libplatform/libplatform.h>
|
|
#include <v8-util.h>
|
|
#endif
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <set>
|
|
#include <assert.h>
|
|
|
|
/* Enable this define enable V8 debugging protocol, this is not yet working */
|
|
//#define V8_ENABLE_DEBUGGING
|
|
|
|
/*
|
|
* Enable this define to force a GC after the script has finished execution.
|
|
* This is only to help debug memory leaks, and should not be needed for anything else
|
|
*/
|
|
//#define V8_FORCE_GC_AFTER_EXECUTION
|
|
|
|
|
|
/* Macro for easy V8 "get property" callback definition */
|
|
#define JS_GET_PROPERTY_DEF(method_name, class_name) \
|
|
static void method_name(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info)\
|
|
{\
|
|
JS_CHECK_SCRIPT_STATE();\
|
|
class_name *obj = JSBase::GetInstance<class_name>(info.Holder());\
|
|
if (obj) {\
|
|
obj->method_name##Impl(property, info);\
|
|
} else {\
|
|
int line;\
|
|
char *file = JSMain::GetStackInfo(info.GetIsolate(), &line);\
|
|
v8::String::Utf8Value str(info.Holder());\
|
|
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "mod_v8", line, NULL, SWITCH_LOG_DEBUG, "No valid internal data available for %s when calling %s\n", *str ? *str : "[unknown]", #class_name "::" #method_name "()");\
|
|
free(file);\
|
|
info.GetReturnValue().Set(false);\
|
|
}\
|
|
}\
|
|
void method_name##Impl(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info)
|
|
|
|
/* Macro for easy V8 "set property" callback definition */
|
|
#define JS_SET_PROPERTY_DEF(method_name, class_name) \
|
|
static void method_name(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)\
|
|
{\
|
|
JS_CHECK_SCRIPT_STATE();\
|
|
class_name *obj = JSBase::GetInstance<class_name>(info.Holder());\
|
|
if (obj) {\
|
|
obj->method_name##Impl(property, value, info);\
|
|
} else {\
|
|
int line;\
|
|
char *file = JSMain::GetStackInfo(info.GetIsolate(), &line);\
|
|
v8::String::Utf8Value str(info.Holder());\
|
|
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "mod_v8", line, NULL, SWITCH_LOG_DEBUG, "No valid internal data available for %s when calling %s\n", *str ? *str : "[unknown]", #class_name "::" #method_name "()");\
|
|
free(file);\
|
|
info.GetReturnValue().Set(false);\
|
|
}\
|
|
}\
|
|
void method_name##Impl(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
|
|
|
|
/* Macro for easy V8 "function" callback definition */
|
|
#define JS_FUNCTION_DEF(method_name, class_name) \
|
|
static void method_name(const v8::FunctionCallbackInfo<v8::Value>& info)\
|
|
{\
|
|
JS_CHECK_SCRIPT_STATE();\
|
|
class_name *obj = JSBase::GetInstance<class_name>(info.Holder());\
|
|
if (obj) {\
|
|
obj->method_name##Impl(info);\
|
|
} else {\
|
|
int line;\
|
|
char *file = JSMain::GetStackInfo(info.GetIsolate(), &line);\
|
|
v8::String::Utf8Value str(info.Holder());\
|
|
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "mod_v8", line, NULL, SWITCH_LOG_DEBUG, "No valid internal data available for %s when calling %s\n", *str ? *str : "[unknown]", #class_name "::" #method_name "()");\
|
|
free(file);\
|
|
info.GetReturnValue().Set(false);\
|
|
}\
|
|
}\
|
|
void method_name##Impl(const v8::FunctionCallbackInfo<v8::Value>& info)
|
|
|
|
/* Macros for V8 callback implementations */
|
|
#define JS_GET_PROPERTY_IMPL(method_name, class_name) void class_name::method_name##Impl(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info)
|
|
#define JS_SET_PROPERTY_IMPL(method_name, class_name) void class_name::method_name##Impl(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
|
|
#define JS_FUNCTION_IMPL(method_name, class_name) void class_name::method_name##Impl(const v8::FunctionCallbackInfo<v8::Value>& info)
|
|
|
|
/* Macros for V8 callback definitions (class static version) */
|
|
#define JS_GET_PROPERTY_DEF_STATIC(method_name) static void method_name(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info)
|
|
#define JS_SET_PROPERTY_DEF_STATIC(method_name) static void method_name(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
|
|
#define JS_FUNCTION_DEF_STATIC(method_name) static void method_name(const v8::FunctionCallbackInfo<v8::Value>& info)
|
|
|
|
/* Macros for V8 callback implementations (class static version) */
|
|
#define JS_GET_PROPERTY_IMPL_STATIC(method_name, class_name) void class_name::method_name(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info)
|
|
#define JS_SET_PROPERTY_IMPL_STATIC(method_name, class_name) void class_name::method_name(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
|
|
#define JS_FUNCTION_IMPL_STATIC(method_name, class_name) void class_name::method_name(const v8::FunctionCallbackInfo<v8::Value>& info)
|
|
|
|
/* Macro for basic script state check (to know if the script is being terminated), should be called before calling any callback actual code */
|
|
#define JS_CHECK_SCRIPT_STATE() \
|
|
if (info.GetIsolate()->IsExecutionTerminating()) return;\
|
|
if (JSMain::GetScriptInstanceFromIsolate(info.GetIsolate()) && JSMain::GetScriptInstanceFromIsolate(info.GetIsolate())->GetForcedTermination()) return
|
|
|
|
/* Macro for easy unlocking an isolate on a long running c call */
|
|
#define JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(call) {\
|
|
/* Unlock isolate on a long running c call to let another thread execute the callback */\
|
|
info.GetIsolate()->Exit();\
|
|
Unlocker unlock(info.GetIsolate());\
|
|
call;\
|
|
}\
|
|
/* Lock it back */\
|
|
info.GetIsolate()->Enter();
|
|
|
|
/* strdup function for all platforms */
|
|
#ifdef NDEBUG
|
|
#if (_MSC_VER >= 1500) // VC9+
|
|
#define js_strdup(ptr, s) (void)( (!!(ptr = _strdup(s))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%d", __FILE__, __LINE__),abort(), 0), ptr)
|
|
#else
|
|
#define js_strdup(ptr, s) (void)( (!!(ptr = strdup(s))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%d", __FILE__, __LINE__),abort(), 0), ptr)
|
|
#endif
|
|
#else
|
|
#if (_MSC_VER >= 1500) // VC9+
|
|
#define js_strdup(ptr, s) (void)(assert(((ptr) = _strdup(s))),ptr);__analysis_assume( ptr )
|
|
#else
|
|
#define js_strdup(ptr, s) (void)(assert(((ptr) = strdup((s)))),ptr)
|
|
#endif
|
|
#endif
|
|
|
|
/* Makes sure to return a valid char pointer */
|
|
#define js_safe_str(s) (s ? s : "")
|
|
|
|
/* JS Constructor callback definition */
|
|
typedef void * void_pointer_t;
|
|
typedef void_pointer_t (*ConstructorCallback)(const v8::FunctionCallbackInfo<v8::Value>& info);
|
|
|
|
/* JS Function definition */
|
|
typedef struct {
|
|
const char *name; /* Name of the function */
|
|
v8::FunctionCallback func; /* Function callback */
|
|
} js_function_t;
|
|
|
|
/* JS Property definition */
|
|
typedef struct {
|
|
const char *name; /* Name of the property */
|
|
v8::AccessorGetterCallback get; /* The property getter */
|
|
v8::AccessorSetterCallback set; /* The property setter */
|
|
} js_property_t;
|
|
|
|
/* JS Class definition */
|
|
typedef struct {
|
|
const char *name; /* The name of the class */
|
|
ConstructorCallback constructor; /* The constructor definition */
|
|
const js_function_t *functions; /* An array of function definitions */
|
|
const js_property_t *properties; /* An array of property definitions */
|
|
} js_class_definition_t;
|
|
|
|
/* Import/export definitions (used by extra loadable modules) */
|
|
#ifdef WIN32
|
|
/* WIN32 */
|
|
#ifdef JSMOD_IMPORT
|
|
#define JSMOD_EXPORT __declspec(dllimport)
|
|
#else
|
|
#define JSMOD_EXPORT __declspec(dllexport)
|
|
#endif
|
|
#else
|
|
/* Not WIN32 */
|
|
#ifdef JSMOD_IMPORT
|
|
#define JSMOD_EXPORT
|
|
#else
|
|
#if (HAVE_VISIBILITY != 1)
|
|
#define JSMOD_EXPORT
|
|
#else
|
|
#define JSMOD_EXPORT __attribute__ ((visibility("default")))
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
/* JSMain class prototype */
|
|
class JSMOD_EXPORT JSMain;
|
|
|
|
/* Base class used by all C++ classes implemented in JS */
|
|
class JSMOD_EXPORT JSBase
|
|
{
|
|
private:
|
|
v8::Persistent<v8::Object> *persistentHandle; /* The persistent handle of the JavaScript object for this instance */
|
|
bool autoDestroy; /* flag to tell if this instance should be auto destroyed during JavaScript GC */
|
|
JSMain *js; /* The "owner" of this instance */
|
|
|
|
/* The callback that happens when the V8 GC cleans up object instances */
|
|
#if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
|
|
static void WeakCallback(const v8::WeakCallbackInfo<JSBase>& data);
|
|
#else
|
|
static void WeakCallback(const v8::WeakCallbackData<v8::Object, JSBase>& data);
|
|
#endif
|
|
|
|
/* Internal basic constructor when creating a new instance from JS. It will call the actual user code inside */
|
|
static void CreateInstance(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
|
|
/* Store a C++ instance to a JS object's private data */
|
|
static void AddInstance(v8::Isolate *isolate, const v8::Handle<v8::Object>& handle, const v8::Handle<v8::External>& object, bool autoDestroy);
|
|
public:
|
|
JSBase(JSMain *owner);
|
|
JSBase(const v8::FunctionCallbackInfo<v8::Value>& info);
|
|
virtual ~JSBase(void);
|
|
|
|
/* Returns the JS object related to the C++ instance */
|
|
v8::Handle<v8::Object> GetJavaScriptObject();
|
|
|
|
/* Register a C++ class inside V8 (must be called within a entered isolate, and context) */
|
|
static void Register(v8::Isolate *isolate, const js_class_definition_t *desc);
|
|
|
|
/* Register an existing C++ class instance inside V8 (must be called within a entered isolate, and context) */
|
|
void RegisterInstance(v8::Isolate *isolate, std::string name, bool autoDestroy);
|
|
|
|
/* Get a JSBase instance from JavaScript callback arguments */
|
|
template <typename T> static T *GetInstance(const v8::FunctionCallbackInfo<v8::Value>& info)
|
|
{
|
|
v8::HandleScope scope(info.GetIsolate());
|
|
return GetInstance<T>(info.Holder());
|
|
}
|
|
|
|
/* Get a JSBase instance from a JavaScript object */
|
|
template <typename T> static T *GetInstance(const v8::Local<v8::Object>& self)
|
|
{
|
|
v8::Local<v8::Value> val = self->GetInternalField(0);
|
|
|
|
if (!val.IsEmpty() && val->IsExternal()) {
|
|
v8::Local<v8::External> wrap = v8::Local<v8::External>::Cast(val);
|
|
JSBase *ptr = static_cast<JSBase*>(wrap->Value());
|
|
return dynamic_cast<T*>(ptr); /* If we're trying to cast to the wrong type, dynamic_cast will return NULL */
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Get a JavaScript function from a JavaScript argument (can be either a string or the actual function) */
|
|
static v8::Handle<v8::Function> GetFunctionFromArg(v8::Isolate *isolate, const v8::Local<v8::Value>& arg);
|
|
|
|
/* Default JS setter callback, to be used for read only values */
|
|
static void DefaultSetProperty(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info);
|
|
|
|
/* Get the name of the JavaScript class - must be overridden by the actual implementation */
|
|
virtual std::string GetJSClassName() = 0;
|
|
|
|
/* Get the JavaScript class instance that owns this instance */
|
|
JSMain *GetOwner();
|
|
|
|
/* Get the JavaScript isolate that's active for the current context */
|
|
v8::Isolate *GetIsolate();
|
|
|
|
/* Get autoDestroy variable */
|
|
bool GetAutoDestroy();
|
|
};
|
|
|
|
/* Definition of the class registration method */
|
|
typedef void (*JSExtenderRegisterMethod)(js_class_definition_t *class_definition);
|
|
|
|
/* The struct holding a C++ class instance, to be used in JS */
|
|
typedef struct {
|
|
JSBase *obj; /* The class instance to be used in JS */
|
|
char *name; /* The name of the instance within JS */
|
|
bool auto_destroy; /* Flag to know if the instance should be auto destroyed when not needed by JS anymore */
|
|
} registered_instance_t;
|
|
|
|
/* Main class for executing a V8 JavaScript */
|
|
class JSMOD_EXPORT JSMain
|
|
{
|
|
private:
|
|
v8::Isolate* isolate; /* The V8 isolate for this script instance */
|
|
|
|
std::vector<const js_class_definition_t *> *extenderClasses;/* List holding C++ classes to be registered in JS on execution */
|
|
std::vector<js_function_t *> *extenderFunctions; /* List holding C++ functions to be registered in JS on execution */
|
|
std::vector<registered_instance_t*> *extenderInstances; /* List holding C++ class instances to be registered in JS on execution */
|
|
std::set<JSBase *> *activeInstances; /* List holding all active instances right now (in a running script) */
|
|
|
|
bool forcedTermination; /* Is set to true if script is triggering a forced termination of the script */
|
|
char *forcedTerminationMessage; /* The message given during forced termination */
|
|
int forcedTerminationLineNumber; /* The JS line number that called the exit function */
|
|
char *forcedTerminationScriptFile; /* The JS script file that called the exit function */
|
|
|
|
/* Internal Log function accessable from JS - used just for testing */
|
|
static void Log(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
public:
|
|
JSMain(void);
|
|
~JSMain(void);
|
|
|
|
void AddJSExtenderFunction(v8::FunctionCallback func, const std::string& name); /* Add a C++ function to be registered when running the script */
|
|
void AddJSExtenderClass(const js_class_definition_t *method); /* Add a C++ class to be registered when running the script */
|
|
void AddJSExtenderInstance(JSBase *instance, const std::string& objectName, bool autoDestroy); /* Add a C++ class instance to be registered when running the script */
|
|
|
|
static JSMain *GetScriptInstanceFromIsolate(v8::Isolate* isolate); /* Get the JavaScript C++ instance from a V8 isolate */
|
|
v8::Isolate *GetIsolate(); /* Get the V8 isolate from the current instance */
|
|
|
|
const std::string ExecuteScript(const std::string& filename, bool *resultIsError);
|
|
const std::string ExecuteString(const std::string& scriptData, const std::string& fileName, bool *resultIsError);
|
|
|
|
#if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
|
|
static void Initialize(v8::Platform **platform); /* Initialize the V8 engine */
|
|
#else
|
|
static void Initialize(); /* Initialize the V8 engine */
|
|
#endif
|
|
|
|
static void Dispose(); /* Deinitialize the V8 engine */
|
|
|
|
static void Include(const v8::FunctionCallbackInfo<v8::Value>& args); /* Adds functionality to include another JavaScript from the running script */
|
|
static void Version(const v8::FunctionCallbackInfo<v8::Value>& args); /* Internal Version function accessable from JS - used to get the current V( version */
|
|
static const std::string GetExceptionInfo(v8::Isolate* isolate, v8::TryCatch* try_catch); /* Get the exception information from a V8 TryCatch instance */
|
|
|
|
const std::vector<const js_class_definition_t *>& GetExtenderClasses() const;/* Returns the list of class definitions */
|
|
const std::vector<js_function_t *>& GetExtenderFunctions() const; /* Returns the list of function definitions */
|
|
const std::vector<registered_instance_t*>& GetExtenderInstances() const; /* Returns the list of class instance definitions */
|
|
|
|
/* Methods to keep track of all created C++ instances within JS */
|
|
void AddActiveInstance(JSBase *obj);
|
|
void RemoveActiveInstance(JSBase *obj);
|
|
void DisposeActiveInstances();
|
|
|
|
static bool FileExists(const char *file);
|
|
static const std::string LoadFileToString(const std::string& filename);
|
|
|
|
/* Data related to forced script termination */
|
|
bool GetForcedTermination(void);
|
|
void ResetForcedTermination(void);
|
|
const char *GetForcedTerminationMessage(void);
|
|
const char *GetForcedTerminationScriptFile(void);
|
|
int GetForcedTerminationLineNumber(void);
|
|
|
|
/* Method to force termination of a script */
|
|
static void ExitScript(v8::Isolate *isolate, const char *msg);
|
|
|
|
/* Get the filename and line number of the current JS stack */
|
|
static char *GetStackInfo(v8::Isolate *isolate, int *lineNumber);
|
|
};
|
|
|
|
#ifdef V8_ENABLE_DEBUGGING
|
|
void V8DispatchDebugMessages();
|
|
#endif
|
|
|
|
#endif /* V8_JAVASCRIPT_H */
|
|
|
|
/* For Emacs:
|
|
* Local Variables:
|
|
* mode:c
|
|
* indent-tabs-mode:t
|
|
* tab-width:4
|
|
* c-basic-offset:4
|
|
* End:
|
|
* For VIM:
|
|
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
|
|
*/
|