]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * 'ajduk' specific functionality, examples for low memory techniques | |
3 | */ | |
4 | ||
5 | #ifdef DUK_CMDLINE_AJSHEAP | |
6 | ||
7 | #include <stdio.h> | |
8 | #include <stdlib.h> | |
9 | #include <string.h> | |
10 | #include <time.h> | |
11 | #include "ajs.h" | |
12 | #include "ajs_heap.h" | |
13 | ||
14 | extern uint8_t dbgHEAPDUMP; | |
15 | ||
16 | /* | |
17 | * Helpers | |
18 | */ | |
19 | ||
20 | static void safe_print_chars(const char *p, duk_size_t len, int until_nul) { | |
21 | duk_size_t i; | |
22 | ||
23 | printf("\""); | |
24 | for (i = 0; i < len; i++) { | |
25 | unsigned char x = (unsigned char) p[i]; | |
26 | if (until_nul && x == 0U) { | |
27 | break; | |
28 | } | |
29 | if (x < 0x20 || x >= 0x7e || x == '"' || x == '\'' || x == '\\') { | |
30 | printf("\\x%02x", (int) x); | |
31 | } else { | |
32 | printf("%c", (char) x); | |
33 | } | |
34 | } | |
35 | printf("\""); | |
36 | } | |
37 | ||
38 | /* | |
39 | * Heap initialization when using AllJoyn.js pool allocator (without any | |
40 | * other AllJoyn.js integration). This serves as an example of how to | |
41 | * integrate Duktape with a pool allocator and is useful for low memory | |
42 | * testing. | |
43 | * | |
44 | * The pool sizes are not optimized here. The sizes are chosen so that | |
45 | * you can look at the high water mark (hwm) and use counts (use) and see | |
46 | * how much allocations are needed for each pool size. To optimize pool | |
47 | * sizes more accurately, you can use --alloc-logging and inspect the memory | |
48 | * allocation log which provides exact byte counts etc. | |
49 | * | |
50 | * https://git.allseenalliance.org/cgit/core/alljoyn-js.git | |
51 | * https://git.allseenalliance.org/cgit/core/alljoyn-js.git/tree/ajs.c | |
52 | */ | |
53 | ||
54 | static const AJS_HeapConfig ajsheap_config[] = { | |
55 | { 8, 10, AJS_POOL_BORROW, 0 }, | |
56 | { 12, 10, AJS_POOL_BORROW, 0 }, | |
57 | { 16, 200, AJS_POOL_BORROW, 0 }, | |
58 | { 20, 400, AJS_POOL_BORROW, 0 }, | |
59 | { 24, 400, AJS_POOL_BORROW, 0 }, | |
60 | { 28, 200, AJS_POOL_BORROW, 0 }, | |
61 | { 32, 200, AJS_POOL_BORROW, 0 }, | |
62 | { 40, 200, AJS_POOL_BORROW, 0 }, | |
63 | { 48, 50, AJS_POOL_BORROW, 0 }, | |
64 | { 52, 50, AJS_POOL_BORROW, 0 }, | |
65 | { 56, 50, AJS_POOL_BORROW, 0 }, | |
66 | { 60, 50, AJS_POOL_BORROW, 0 }, | |
67 | { 64, 50, AJS_POOL_BORROW, 0 }, | |
68 | { 128, 80, AJS_POOL_BORROW, 0 }, | |
69 | { 256, 16, AJS_POOL_BORROW, 0 }, | |
70 | { 512, 16, AJS_POOL_BORROW, 0 }, | |
71 | { 1024, 6, AJS_POOL_BORROW, 0 }, | |
72 | { 1360, 1, AJS_POOL_BORROW, 0 }, /* duk_heap, with heap ptr compression */ | |
73 | { 2048, 5, AJS_POOL_BORROW, 0 }, | |
74 | { 4096, 3, 0, 0 }, | |
75 | { 8192, 3, 0, 0 }, | |
76 | { 16384, 1, 0, 0 }, | |
77 | { 32768, 1, 0, 0 } | |
78 | }; | |
79 | ||
80 | uint8_t *ajsheap_ram = NULL; | |
81 | ||
82 | void ajsheap_init(void) { | |
83 | size_t heap_sz[1]; | |
84 | uint8_t *heap_array[1]; | |
85 | uint8_t num_pools, i; | |
86 | AJ_Status ret; | |
87 | ||
88 | num_pools = (uint8_t) (sizeof(ajsheap_config) / sizeof(AJS_HeapConfig)); | |
89 | heap_sz[0] = AJS_HeapRequired(ajsheap_config, /* heapConfig */ | |
90 | num_pools, /* numPools */ | |
91 | 0); /* heapNum */ | |
92 | ajsheap_ram = (uint8_t *) malloc(heap_sz[0]); | |
93 | if (ajsheap_ram == NULL) { | |
94 | fprintf(stderr, "Failed to allocate AJS heap\n"); | |
95 | fflush(stderr); | |
96 | exit(1); | |
97 | } | |
98 | heap_array[0] = ajsheap_ram; | |
99 | ||
100 | fprintf(stderr, "Allocated AJS heap of %ld bytes, pools:", (long) heap_sz[0]); | |
101 | for (i = 0; i < num_pools; i++) { | |
102 | fprintf(stderr, " (sz:%ld,num:%ld,brw:%ld,idx:%ld)", | |
103 | (long) ajsheap_config[i].size, (long) ajsheap_config[i].entries, | |
104 | (long) ajsheap_config[i].borrow, (long) ajsheap_config[i].heapIndex); | |
105 | } | |
106 | fprintf(stderr, "\n"); | |
107 | fflush(stderr); | |
108 | ||
109 | ret = AJS_HeapInit((void **) heap_array, /* heap */ | |
110 | (size_t *) heap_sz, /* heapSz */ | |
111 | ajsheap_config, /* heapConfig */ | |
112 | num_pools, /* numPools */ | |
113 | 1); /* numHeaps */ | |
114 | fprintf(stderr, "AJS_HeapInit() -> %ld\n", (long) ret); | |
115 | fflush(stderr); | |
116 | ||
117 | /* Enable heap dumps */ | |
118 | dbgHEAPDUMP = 1; | |
119 | } | |
120 | ||
121 | /* AjsHeap.dump(), allows Ecmascript code to dump heap status at suitable | |
122 | * points. | |
123 | */ | |
124 | duk_ret_t ajsheap_dump_binding(duk_context *ctx) { | |
125 | AJS_HeapDump(); | |
126 | fflush(stdout); | |
127 | return 0; | |
128 | } | |
129 | ||
130 | void ajsheap_dump(void) { | |
131 | AJS_HeapDump(); | |
132 | fflush(stdout); | |
133 | } | |
134 | ||
135 | void ajsheap_register(duk_context *ctx) { | |
136 | duk_push_object(ctx); | |
137 | duk_push_c_function(ctx, ajsheap_dump_binding, 0); | |
138 | duk_put_prop_string(ctx, -2, "dump"); | |
139 | duk_put_global_string(ctx, "AjsHeap"); | |
140 | } | |
141 | ||
142 | /* | |
143 | * Wrapped ajs_heap.c alloc functions | |
144 | * | |
145 | * Used to write an alloc log. | |
146 | */ | |
147 | ||
148 | static FILE *ajsheap_alloc_log = NULL; | |
149 | ||
150 | static void ajsheap_write_alloc_log(const char *fmt, ...) { | |
151 | va_list ap; | |
152 | char buf[256]; | |
153 | ||
154 | va_start(ap, fmt); | |
155 | vsnprintf(buf, sizeof(buf), fmt, ap); | |
156 | buf[sizeof(buf) - 1] = (char) 0; | |
157 | va_end(ap); | |
158 | ||
159 | if (ajsheap_alloc_log == NULL) { | |
160 | ajsheap_alloc_log = fopen("/tmp/ajduk-alloc-log.txt", "wb"); | |
161 | if (ajsheap_alloc_log == NULL) { | |
162 | fprintf(stderr, "WARNING: failed to write alloc log, ignoring\n"); | |
163 | fflush(stderr); | |
164 | return; | |
165 | } | |
166 | } | |
167 | ||
168 | (void) fwrite((const void *) buf, 1, strlen(buf), ajsheap_alloc_log); | |
169 | (void) fflush(ajsheap_alloc_log); | |
170 | } | |
171 | ||
172 | void *ajsheap_alloc_wrapped(void *udata, duk_size_t size) { | |
173 | void *ret = AJS_Alloc(udata, size); | |
174 | if (size > 0 && ret == NULL) { | |
175 | ajsheap_write_alloc_log("A FAIL %ld\n", (long) size); | |
176 | } else if (ret == NULL) { | |
177 | ajsheap_write_alloc_log("A NULL %ld\n", (long) size); | |
178 | } else { | |
179 | ajsheap_write_alloc_log("A %p %ld\n", ret, (long) size); | |
180 | } | |
181 | return ret; | |
182 | } | |
183 | ||
184 | void *ajsheap_realloc_wrapped(void *udata, void *ptr, duk_size_t size) { | |
185 | void *ret = AJS_Realloc(udata, ptr, size); | |
186 | if (size > 0 && ret == NULL) { | |
187 | if (ptr == NULL) { | |
188 | ajsheap_write_alloc_log("R NULL -1 FAIL %ld\n", (long) size); | |
189 | } else { | |
190 | ajsheap_write_alloc_log("R %p -1 FAIL %ld\n", ptr, (long) size); | |
191 | } | |
192 | } else if (ret == NULL) { | |
193 | if (ptr == NULL) { | |
194 | ajsheap_write_alloc_log("R NULL -1 NULL %ld\n", (long) size); | |
195 | } else { | |
196 | ajsheap_write_alloc_log("R %p -1 NULL %ld\n", ptr, (long) size); | |
197 | } | |
198 | } else { | |
199 | if (ptr == NULL) { | |
200 | ajsheap_write_alloc_log("R NULL -1 %p %ld\n", ret, (long) size); | |
201 | } else { | |
202 | ajsheap_write_alloc_log("R %p -1 %p %ld\n", ptr, ret, (long) size); | |
203 | } | |
204 | } | |
205 | return ret; | |
206 | } | |
207 | ||
208 | void ajsheap_free_wrapped(void *udata, void *ptr) { | |
209 | AJS_Free(udata, ptr); | |
210 | if (ptr == NULL) { | |
211 | } else { | |
212 | ajsheap_write_alloc_log("F %p -1\n", ptr); | |
213 | } | |
214 | } | |
215 | ||
216 | /* | |
217 | * Example pointer compression functions. | |
218 | * | |
219 | * 'base' is chosen so that no non-NULL pointer results in a zero result | |
220 | * which is reserved for NULL pointers. | |
221 | */ | |
222 | ||
223 | duk_uint16_t ajsheap_enc16(void *ud, void *p) { | |
224 | duk_uint32_t ret; | |
225 | char *base = (char *) ajsheap_ram - 4; | |
226 | ||
227 | /* Userdata is not needed in this case but would be useful if heap | |
228 | * pointer compression were used for multiple heaps. The userdata | |
229 | * allows the callback to distinguish between heaps and their base | |
230 | * pointers. | |
231 | * | |
232 | * If not needed, the userdata can be left out during compilation | |
233 | * by simply ignoring the userdata argument of the pointer encode | |
234 | * and decode macros. It is kept here so that any bugs in actually | |
235 | * providing the value inside Duktape are revealed during compilation. | |
236 | */ | |
237 | (void) ud; | |
238 | #if 1 | |
239 | /* Ensure that we always get the heap_udata given in heap creation. | |
240 | * (Useful for Duktape development, not needed for user programs.) | |
241 | */ | |
242 | if (ud != (void *) 0xdeadbeef) { | |
243 | fprintf(stderr, "invalid udata for ajsheap_enc16: %p\n", ud); | |
244 | fflush(stderr); | |
245 | } | |
246 | #endif | |
247 | ||
248 | if (p == NULL) { | |
249 | ret = 0; | |
250 | } else { | |
251 | ret = (duk_uint32_t) (((char *) p - base) >> 2); | |
252 | } | |
253 | #if 0 | |
254 | printf("ajsheap_enc16: %p -> %u\n", p, (unsigned int) ret); | |
255 | #endif | |
256 | if (ret > 0xffffUL) { | |
257 | fprintf(stderr, "Failed to compress pointer\n"); | |
258 | fflush(stderr); | |
259 | abort(); | |
260 | } | |
261 | return (duk_uint16_t) ret; | |
262 | } | |
263 | void *ajsheap_dec16(void *ud, duk_uint16_t x) { | |
264 | void *ret; | |
265 | char *base = (char *) ajsheap_ram - 4; | |
266 | ||
267 | /* See userdata discussion in ajsheap_enc16(). */ | |
268 | (void) ud; | |
269 | #if 1 | |
270 | /* Ensure that we always get the heap_udata given in heap creation. */ | |
271 | if (ud != (void *) 0xdeadbeef) { | |
272 | fprintf(stderr, "invalid udata for ajsheap_dec16: %p\n", ud); | |
273 | fflush(stderr); | |
274 | } | |
275 | #endif | |
276 | ||
277 | if (x == 0) { | |
278 | ret = NULL; | |
279 | } else { | |
280 | ret = (void *) (base + (((duk_uint32_t) x) << 2)); | |
281 | } | |
282 | #if 0 | |
283 | printf("ajsheap_dec16: %u -> %p\n", (unsigned int) x, ret); | |
284 | #endif | |
285 | return ret; | |
286 | } | |
287 | ||
288 | /* | |
289 | * Simplified example of an external strings strategy where incoming strings | |
290 | * are written sequentially into a fixed, memory mapped flash area. | |
291 | * | |
292 | * The example first scans if the string is already in the flash (which may | |
293 | * happen if the same string is interned multiple times), then adds it to | |
294 | * flash if there is space. | |
295 | * | |
296 | * This example is too slow to be used in a real world application: there | |
297 | * should be e.g. a hash table to quickly check for strings that are already | |
298 | * present in the string data (similarly to how string interning works in | |
299 | * Duktape itself). | |
300 | */ | |
301 | ||
302 | static uint8_t ajsheap_strdata[65536]; | |
303 | static size_t ajsheap_strdata_used = 0; | |
304 | ||
305 | const void *ajsheap_extstr_check_1(const void *ptr, duk_size_t len) { | |
306 | uint8_t *p, *p_end; | |
307 | uint8_t initial; | |
308 | uint8_t *ret; | |
309 | size_t left; | |
310 | ||
311 | (void) safe_print_chars; /* potentially unused */ | |
312 | ||
313 | if (len <= 3) { | |
314 | /* It's not worth it to make very small strings external, as | |
315 | * they would take the same space anyway. Also avoids zero | |
316 | * length degenerate case. | |
317 | */ | |
318 | return NULL; | |
319 | } | |
320 | ||
321 | /* | |
322 | * Check if we already have the string. Be careful to compare for | |
323 | * NUL terminator too, it is NOT present in 'ptr'. This algorithm | |
324 | * is too simplistic and way too slow for actual use. | |
325 | */ | |
326 | ||
327 | initial = ((const uint8_t *) ptr)[0]; | |
328 | for (p = ajsheap_strdata, p_end = p + ajsheap_strdata_used; p != p_end; p++) { | |
329 | if (*p != initial) { | |
330 | continue; | |
331 | } | |
332 | left = (size_t) (p_end - p); | |
333 | if (left >= len + 1 && | |
334 | memcmp(p, ptr, len) == 0 && | |
335 | p[len] == 0) { | |
336 | ret = p; | |
337 | #if 0 | |
338 | printf("ajsheap_extstr_check_1: ptr=%p, len=%ld ", | |
339 | (void *) ptr, (long) len); | |
340 | safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); | |
341 | printf(" -> existing %p (used=%ld)\n", | |
342 | (void *) ret, (long) ajsheap_strdata_used); | |
343 | #endif | |
344 | return ret; | |
345 | } | |
346 | } | |
347 | ||
348 | /* | |
349 | * Not present yet, check if we have space. Again, be careful to | |
350 | * ensure there is space for a NUL following the input data. | |
351 | */ | |
352 | ||
353 | if (ajsheap_strdata_used + len + 1 > sizeof(ajsheap_strdata)) { | |
354 | #if 0 | |
355 | printf("ajsheap_extstr_check_1: ptr=%p, len=%ld ", (void *) ptr, (long) len); | |
356 | safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); | |
357 | printf(" -> no space (used=%ld)\n", (long) ajsheap_strdata_used); | |
358 | #endif | |
359 | return NULL; | |
360 | } | |
361 | ||
362 | /* | |
363 | * There is space, add the string to our collection, being careful | |
364 | * to append the NUL. | |
365 | */ | |
366 | ||
367 | ret = ajsheap_strdata + ajsheap_strdata_used; | |
368 | memcpy(ret, ptr, len); | |
369 | ret[len] = (uint8_t) 0; | |
370 | ajsheap_strdata_used += len + 1; | |
371 | ||
372 | #if 0 | |
373 | printf("ajsheap_extstr_check_1: ptr=%p, len=%ld -> ", (void *) ptr, (long) len); | |
374 | safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); | |
375 | printf(" -> %p (used=%ld)\n", (void *) ret, (long) ajsheap_strdata_used); | |
376 | #endif | |
377 | return (const void *) ret; | |
378 | } | |
379 | ||
380 | void ajsheap_extstr_free_1(const void *ptr) { | |
381 | (void) ptr; | |
382 | #if 0 | |
383 | printf("ajsheap_extstr_free_1: freeing extstr %p -> ", ptr); | |
384 | safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/); | |
385 | printf("\n"); | |
386 | #endif | |
387 | } | |
388 | ||
389 | /* | |
390 | * Simplified example of an external strings strategy where a set of strings | |
391 | * is gathered during application compile time and baked into the application | |
392 | * binary. | |
393 | * | |
394 | * Duktape built-in strings are available from duk_build_meta.json, see | |
395 | * util/duk_meta_to_strarray.py. There may also be a lot of application | |
396 | * specific strings, e.g. those used by application specific APIs. These | |
397 | * must be gathered through some other means, see e.g. util/scan_strings.py. | |
398 | */ | |
399 | ||
400 | static const char *strdata_duk_builtin_strings[] = { | |
401 | /* | |
402 | * These strings are from util/duk_meta_to_strarray.py | |
403 | */ | |
404 | ||
405 | "Logger", | |
406 | "Thread", | |
407 | "Pointer", | |
408 | "Buffer", | |
409 | "DecEnv", | |
410 | "ObjEnv", | |
411 | "", | |
412 | "global", | |
413 | "Arguments", | |
414 | "JSON", | |
415 | "Math", | |
416 | "Error", | |
417 | "RegExp", | |
418 | "Date", | |
419 | "Number", | |
420 | "Boolean", | |
421 | "String", | |
422 | "Array", | |
423 | "Function", | |
424 | "Object", | |
425 | "Null", | |
426 | "Undefined", | |
427 | "{_func:true}", | |
428 | "{\x22" "_func\x22" ":true}", | |
429 | "{\x22" "_ninf\x22" ":true}", | |
430 | "{\x22" "_inf\x22" ":true}", | |
431 | "{\x22" "_nan\x22" ":true}", | |
432 | "{\x22" "_undef\x22" ":true}", | |
433 | "toLogString", | |
434 | "clog", | |
435 | "l", | |
436 | "n", | |
437 | "fatal", | |
438 | "error", | |
439 | "warn", | |
440 | "debug", | |
441 | "trace", | |
442 | "raw", | |
443 | "fmt", | |
444 | "current", | |
445 | "resume", | |
446 | "compact", | |
447 | "jc", | |
448 | "jx", | |
449 | "base64", | |
450 | "hex", | |
451 | "dec", | |
452 | "enc", | |
453 | "fin", | |
454 | "gc", | |
455 | "act", | |
456 | "info", | |
457 | "version", | |
458 | "env", | |
459 | "modLoaded", | |
460 | "modSearch", | |
461 | "errThrow", | |
462 | "errCreate", | |
463 | "compile", | |
464 | "\xff" "Regbase", | |
465 | "\xff" "Thread", | |
466 | "\xff" "Handler", | |
467 | "\xff" "Finalizer", | |
468 | "\xff" "Callee", | |
469 | "\xff" "Map", | |
470 | "\xff" "Args", | |
471 | "\xff" "This", | |
472 | "\xff" "Pc2line", | |
473 | "\xff" "Source", | |
474 | "\xff" "Varenv", | |
475 | "\xff" "Lexenv", | |
476 | "\xff" "Varmap", | |
477 | "\xff" "Formals", | |
478 | "\xff" "Bytecode", | |
479 | "\xff" "Next", | |
480 | "\xff" "Target", | |
481 | "\xff" "Value", | |
482 | "pointer", | |
483 | "buffer", | |
484 | "\xff" "Tracedata", | |
485 | "lineNumber", | |
486 | "fileName", | |
487 | "pc", | |
488 | "stack", | |
489 | "ThrowTypeError", | |
490 | "Duktape", | |
491 | "id", | |
492 | "require", | |
493 | "__proto__", | |
494 | "setPrototypeOf", | |
495 | "ownKeys", | |
496 | "enumerate", | |
497 | "deleteProperty", | |
498 | "has", | |
499 | "Proxy", | |
500 | "callee", | |
501 | "Invalid Date", | |
502 | "[...]", | |
503 | "\x0a" "\x09", | |
504 | " ", | |
505 | ",", | |
506 | "-0", | |
507 | "+0", | |
508 | "0", | |
509 | "-Infinity", | |
510 | "+Infinity", | |
511 | "Infinity", | |
512 | "object", | |
513 | "string", | |
514 | "number", | |
515 | "boolean", | |
516 | "undefined", | |
517 | "stringify", | |
518 | "tan", | |
519 | "sqrt", | |
520 | "sin", | |
521 | "round", | |
522 | "random", | |
523 | "pow", | |
524 | "min", | |
525 | "max", | |
526 | "log", | |
527 | "floor", | |
528 | "exp", | |
529 | "cos", | |
530 | "ceil", | |
531 | "atan2", | |
532 | "atan", | |
533 | "asin", | |
534 | "acos", | |
535 | "abs", | |
536 | "SQRT2", | |
537 | "SQRT1_2", | |
538 | "PI", | |
539 | "LOG10E", | |
540 | "LOG2E", | |
541 | "LN2", | |
542 | "LN10", | |
543 | "E", | |
544 | "message", | |
545 | "name", | |
546 | "input", | |
547 | "index", | |
548 | "(?:)", | |
549 | "lastIndex", | |
550 | "multiline", | |
551 | "ignoreCase", | |
552 | "source", | |
553 | "test", | |
554 | "exec", | |
555 | "toGMTString", | |
556 | "setYear", | |
557 | "getYear", | |
558 | "toJSON", | |
559 | "toISOString", | |
560 | "toUTCString", | |
561 | "setUTCFullYear", | |
562 | "setFullYear", | |
563 | "setUTCMonth", | |
564 | "setMonth", | |
565 | "setUTCDate", | |
566 | "setDate", | |
567 | "setUTCHours", | |
568 | "setHours", | |
569 | "setUTCMinutes", | |
570 | "setMinutes", | |
571 | "setUTCSeconds", | |
572 | "setSeconds", | |
573 | "setUTCMilliseconds", | |
574 | "setMilliseconds", | |
575 | "setTime", | |
576 | "getTimezoneOffset", | |
577 | "getUTCMilliseconds", | |
578 | "getMilliseconds", | |
579 | "getUTCSeconds", | |
580 | "getSeconds", | |
581 | "getUTCMinutes", | |
582 | "getMinutes", | |
583 | "getUTCHours", | |
584 | "getHours", | |
585 | "getUTCDay", | |
586 | "getDay", | |
587 | "getUTCDate", | |
588 | "getDate", | |
589 | "getUTCMonth", | |
590 | "getMonth", | |
591 | "getUTCFullYear", | |
592 | "getFullYear", | |
593 | "getTime", | |
594 | "toLocaleTimeString", | |
595 | "toLocaleDateString", | |
596 | "toTimeString", | |
597 | "toDateString", | |
598 | "now", | |
599 | "UTC", | |
600 | "parse", | |
601 | "toPrecision", | |
602 | "toExponential", | |
603 | "toFixed", | |
604 | "POSITIVE_INFINITY", | |
605 | "NEGATIVE_INFINITY", | |
606 | "NaN", | |
607 | "MIN_VALUE", | |
608 | "MAX_VALUE", | |
609 | "substr", | |
610 | "trim", | |
611 | "toLocaleUpperCase", | |
612 | "toUpperCase", | |
613 | "toLocaleLowerCase", | |
614 | "toLowerCase", | |
615 | "substring", | |
616 | "split", | |
617 | "search", | |
618 | "replace", | |
619 | "match", | |
620 | "localeCompare", | |
621 | "charCodeAt", | |
622 | "charAt", | |
623 | "fromCharCode", | |
624 | "reduceRight", | |
625 | "reduce", | |
626 | "filter", | |
627 | "map", | |
628 | "forEach", | |
629 | "some", | |
630 | "every", | |
631 | "lastIndexOf", | |
632 | "indexOf", | |
633 | "unshift", | |
634 | "splice", | |
635 | "sort", | |
636 | "slice", | |
637 | "shift", | |
638 | "reverse", | |
639 | "push", | |
640 | "pop", | |
641 | "join", | |
642 | "concat", | |
643 | "isArray", | |
644 | "arguments", | |
645 | "caller", | |
646 | "bind", | |
647 | "call", | |
648 | "apply", | |
649 | "propertyIsEnumerable", | |
650 | "isPrototypeOf", | |
651 | "hasOwnProperty", | |
652 | "valueOf", | |
653 | "toLocaleString", | |
654 | "toString", | |
655 | "constructor", | |
656 | "set", | |
657 | "get", | |
658 | "enumerable", | |
659 | "configurable", | |
660 | "writable", | |
661 | "value", | |
662 | "keys", | |
663 | "isExtensible", | |
664 | "isFrozen", | |
665 | "isSealed", | |
666 | "preventExtensions", | |
667 | "freeze", | |
668 | "seal", | |
669 | "defineProperties", | |
670 | "defineProperty", | |
671 | "create", | |
672 | "getOwnPropertyNames", | |
673 | "getOwnPropertyDescriptor", | |
674 | "getPrototypeOf", | |
675 | "prototype", | |
676 | "length", | |
677 | "alert", | |
678 | "print", | |
679 | "unescape", | |
680 | "escape", | |
681 | "encodeURIComponent", | |
682 | "encodeURI", | |
683 | "decodeURIComponent", | |
684 | "decodeURI", | |
685 | "isFinite", | |
686 | "isNaN", | |
687 | "parseFloat", | |
688 | "parseInt", | |
689 | "eval", | |
690 | "URIError", | |
691 | "TypeError", | |
692 | "SyntaxError", | |
693 | "ReferenceError", | |
694 | "RangeError", | |
695 | "EvalError", | |
696 | "break", | |
697 | "case", | |
698 | "catch", | |
699 | "continue", | |
700 | "debugger", | |
701 | "default", | |
702 | "delete", | |
703 | "do", | |
704 | "else", | |
705 | "finally", | |
706 | "for", | |
707 | "function", | |
708 | "if", | |
709 | "in", | |
710 | "instanceof", | |
711 | "new", | |
712 | "return", | |
713 | "switch", | |
714 | "this", | |
715 | "throw", | |
716 | "try", | |
717 | "typeof", | |
718 | "var", | |
719 | "void", | |
720 | "while", | |
721 | "with", | |
722 | "class", | |
723 | "const", | |
724 | "enum", | |
725 | "export", | |
726 | "extends", | |
727 | "import", | |
728 | "super", | |
729 | "null", | |
730 | "true", | |
731 | "false", | |
732 | "implements", | |
733 | "interface", | |
734 | "let", | |
735 | "package", | |
736 | "private", | |
737 | "protected", | |
738 | "public", | |
739 | "static", | |
740 | "yield", | |
741 | ||
742 | /* | |
743 | * These strings are manually added, and would be gathered in some | |
744 | * application specific manner. | |
745 | */ | |
746 | ||
747 | "foo", | |
748 | "bar", | |
749 | "quux", | |
750 | "enableFrob", | |
751 | "disableFrob" | |
752 | /* ... */ | |
753 | }; | |
754 | ||
755 | const void *ajsheap_extstr_check_2(const void *ptr, duk_size_t len) { | |
756 | int i, n; | |
757 | ||
758 | (void) safe_print_chars; /* potentially unused */ | |
759 | ||
760 | /* Linear scan. An actual implementation would need some acceleration | |
761 | * structure, e.g. select a sublist based on first character. | |
762 | * | |
763 | * NOTE: input string (behind 'ptr' with 'len' bytes) DOES NOT have a | |
764 | * trailing NUL character. Any strings returned from this function | |
765 | * MUST have a trailing NUL character. | |
766 | */ | |
767 | ||
768 | n = (int) (sizeof(strdata_duk_builtin_strings) / sizeof(const char *)); | |
769 | for (i = 0; i < n; i++) { | |
770 | const char *str; | |
771 | ||
772 | str = strdata_duk_builtin_strings[i]; | |
773 | if (strlen(str) == len && memcmp(ptr, (const void *) str, len) == 0) { | |
774 | #if 0 | |
775 | printf("ajsheap_extstr_check_2: ptr=%p, len=%ld ", | |
776 | (void *) ptr, (long) len); | |
777 | safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); | |
778 | printf(" -> constant string index %ld\n", (long) i); | |
779 | #endif | |
780 | return (void *) strdata_duk_builtin_strings[i]; | |
781 | } | |
782 | } | |
783 | ||
784 | #if 0 | |
785 | printf("ajsheap_extstr_check_2: ptr=%p, len=%ld ", | |
786 | (void *) ptr, (long) len); | |
787 | safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); | |
788 | printf(" -> not found\n"); | |
789 | #endif | |
790 | return NULL; | |
791 | } | |
792 | ||
793 | void ajsheap_extstr_free_2(const void *ptr) { | |
794 | (void) ptr; | |
795 | #if 0 | |
796 | printf("ajsheap_extstr_free_2: freeing extstr %p -> ", ptr); | |
797 | safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/); | |
798 | printf("\n"); | |
799 | #endif | |
800 | } | |
801 | ||
802 | /* | |
803 | * External strings strategy intended for valgrind testing: external strings | |
804 | * are allocated using malloc()/free() so that valgrind can be used to ensure | |
805 | * that strings are e.g. freed exactly once. | |
806 | */ | |
807 | ||
808 | const void *ajsheap_extstr_check_3(const void *ptr, duk_size_t len) { | |
809 | duk_uint8_t *ret; | |
810 | ||
811 | (void) safe_print_chars; /* potentially unused */ | |
812 | ||
813 | ret = malloc((size_t) len + 1); | |
814 | if (ret == NULL) { | |
815 | #if 0 | |
816 | printf("ajsheap_extstr_check_3: ptr=%p, len=%ld ", | |
817 | (void *) ptr, (long) len); | |
818 | safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); | |
819 | printf(" -> malloc failed, return NULL\n"); | |
820 | #endif | |
821 | return (const void *) NULL; | |
822 | } | |
823 | ||
824 | if (len > 0) { | |
825 | memcpy((void *) ret, ptr, (size_t) len); | |
826 | } | |
827 | ret[len] = (duk_uint8_t) 0; | |
828 | ||
829 | #if 0 | |
830 | printf("ajsheap_extstr_check_3: ptr=%p, len=%ld ", | |
831 | (void *) ptr, (long) len); | |
832 | safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); | |
833 | printf(" -> %p\n", (void *) ret); | |
834 | #endif | |
835 | return (const void *) ret; | |
836 | } | |
837 | ||
838 | void ajsheap_extstr_free_3(const void *ptr) { | |
839 | (void) ptr; | |
840 | #if 0 | |
841 | printf("ajsheap_extstr_free_3: freeing extstr %p -> ", ptr); | |
842 | safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/); | |
843 | printf("\n"); | |
844 | #endif | |
845 | free((void *) ptr); | |
846 | } | |
847 | ||
848 | /* | |
849 | * Execution timeout example | |
850 | */ | |
851 | ||
852 | #define AJSHEAP_EXEC_TIMEOUT 5 /* seconds */ | |
853 | ||
854 | static time_t curr_pcall_start = 0; | |
855 | static long exec_timeout_check_counter = 0; | |
856 | ||
857 | void ajsheap_start_exec_timeout(void) { | |
858 | curr_pcall_start = time(NULL); | |
859 | } | |
860 | ||
861 | void ajsheap_clear_exec_timeout(void) { | |
862 | curr_pcall_start = 0; | |
863 | } | |
864 | ||
865 | duk_bool_t ajsheap_exec_timeout_check(void *udata) { | |
866 | time_t now = time(NULL); | |
867 | time_t diff = now - curr_pcall_start; | |
868 | ||
869 | (void) udata; /* not needed */ | |
870 | ||
871 | exec_timeout_check_counter++; | |
872 | #if 0 | |
873 | printf("exec timeout check %ld: now=%ld, start=%ld, diff=%ld\n", | |
874 | (long) exec_timeout_check_counter, (long) now, (long) curr_pcall_start, (long) diff); | |
875 | fflush(stdout); | |
876 | #endif | |
877 | ||
878 | if (curr_pcall_start == 0) { | |
879 | /* protected call not yet running */ | |
880 | return 0; | |
881 | } | |
882 | if (diff > AJSHEAP_EXEC_TIMEOUT) { | |
883 | return 1; | |
884 | } | |
885 | return 0; | |
886 | } | |
887 | ||
888 | #else /* DUK_CMDLINE_AJSHEAP */ | |
889 | ||
890 | int ajs_dummy = 0; /* to avoid empty source file */ | |
891 | ||
892 | #endif /* DUK_CMDLINE_AJSHEAP */ |