1 /* 2 * mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 3 * Copyright (C) 2013-2014, Peter Olsson <peter@olssononline.se> 4 * 5 * Version: MPL 1.1 6 * 7 * The contents of this file are subject to the Mozilla Public License Version 8 * 1.1 (the "License"); you may not use this file except in compliance with 9 * the License. You may obtain a copy of the License at 10 * http://www.mozilla.org/MPL/ 11 * 12 * Software distributed under the License is distributed on an "AS IS" basis, 13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 * for the specific language governing rights and limitations under the 15 * License. 16 * 17 * The Original Code is mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 18 * 19 * The Initial Developer of the Original Code is 20 * Peter Olsson <peter@olssononline.se> 21 * Portions created by the Initial Developer are Copyright (C) 22 * the Initial Developer. All Rights Reserved. 23 * 24 * Contributor(s): 25 * Peter Olsson <peter@olssononline.se> 26 * 27 * javascript.hpp -- Header file for main JavaScript classes 28 * 29 */ 30 31 #ifndef V8_JAVASCRIPT_H 32 #define V8_JAVASCRIPT_H 33 34 #include <stdint.h> 35 #include <v8.h> 36 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5 37 #include <libplatform/libplatform.h> 38 #include <v8-util.h> 39 #endif 40 41 #include <string> 42 #include <vector> 43 #include <set> 44 #include <assert.h> 45 46 /* Enable this define enable V8 debugging protocol, this is not yet working */ 47 //#define V8_ENABLE_DEBUGGING 48 49 /* 50 * Enable this define to force a GC after the script has finished execution. 51 * This is only to help debug memory leaks, and should not be needed for anything else 52 */ 53 //#define V8_FORCE_GC_AFTER_EXECUTION 54 55 56 /* Macro for easy V8 "get property" callback definition */ 57 #define JS_GET_PROPERTY_DEF(method_name, class_name) \ 58 static void method_name(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info)\ 59 {\ 60 JS_CHECK_SCRIPT_STATE();\ 61 class_name *obj = JSBase::GetInstance<class_name>(info.Holder());\ 62 if (obj) {\ 63 obj->method_name##Impl(property, info);\ 64 } else {\ 65 int line;\ 66 char *file = JSMain::GetStackInfo(info.GetIsolate(), &line);\ 67 v8::String::Utf8Value str(info.Holder());\ 68 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 "()");\ 69 free(file);\ 70 info.GetReturnValue().Set(false);\ 71 }\ 72 }\ 73 void method_name##Impl(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) 74 75 /* Macro for easy V8 "set property" callback definition */ 76 #define JS_SET_PROPERTY_DEF(method_name, class_name) \ 77 static void method_name(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)\ 78 {\ 79 JS_CHECK_SCRIPT_STATE();\ 80 class_name *obj = JSBase::GetInstance<class_name>(info.Holder());\ 81 if (obj) {\ 82 obj->method_name##Impl(property, value, info);\ 83 } else {\ 84 int line;\ 85 char *file = JSMain::GetStackInfo(info.GetIsolate(), &line);\ 86 v8::String::Utf8Value str(info.Holder());\ 87 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 "()");\ 88 free(file);\ 89 info.GetReturnValue().Set(false);\ 90 }\ 91 }\ 92 void method_name##Impl(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info) 93 94 /* Macro for easy V8 "function" callback definition */ 95 #define JS_FUNCTION_DEF(method_name, class_name) \ 96 static void method_name(const v8::FunctionCallbackInfo<v8::Value>& info)\ 97 {\ 98 JS_CHECK_SCRIPT_STATE();\ 99 class_name *obj = JSBase::GetInstance<class_name>(info.Holder());\ 100 if (obj) {\ 101 obj->method_name##Impl(info);\ 102 } else {\ 103 int line;\ 104 char *file = JSMain::GetStackInfo(info.GetIsolate(), &line);\ 105 v8::String::Utf8Value str(info.Holder());\ 106 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 "()");\ 107 free(file);\ 108 info.GetReturnValue().Set(false);\ 109 }\ 110 }\ 111 void method_name##Impl(const v8::FunctionCallbackInfo<v8::Value>& info) 112 113 /* Macros for V8 callback implementations */ 114 #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) 115 #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) 116 #define JS_FUNCTION_IMPL(method_name, class_name) void class_name::method_name##Impl(const v8::FunctionCallbackInfo<v8::Value>& info) 117 118 /* Macros for V8 callback definitions (class static version) */ 119 #define JS_GET_PROPERTY_DEF_STATIC(method_name) static void method_name(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) 120 #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) 121 #define JS_FUNCTION_DEF_STATIC(method_name) static void method_name(const v8::FunctionCallbackInfo<v8::Value>& info) 122 123 /* Macros for V8 callback implementations (class static version) */ 124 #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) 125 #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) 126 #define JS_FUNCTION_IMPL_STATIC(method_name, class_name) void class_name::method_name(const v8::FunctionCallbackInfo<v8::Value>& info) 127 128 /* Macro for basic script state check (to know if the script is being terminated), should be called before calling any callback actual code */ 129 #define JS_CHECK_SCRIPT_STATE() \ 130 if (info.GetIsolate()->IsExecutionTerminating()) return;\ 131 if (JSMain::GetScriptInstanceFromIsolate(info.GetIsolate()) && JSMain::GetScriptInstanceFromIsolate(info.GetIsolate())->GetForcedTermination()) return 132 133 /* Macro for easy unlocking an isolate on a long running c call */ 134 #define JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(call) {\ 135 /* Unlock isolate on a long running c call to let another thread execute the callback */\ 136 info.GetIsolate()->Exit();\ 137 Unlocker unlock(info.GetIsolate());\ 138 call;\ 139 }\ 140 /* Lock it back */\ 141 info.GetIsolate()->Enter(); 142 143 /* strdup function for all platforms */ 144 #ifdef NDEBUG 145 #if (_MSC_VER >= 1500) // VC9+ 146 #define js_strdup(ptr, s) (void)( (!!(ptr = _strdup(s))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%d", __FILE__, __LINE__),abort(), 0), ptr) 147 #else 148 #define js_strdup(ptr, s) (void)( (!!(ptr = strdup(s))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%d", __FILE__, __LINE__),abort(), 0), ptr) 149 #endif 150 #else 151 #if (_MSC_VER >= 1500) // VC9+ 152 #define js_strdup(ptr, s) (void)(assert(((ptr) = _strdup(s))),ptr);__analysis_assume( ptr ) 153 #else 154 #define js_strdup(ptr, s) (void)(assert(((ptr) = strdup((s)))),ptr) 155 #endif 156 #endif 157 158 /* Makes sure to return a valid char pointer */ 159 #define js_safe_str(s) (s ? s : "") 160 161 /* JS Constructor callback definition */ 162 typedef void * void_pointer_t; 163 typedef void_pointer_t (*ConstructorCallback)(const v8::FunctionCallbackInfo<v8::Value>& info); 164 165 /* JS Function definition */ 166 typedef struct { 167 const char *name; /* Name of the function */ 168 v8::FunctionCallback func; /* Function callback */ 169 } js_function_t; 170 171 /* JS Property definition */ 172 typedef struct { 173 const char *name; /* Name of the property */ 174 v8::AccessorGetterCallback get; /* The property getter */ 175 v8::AccessorSetterCallback set; /* The property setter */ 176 } js_property_t; 177 178 /* JS Class definition */ 179 typedef struct { 180 const char *name; /* The name of the class */ 181 ConstructorCallback constructor; /* The constructor definition */ 182 const js_function_t *functions; /* An array of function definitions */ 183 const js_property_t *properties; /* An array of property definitions */ 184 } js_class_definition_t; 185 186 /* Import/export definitions (used by extra loadable modules) */ 187 #ifdef WIN32 188 /* WIN32 */ 189 #ifdef JSMOD_IMPORT 190 #define JSMOD_EXPORT __declspec(dllimport) 191 #else 192 #define JSMOD_EXPORT __declspec(dllexport) 193 #endif 194 #else 195 /* Not WIN32 */ 196 #ifdef JSMOD_IMPORT 197 #define JSMOD_EXPORT 198 #else 199 #if (HAVE_VISIBILITY != 1) 200 #define JSMOD_EXPORT 201 #else 202 #define JSMOD_EXPORT __attribute__ ((visibility("default"))) 203 #endif 204 #endif 205 #endif 206 207 /* JSMain class prototype */ 208 class JSMOD_EXPORT JSMain; 209 210 /* Base class used by all C++ classes implemented in JS */ 211 class JSMOD_EXPORT JSBase 212 { 213 private: 214 v8::Persistent<v8::Object> *persistentHandle; /* The persistent handle of the JavaScript object for this instance */ 215 bool autoDestroy; /* flag to tell if this instance should be auto destroyed during JavaScript GC */ 216 JSMain *js; /* The "owner" of this instance */ 217 218 /* The callback that happens when the V8 GC cleans up object instances */ 219 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5 220 static void WeakCallback(const v8::WeakCallbackInfo<JSBase>& data); 221 #else 222 static void WeakCallback(const v8::WeakCallbackData<v8::Object, JSBase>& data); 223 #endif 224 225 /* Internal basic constructor when creating a new instance from JS. It will call the actual user code inside */ 226 static void CreateInstance(const v8::FunctionCallbackInfo<v8::Value>& args); 227 228 /* Store a C++ instance to a JS object's private data */ 229 static void AddInstance(v8::Isolate *isolate, const v8::Handle<v8::Object>& handle, const v8::Handle<v8::External>& object, bool autoDestroy); 230 public: 231 JSBase(JSMain *owner); 232 JSBase(const v8::FunctionCallbackInfo<v8::Value>& info); 233 virtual ~JSBase(void); 234 235 /* Returns the JS object related to the C++ instance */ 236 v8::Handle<v8::Object> GetJavaScriptObject(); 237 238 /* Register a C++ class inside V8 (must be called within a entered isolate, and context) */ 239 static void Register(v8::Isolate *isolate, const js_class_definition_t *desc); 240 241 /* Register an existing C++ class instance inside V8 (must be called within a entered isolate, and context) */ 242 void RegisterInstance(v8::Isolate *isolate, std::string name, bool autoDestroy); 243 244 /* Get a JSBase instance from JavaScript callback arguments */ GetInstance(const v8::FunctionCallbackInfo<v8::Value> & info)245 template <typename T> static T *GetInstance(const v8::FunctionCallbackInfo<v8::Value>& info) 246 { 247 v8::HandleScope scope(info.GetIsolate()); 248 return GetInstance<T>(info.Holder()); 249 } 250 251 /* Get a JSBase instance from a JavaScript object */ GetInstance(const v8::Local<v8::Object> & self)252 template <typename T> static T *GetInstance(const v8::Local<v8::Object>& self) 253 { 254 v8::Local<v8::Value> val = self->GetInternalField(0); 255 256 if (!val.IsEmpty() && val->IsExternal()) { 257 v8::Local<v8::External> wrap = v8::Local<v8::External>::Cast(val); 258 JSBase *ptr = static_cast<JSBase*>(wrap->Value()); 259 return dynamic_cast<T*>(ptr); /* If we're trying to cast to the wrong type, dynamic_cast will return NULL */ 260 } else { 261 return NULL; 262 } 263 } 264 265 /* Get a JavaScript function from a JavaScript argument (can be either a string or the actual function) */ 266 static v8::Handle<v8::Function> GetFunctionFromArg(v8::Isolate *isolate, const v8::Local<v8::Value>& arg); 267 268 /* Default JS setter callback, to be used for read only values */ 269 static void DefaultSetProperty(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info); 270 271 /* Get the name of the JavaScript class - must be overridden by the actual implementation */ 272 virtual std::string GetJSClassName() = 0; 273 274 /* Get the JavaScript class instance that owns this instance */ 275 JSMain *GetOwner(); 276 277 /* Get the JavaScript isolate that's active for the current context */ 278 v8::Isolate *GetIsolate(); 279 280 /* Get autoDestroy variable */ 281 bool GetAutoDestroy(); 282 }; 283 284 /* Definition of the class registration method */ 285 typedef void (*JSExtenderRegisterMethod)(js_class_definition_t *class_definition); 286 287 /* The struct holding a C++ class instance, to be used in JS */ 288 typedef struct { 289 JSBase *obj; /* The class instance to be used in JS */ 290 char *name; /* The name of the instance within JS */ 291 bool auto_destroy; /* Flag to know if the instance should be auto destroyed when not needed by JS anymore */ 292 } registered_instance_t; 293 294 /* Main class for executing a V8 JavaScript */ 295 class JSMOD_EXPORT JSMain 296 { 297 private: 298 v8::Isolate* isolate; /* The V8 isolate for this script instance */ 299 300 std::vector<const js_class_definition_t *> *extenderClasses;/* List holding C++ classes to be registered in JS on execution */ 301 std::vector<js_function_t *> *extenderFunctions; /* List holding C++ functions to be registered in JS on execution */ 302 std::vector<registered_instance_t*> *extenderInstances; /* List holding C++ class instances to be registered in JS on execution */ 303 std::set<JSBase *> *activeInstances; /* List holding all active instances right now (in a running script) */ 304 305 bool forcedTermination; /* Is set to true if script is triggering a forced termination of the script */ 306 char *forcedTerminationMessage; /* The message given during forced termination */ 307 int forcedTerminationLineNumber; /* The JS line number that called the exit function */ 308 char *forcedTerminationScriptFile; /* The JS script file that called the exit function */ 309 310 /* Internal Log function accessable from JS - used just for testing */ 311 static void Log(const v8::FunctionCallbackInfo<v8::Value>& args); 312 public: 313 JSMain(void); 314 ~JSMain(void); 315 316 void AddJSExtenderFunction(v8::FunctionCallback func, const std::string& name); /* Add a C++ function to be registered when running the script */ 317 void AddJSExtenderClass(const js_class_definition_t *method); /* Add a C++ class to be registered when running the script */ 318 void AddJSExtenderInstance(JSBase *instance, const std::string& objectName, bool autoDestroy); /* Add a C++ class instance to be registered when running the script */ 319 320 static JSMain *GetScriptInstanceFromIsolate(v8::Isolate* isolate); /* Get the JavaScript C++ instance from a V8 isolate */ 321 v8::Isolate *GetIsolate(); /* Get the V8 isolate from the current instance */ 322 323 const std::string ExecuteScript(const std::string& filename, bool *resultIsError); 324 const std::string ExecuteString(const std::string& scriptData, const std::string& fileName, bool *resultIsError); 325 326 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5 327 static void Initialize(v8::Platform **platform); /* Initialize the V8 engine */ 328 #else 329 static void Initialize(); /* Initialize the V8 engine */ 330 #endif 331 332 static void Dispose(); /* Deinitialize the V8 engine */ 333 334 static void Include(const v8::FunctionCallbackInfo<v8::Value>& args); /* Adds functionality to include another JavaScript from the running script */ 335 static const std::string GetExceptionInfo(v8::Isolate* isolate, v8::TryCatch* try_catch); /* Get the exception information from a V8 TryCatch instance */ 336 337 const std::vector<const js_class_definition_t *>& GetExtenderClasses() const;/* Returns the list of class definitions */ 338 const std::vector<js_function_t *>& GetExtenderFunctions() const; /* Returns the list of function definitions */ 339 const std::vector<registered_instance_t*>& GetExtenderInstances() const; /* Returns the list of class instance definitions */ 340 341 /* Methods to keep track of all created C++ instances within JS */ 342 void AddActiveInstance(JSBase *obj); 343 void RemoveActiveInstance(JSBase *obj); 344 void DisposeActiveInstances(); 345 346 static bool FileExists(const char *file); 347 static const std::string LoadFileToString(const std::string& filename); 348 349 /* Data related to forced script termination */ 350 bool GetForcedTermination(void); 351 void ResetForcedTermination(void); 352 const char *GetForcedTerminationMessage(void); 353 const char *GetForcedTerminationScriptFile(void); 354 int GetForcedTerminationLineNumber(void); 355 356 /* Method to force termination of a script */ 357 static void ExitScript(v8::Isolate *isolate, const char *msg, bool jskill = false); 358 359 /* Get the filename and line number of the current JS stack */ 360 static char *GetStackInfo(v8::Isolate *isolate, int *lineNumber); 361 }; 362 363 #ifdef V8_ENABLE_DEBUGGING 364 void V8DispatchDebugMessages(); 365 #endif 366 367 #endif /* V8_JAVASCRIPT_H */ 368 369 /* For Emacs: 370 * Local Variables: 371 * mode:c 372 * indent-tabs-mode:t 373 * tab-width:4 374 * c-basic-offset:4 375 * End: 376 * For VIM: 377 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 378 */ 379