2014-01-14 10:04:03 +00:00
/*
* 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>
2017-06-28 22:05:19 +00:00
# if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
# include <libplatform/libplatform.h>
# include <v8-util.h>
# endif
2014-01-14 10:04:03 +00:00
# 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() \
2018-05-03 23:13:05 +00:00
if ( info . GetIsolate ( ) - > IsExecutionTerminating ( ) ) return ; \
2014-01-14 10:04:03 +00:00
if ( JSMain : : GetScriptInstanceFromIsolate ( info . GetIsolate ( ) ) & & JSMain : : GetScriptInstanceFromIsolate ( info . GetIsolate ( ) ) - > GetForcedTermination ( ) ) return
2017-07-03 20:01:59 +00:00
/* 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 ( ) ;
2014-01-14 10:04:03 +00:00
/* 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 */
2017-06-28 22:05:19 +00:00
# if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
static void WeakCallback ( const v8 : : WeakCallbackInfo < JSBase > & data ) ;
# else
2014-01-14 10:04:03 +00:00
static void WeakCallback ( const v8 : : WeakCallbackData < v8 : : Object , JSBase > & data ) ;
2017-06-28 22:05:19 +00:00
# endif
2014-01-14 10:04:03 +00:00
/* 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 ) ;
2017-06-28 22:05:19 +00:00
# if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
static void Initialize ( v8 : : Platform * * platform ) ; /* Initialize the V8 engine */
# else
2014-01-14 10:04:03 +00:00
static void Initialize ( ) ; /* Initialize the V8 engine */
2017-06-28 22:05:19 +00:00
# endif
2014-01-14 10:04:03 +00:00
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 :
*/