PageRenderTime 54ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/src/js.c

https://bitbucket.org/isd/dwb
C | 459 lines | 374 code | 53 blank | 32 comment | 100 complexity | 889c5e384dcb7b065a4ba6db239c371a MD5 | raw file
  1. /*
  2. * Copyright (c) 2010-2013 Stefan Bolte <portix@gmx.net>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. */
  18. #include <JavaScriptCore/JavaScript.h>
  19. #include <string.h>
  20. #include <math.h>
  21. #include "dwb.h"
  22. #include "util.h"
  23. #include "js.h"
  24. void
  25. js_make_exception(JSContextRef ctx, JSValueRef *exception, const gchar *format, ...)
  26. {
  27. va_list arg_list;
  28. va_start(arg_list, format);
  29. gchar message[STRING_LENGTH];
  30. vsnprintf(message, sizeof(message), format, arg_list);
  31. va_end(arg_list);
  32. *exception = js_char_to_value(ctx, message);
  33. }
  34. void
  35. js_set_property(JSContextRef ctx, JSObjectRef arg, const char *name, JSValueRef prop, JSClassAttributes attributes, JSValueRef *exc)
  36. {
  37. JSStringRef js_key = JSStringCreateWithUTF8CString(name);
  38. JSObjectSetProperty(ctx, arg, js_key, prop, attributes, exc);
  39. JSStringRelease(js_key);
  40. }
  41. void
  42. js_set_object_property(JSContextRef ctx, JSObjectRef arg, const char *name, const char *value, JSValueRef *exc)
  43. {
  44. JSStringRef js_key = JSStringCreateWithUTF8CString(name);
  45. JSValueRef js_value = js_char_to_value(ctx, value);
  46. JSObjectSetProperty(ctx, arg, js_key, js_value, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly, exc);
  47. JSStringRelease(js_key);
  48. }
  49. gboolean
  50. js_object_has_property(JSContextRef ctx, JSObjectRef arg, const char *name)
  51. {
  52. JSStringRef js_key = JSStringCreateWithUTF8CString(name);
  53. gboolean result = JSObjectHasProperty(ctx, arg, js_key);
  54. JSStringRelease(js_key);
  55. return result;
  56. }
  57. void
  58. js_set_object_number_property(JSContextRef ctx, JSObjectRef arg, const char *name, gdouble value, JSValueRef *exc)
  59. {
  60. JSStringRef js_key = JSStringCreateWithUTF8CString(name);
  61. JSValueRef js_value = JSValueMakeNumber(ctx, value);
  62. JSObjectSetProperty(ctx, arg, js_key, js_value, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly, exc);
  63. JSStringRelease(js_key);
  64. }
  65. /* js_get_object_property {{{*/
  66. JSObjectRef
  67. js_get_object_property(JSContextRef ctx, JSObjectRef arg, const char *name)
  68. {
  69. JSValueRef exc = NULL;
  70. JSObjectRef ret;
  71. JSStringRef buffer = JSStringCreateWithUTF8CString(name);
  72. JSValueRef val = JSObjectGetProperty(ctx, arg, buffer, &exc);
  73. JSStringRelease(buffer);
  74. if (exc != NULL || !JSValueIsObject(ctx, val))
  75. return NULL;
  76. ret = JSValueToObject(ctx, val, &exc);
  77. if (exc != NULL)
  78. return NULL;
  79. return ret;
  80. }/*}}}*/
  81. JSValueRef
  82. js_char_to_value(JSContextRef ctx, const char *text)
  83. {
  84. JSStringRef string = JSStringCreateWithUTF8CString(text);
  85. JSValueRef ret = JSValueMakeString(ctx, string);
  86. JSStringRelease(string);
  87. return ret;
  88. }
  89. /* js_get_string_property {{{*/
  90. char *
  91. js_get_string_property(JSContextRef ctx, JSObjectRef arg, const char *name)
  92. {
  93. JSValueRef exc = NULL;
  94. JSStringRef buffer = JSStringCreateWithUTF8CString(name);
  95. JSValueRef val = JSObjectGetProperty(ctx, arg, buffer, &exc);
  96. JSStringRelease(buffer);
  97. if (exc != NULL || !JSValueIsString(ctx, val) )
  98. return NULL;
  99. return js_value_to_char(ctx, val, JS_STRING_MAX, NULL);
  100. }/*}}}*/
  101. /* js_get_double_property {{{*/
  102. double
  103. js_get_double_property(JSContextRef ctx, JSObjectRef arg, const char *name)
  104. {
  105. double ret;
  106. JSValueRef exc = NULL;
  107. JSStringRef buffer = JSStringCreateWithUTF8CString(name);
  108. JSValueRef val = JSObjectGetProperty(ctx, arg, buffer, &exc);
  109. JSStringRelease(buffer);
  110. if (exc != NULL || !JSValueIsNumber(ctx, val) )
  111. return 0;
  112. ret = JSValueToNumber(ctx, val, &exc);
  113. if (exc != NULL)
  114. return 0;
  115. return ret;
  116. }/*}}}*/
  117. /* js_string_to_char
  118. * Converts a JSStringRef, return a newly allocated char.
  119. * {{{*/
  120. char *
  121. js_string_to_char(JSContextRef ctx, JSStringRef jsstring, size_t size)
  122. {
  123. size_t length;
  124. if (size > 0)
  125. length = MIN(JSStringGetMaximumUTF8CStringSize(jsstring), size);
  126. else
  127. length = JSStringGetMaximumUTF8CStringSize(jsstring);
  128. char *ret = g_malloc(sizeof(gchar) * length);
  129. JSStringGetUTF8CString(jsstring, ret, length);
  130. return ret;
  131. }/*}}}*/
  132. JSValueRef
  133. js_context_change(JSContextRef source_ctx, JSContextRef dest_ctx, JSValueRef val, JSValueRef *exc)
  134. {
  135. char *c_val = js_value_to_json(source_ctx, val, -1, exc);
  136. if (c_val == NULL)
  137. return JSValueMakeNull(dest_ctx);
  138. JSStringRef json = JSStringCreateWithUTF8CString(c_val);
  139. JSValueRef ret = JSValueMakeFromJSONString(dest_ctx, json);
  140. g_free(c_val);
  141. JSStringRelease(json);
  142. if (ret == NULL)
  143. return JSValueMakeNull(dest_ctx);
  144. return ret;
  145. }
  146. /* js_create_object(WebKitWebFrame *frame, const char *)
  147. *
  148. * Executes a script in a function scope, should return an object with
  149. * function-properties
  150. * {{{*/
  151. JSObjectRef
  152. js_create_object(WebKitWebFrame *frame, const char *script)
  153. {
  154. if (script == NULL)
  155. return NULL;
  156. JSStringRef js_script;
  157. JSValueRef ret, exc = NULL;
  158. JSObjectRef return_object;
  159. JSContextRef ctx = webkit_web_frame_get_global_context(frame);
  160. js_script = JSStringCreateWithUTF8CString(script);
  161. ret = JSEvaluateScript(ctx, js_script, NULL, NULL, 0, &exc);
  162. JSStringRelease(js_script);
  163. if (exc != NULL)
  164. return NULL;
  165. return_object = JSValueToObject(ctx, ret, &exc);
  166. if (exc != NULL)
  167. return NULL;
  168. JSValueProtect(ctx, ret);
  169. return return_object;
  170. }/*}}}*/
  171. /* js_call_as_function(WebKitWebFrame, JSObjectRef, char *string, char *json, * char **ret) {{{*/
  172. char *
  173. js_call_as_function(WebKitWebFrame *frame, JSObjectRef obj, const char *string, const char *json, JSType arg_type, char **char_ret)
  174. {
  175. char *ret = NULL;
  176. JSValueRef js_ret, function, v = NULL;
  177. JSObjectRef function_object;
  178. JSStringRef js_json, js_name = NULL;
  179. JSContextRef ctx;
  180. if (obj == NULL)
  181. goto error_out;
  182. ctx = webkit_web_frame_get_global_context(frame);
  183. js_name = JSStringCreateWithUTF8CString(string);
  184. if (!JSObjectHasProperty(ctx, obj, js_name))
  185. goto error_out;
  186. function = JSObjectGetProperty(ctx, obj, js_name, NULL);
  187. function_object = JSValueToObject(ctx, function, NULL);
  188. if (json != NULL)
  189. {
  190. switch(arg_type)
  191. {
  192. case kJSTypeObject :
  193. js_json = JSStringCreateWithUTF8CString(json);
  194. v = JSValueMakeFromJSONString(ctx, js_json);
  195. JSStringRelease(js_json);
  196. break;
  197. case kJSTypeString :
  198. v = js_char_to_value(ctx, json);
  199. break;
  200. default :
  201. break;
  202. }
  203. }
  204. if (v)
  205. {
  206. JSValueRef vals[] = { v };
  207. js_ret = JSObjectCallAsFunction(ctx, function_object, NULL, 1, vals, NULL);
  208. }
  209. else
  210. js_ret = JSObjectCallAsFunction(ctx, function_object, NULL, 0, NULL, NULL);
  211. if (char_ret != NULL)
  212. ret = js_value_to_char(ctx, js_ret, JS_STRING_MAX, NULL);
  213. error_out:
  214. if (js_name)
  215. JSStringRelease(js_name);
  216. if (char_ret != NULL)
  217. *char_ret = ret;
  218. return ret;
  219. }/*}}}*/
  220. char *
  221. js_value_to_string(JSContextRef ctx, JSValueRef value, size_t limit, JSValueRef *exc)
  222. {
  223. JSStringRef jsstring = JSValueToStringCopy(ctx, value, exc);
  224. if (jsstring == NULL)
  225. return NULL;
  226. char *ret = js_string_to_char(ctx, jsstring, limit);
  227. JSStringRelease(jsstring);
  228. return ret;
  229. }
  230. /*{{{*/
  231. char *
  232. js_value_to_char(JSContextRef ctx, JSValueRef value, size_t limit, JSValueRef *exc)
  233. {
  234. if (value == NULL)
  235. return NULL;
  236. if (! JSValueIsString(ctx, value))
  237. return NULL;
  238. return js_value_to_string(ctx, value, limit, exc);
  239. }/*}}}*/
  240. /* print_exception {{{*/
  241. gboolean
  242. js_print_exception(JSContextRef ctx, JSValueRef exception, char *buffer, size_t bufferSize, int starting_line, int *linenumber)
  243. {
  244. if (exception == NULL)
  245. return false;
  246. if (!JSValueIsObject(ctx, exception))
  247. return false;
  248. JSObjectRef o = JSValueToObject(ctx, exception, NULL);
  249. if (o == NULL)
  250. return false;
  251. gint line = (int)js_get_double_property(ctx, o, "line");
  252. gchar *message = js_get_string_property(ctx, o, "message");
  253. char *sourceURL = js_get_string_property(ctx, o, "sourceURL");
  254. if (sourceURL)
  255. fprintf(stderr, "DWB SCRIPT EXCEPTION: in file %s\n", sourceURL);
  256. fprintf(stderr, "DWB SCRIPT EXCEPTION: in line %d: %s\n", line + starting_line, message == NULL ? "unknown" : message);
  257. if (sourceURL != NULL && buffer != NULL)
  258. {
  259. strncpy(buffer, message, bufferSize-1);
  260. buffer[bufferSize-1] = '\0';
  261. }
  262. if (linenumber)
  263. *linenumber = line;
  264. g_free(message);
  265. g_free(sourceURL);
  266. return true;
  267. }
  268. /*}}}*/
  269. JSObjectRef
  270. js_make_function(JSContextRef ctx, const char *script, const char *sourceurl, int linenumber)
  271. {
  272. JSValueRef exc;
  273. JSObjectRef ret = NULL;
  274. JSStringRef body = JSStringCreateWithUTF8CString(script);
  275. JSStringRef source = NULL;
  276. if (sourceurl)
  277. JSStringCreateWithUTF8CString(sourceurl);
  278. JSObjectRef function = JSObjectMakeFunction(ctx, NULL, 0, NULL, body, source, linenumber, &exc);
  279. if (function != NULL)
  280. ret = function;
  281. else
  282. js_print_exception(ctx, exc, NULL, 0, 0, NULL);
  283. JSStringRelease(body);
  284. if (source != NULL)
  285. JSStringRelease(source);
  286. return ret;
  287. }
  288. char *
  289. js_value_to_json(JSContextRef ctx, JSValueRef value, size_t limit, JSValueRef *exc)
  290. {
  291. if (value == NULL)
  292. return NULL;
  293. JSStringRef js_json = JSValueCreateJSONString(ctx, value, 2, exc);
  294. if (js_json == NULL)
  295. return NULL;
  296. char *json = js_string_to_char(ctx, js_json, limit);
  297. JSStringRelease(js_json);
  298. return json;
  299. }
  300. JSValueRef
  301. js_json_to_value(JSContextRef ctx, const char *text)
  302. {
  303. JSStringRef json = JSStringCreateWithUTF8CString(text == NULL || *text == 0 ? "{}" : text);
  304. JSValueRef ret = JSValueMakeFromJSONString(ctx, json);
  305. JSStringRelease(json);
  306. return ret;
  307. }
  308. JSValueRef
  309. js_execute(JSContextRef ctx, const char *script, JSValueRef *exc)
  310. {
  311. JSObjectRef function = js_make_function(ctx, script, NULL, 0);
  312. if (function != NULL)
  313. return JSObjectCallAsFunction(ctx, function, function, 0, NULL, exc);
  314. return NULL;
  315. }
  316. void
  317. js_array_iterator_init(JSContextRef ctx, js_array_iterator *iter, JSObjectRef object)
  318. {
  319. g_return_if_fail(ctx != NULL && object != NULL);
  320. iter->ctx = ctx;
  321. iter->array = object;
  322. JSValueProtect(ctx, iter->array);
  323. iter->current_index = 0;
  324. double length = js_get_double_property(ctx, object, "length");
  325. iter->length = isnan(length) ? -1 : (int) length;
  326. }
  327. JSValueRef
  328. js_array_iterator_next(js_array_iterator *iter, JSValueRef *exc)
  329. {
  330. g_return_val_if_fail(iter != NULL && iter->array != NULL, NULL);
  331. if (iter->current_index < iter->length)
  332. return JSObjectGetPropertyAtIndex(iter->ctx, iter->array, iter->current_index++, exc);
  333. return NULL;
  334. }
  335. void
  336. js_array_iterator_finish(js_array_iterator *iter)
  337. {
  338. g_return_if_fail(iter != NULL && iter != NULL && iter->array != NULL);
  339. JSValueUnprotect(iter->ctx, iter->array);
  340. }
  341. void
  342. js_property_iterator_init(JSContextRef ctx, js_property_iterator *iter, JSObjectRef object)
  343. {
  344. iter->ctx = ctx;
  345. iter->array = JSObjectCopyPropertyNames(ctx, object);
  346. JSPropertyNameArrayRetain(iter->array);
  347. iter->object = object;
  348. JSValueProtect(ctx, object);
  349. iter->current_index = 0;
  350. iter->length = JSPropertyNameArrayGetCount(iter->array);
  351. }
  352. JSValueRef
  353. js_property_iterator_next(js_property_iterator *iter, JSStringRef *jsname_ret, char **name_ret, JSValueRef *exc)
  354. {
  355. g_return_val_if_fail(iter != NULL && iter->array != NULL, NULL);
  356. if (iter->current_index < iter->length)
  357. {
  358. JSStringRef js_name = JSPropertyNameArrayGetNameAtIndex(iter->array, iter->current_index++);
  359. JSValueRef ret = JSObjectGetProperty(iter->ctx, iter->object, js_name, exc);
  360. if (name_ret)
  361. *name_ret = js_string_to_char(iter->ctx, js_name, -1);
  362. if (jsname_ret)
  363. *jsname_ret = js_name;
  364. else
  365. JSStringRelease(js_name);
  366. return ret;
  367. }
  368. if (name_ret)
  369. *name_ret = NULL;
  370. return NULL;
  371. }
  372. void
  373. js_property_iterator_finish(js_property_iterator *iter)
  374. {
  375. g_return_if_fail(iter != NULL);
  376. if (iter->array)
  377. JSPropertyNameArrayRelease(iter->array);
  378. if (iter->object)
  379. JSValueUnprotect(iter->ctx, iter->object);
  380. }
  381. JSObjectRef
  382. js_value_to_function(JSContextRef ctx, JSValueRef val, JSValueRef *exc)
  383. {
  384. JSObjectRef ret = JSValueToObject(ctx, val, exc);
  385. if (ret != NULL && JSObjectIsFunction(ctx, ret))
  386. return ret;
  387. return NULL;
  388. }
  389. gboolean
  390. js_check_syntax(JSContextRef ctx, const char *script, const char *filename, int lineOffset)
  391. {
  392. JSValueRef exc = NULL;
  393. JSStringRef jsscript = JSStringCreateWithUTF8CString(script);
  394. JSStringRef jssource = NULL;
  395. if (filename != NULL)
  396. jssource = JSStringCreateWithUTF8CString(filename);
  397. gboolean correct = JSCheckScriptSyntax(ctx, jsscript, jssource, lineOffset, &exc);
  398. if (!correct)
  399. {
  400. js_print_exception(ctx, exc, NULL, 0, 0, NULL);
  401. }
  402. JSStringRelease(jsscript);
  403. if (jssource != NULL)
  404. JSStringRelease(jssource);
  405. return correct;
  406. }