static duk_ret_t test_1(duk_context *ctx) { duk_int_t i, j, n; char *ptr; duk_set_top(ctx, 0); duk_push_undefined(ctx); duk_push_null(ctx); duk_push_true(ctx); duk_push_false(ctx); duk_push_int(ctx, 1); duk_push_number(ctx, -123.456); duk_push_nan(ctx); duk_push_number(ctx, INFINITY); duk_push_number(ctx, -INFINITY); duk_push_string(ctx, ""); duk_push_string(ctx, "foo"); duk_push_lstring(ctx, "foo\0bar", 7); duk_push_object(ctx); duk_push_thread(ctx); (void) duk_push_fixed_buffer(ctx, 0); ptr = (char *) duk_push_fixed_buffer(ctx, 16); for (i = 0; i < 16; i++) { ptr[i] = (char) ('a' + i); } (void) duk_push_dynamic_buffer(ctx, 0); ptr = (char *) duk_push_dynamic_buffer(ctx, 16); for (i = 0; i < 16; i++) { ptr[i] = (char) ('a' + i); } duk_push_pointer(ctx, (void *) NULL); duk_push_pointer(ctx, (void *) 0xdeadbeef); n = duk_get_top(ctx); printf("top: %ld\n", (long) n); for (i = 0; i < n; i++) { const unsigned char *p; duk_size_t sz; duk_dup(ctx, i); sz = (duk_size_t) 0xdeadbeef; p = (const unsigned char *) duk_to_lstring(ctx, -1, &sz); printf("index %ld, string: '", (long) i); for (j = 0; j < sz; j++) { if (p[j] >= 0x20 && p[j] <= 0x7e) { printf("%c", (int) p[j]); } else { printf("\\x%02x", (int) p[j]); } } printf("', length %lu\n", (unsigned long) sz); duk_pop(ctx); duk_dup(ctx, i); sz = (duk_size_t) 0xdeadbeef; p = (const unsigned char *) duk_to_lstring(ctx, -1, NULL); printf("index %ld, string: '%s'\n", (long) i, (const char *) p); duk_pop(ctx); } return 0; }
/* Raw helper to extract internal information / statistics about a value. * The return values are version specific and must not expose anything * that would lead to security issues (e.g. exposing compiled function * 'data' buffer might be an issue). Currently only counts and sizes and * such are given so there should not be a security impact. */ duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) { duk_tval *tv; duk_heaphdr *h; duk_int_t i, n; tv = duk_get_tval(ctx, 0); DUK_ASSERT(tv != NULL); /* because arg count is 1 */ duk_push_array(ctx); /* -> [ val arr ] */ /* type tag (public) */ duk_push_int(ctx, duk_get_type(ctx, 0)); /* address */ if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { h = DUK_TVAL_GET_HEAPHDR(tv); duk_push_pointer(ctx, (void *) h); } else { goto done; } DUK_ASSERT(h != NULL); /* refcount */ #ifdef DUK_USE_REFERENCE_COUNTING duk_push_int(ctx, DUK_HEAPHDR_GET_REFCOUNT(h)); #else duk_push_undefined(ctx); #endif /* heaphdr size and additional allocation size, followed by * type specific stuff (with varying value count) */ switch (DUK_HEAPHDR_GET_TYPE(h)) { case DUK_HTYPE_STRING: { duk_hstring *h_str = (duk_hstring *) h; duk_push_int(ctx, (int) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1)); break; } case DUK_HTYPE_OBJECT: { duk_hobject *h_obj = (duk_hobject *) h; duk_int_t hdr_size; if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { hdr_size = (duk_int_t) sizeof(duk_hcompiledfunction); } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h_obj)) { hdr_size = (duk_int_t) sizeof(duk_hnativefunction); } else if (DUK_HOBJECT_IS_THREAD(h_obj)) { hdr_size = (duk_int_t) sizeof(duk_hthread); } else { hdr_size = (duk_int_t) sizeof(duk_hobject); } duk_push_int(ctx, (int) hdr_size); duk_push_int(ctx, (int) DUK_HOBJECT_E_ALLOC_SIZE(h_obj)); duk_push_int(ctx, (int) h_obj->e_size); duk_push_int(ctx, (int) h_obj->e_used); duk_push_int(ctx, (int) h_obj->a_size); duk_push_int(ctx, (int) h_obj->h_size); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { duk_hbuffer *h_data = ((duk_hcompiledfunction *) h_obj)->data; if (h_data) { duk_push_int(ctx, DUK_HBUFFER_GET_SIZE(h_data)); } else { duk_push_int(ctx, 0); } } break; } case DUK_HTYPE_BUFFER: { duk_hbuffer *h_buf = (duk_hbuffer *) h; if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) { /* XXX: when usable_size == 0, dynamic buf ptr may now be NULL, in which case * the second allocation does not exist. */ duk_hbuffer_dynamic *h_dyn = (duk_hbuffer_dynamic *) h; duk_push_int(ctx, (int) (sizeof(duk_hbuffer_dynamic))); duk_push_int(ctx, (int) (DUK_HBUFFER_DYNAMIC_GET_ALLOC_SIZE(h_dyn))); } else { duk_push_int(ctx, (int) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf) + 1)); } break; } } done: /* set values into ret array */ /* FIXME: primitive to make array from valstack slice */ n = duk_get_top(ctx); for (i = 2; i < n; i++) { duk_dup(ctx, i); duk_put_prop_index(ctx, 1, i - 2); } duk_dup(ctx, 1); return 1; }
static duk_ret_t test_1(duk_context *ctx) { duk_idx_t i, n; duk_set_top(ctx, 0); duk_push_undefined(ctx); duk_push_null(ctx); duk_push_true(ctx); duk_push_false(ctx); duk_push_int(ctx, 0); duk_push_int(ctx, 1); duk_push_int(ctx, -1); duk_push_number(ctx, 123.456); duk_push_number(ctx, -123.456); duk_push_number(ctx, 123.999); duk_push_number(ctx, -123.999); duk_push_number(ctx, -2147483649.0); /* min int32 - 1 */ duk_push_number(ctx, -2147483648.0); /* min int32 */ duk_push_number(ctx, 2147483647.0); /* max int32 */ duk_push_number(ctx, 2147483648.0); /* max int32 + 1 */ duk_push_number(ctx, 4294967295.0); /* max uint32 */ duk_push_number(ctx, 4294967296.0); /* max uint32 + 1 */ duk_push_number(ctx, 65535.0); /* max uint16 */ duk_push_number(ctx, 65536.0); /* max uint16 + 1 */ duk_push_number(ctx, 9999999999.0); duk_push_nan(ctx); duk_push_number(ctx, INFINITY); duk_push_string(ctx, ""); duk_push_string(ctx, "foo"); duk_push_string(ctx, "123"); duk_push_string(ctx, "123.456"); duk_push_string(ctx, "123.456e3"); duk_push_string(ctx, " -123.456e+3 "); duk_push_string(ctx, "NaN"); duk_push_string(ctx, "-Infinity"); duk_push_string(ctx, "+Infinity"); duk_push_string(ctx, "Infinity"); duk_push_string(ctx, "Infinityx"); duk_push_string(ctx, "xInfinity"); duk_push_string(ctx, " Infinity "); duk_push_object(ctx); duk_push_thread(ctx); duk_push_fixed_buffer(ctx, 0); /* ToNumber('') = 0 */ duk_push_fixed_buffer(ctx, 1024); /* ToNumber('\u0000...') = NaN, converts into 0 when doing integer coercion */ duk_push_dynamic_buffer(ctx, 0); duk_push_dynamic_buffer(ctx, 1024); duk_push_pointer(ctx, (void *) NULL); duk_push_pointer(ctx, (void *) 0xdeadbeef); n = duk_get_top(ctx); printf("top: %ld\n", (long) n); for (i = 0; i < n; i++) { duk_double_t dval_pre; duk_double_t dval_post; duk_int_t ival; duk_uint_t uval; duk_dup(ctx, i); dval_pre = duk_get_number(ctx, -1); ival = duk_to_int32(ctx, -1); dval_post = duk_get_number(ctx, -1); printf("index %ld, int32: %ld, number before: %lf, number after: %lf\n", (long) i, (long) ival, (double) dval_pre, (double) dval_post); duk_pop(ctx); duk_dup(ctx, i); dval_pre = duk_get_number(ctx, -1); uval = duk_to_uint32(ctx, -1); dval_post = duk_get_number(ctx, -1); printf("index %ld, uint32: %lu, number before: %lf, number after: %lf\n", (long) i, (unsigned long) uval, (double) dval_pre, (double) dval_post); duk_pop(ctx); duk_dup(ctx, i); dval_pre = duk_get_number(ctx, -1); uval = duk_to_uint16(ctx, -1); dval_post = duk_get_number(ctx, -1); printf("index %ld, uint16: %lu, number before: %lf, number after: %lf\n", (long) i, (unsigned long) uval, (double) dval_pre, (double) dval_post); duk_pop(ctx); } return 0; }
/* Raw helper to extract internal information / statistics about a value. * The return values are version specific and must not expose anything * that would lead to security issues (e.g. exposing compiled function * 'data' buffer might be an issue). Currently only counts and sizes and * such are given so there should not be a security impact. */ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval *tv; duk_heaphdr *h; duk_int_t i, n; DUK_UNREF(thr); /* result array */ duk_push_array(ctx); /* -> [ val arr ] */ /* type tag (public) */ duk_push_int(ctx, duk_get_type(ctx, 0)); /* address */ tv = duk_get_tval(ctx, 0); DUK_ASSERT(tv != NULL); /* because arg count is 1 */ if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { h = DUK_TVAL_GET_HEAPHDR(tv); duk_push_pointer(ctx, (void *) h); } else { /* internal type tag */ duk_push_int(ctx, (duk_int_t) DUK_TVAL_GET_TAG(tv)); goto done; } DUK_ASSERT(h != NULL); /* refcount */ #ifdef DUK_USE_REFERENCE_COUNTING duk_push_size_t(ctx, DUK_HEAPHDR_GET_REFCOUNT(h)); #else duk_push_undefined(ctx); #endif /* heaphdr size and additional allocation size, followed by * type specific stuff (with varying value count) */ switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) { case DUK_HTYPE_STRING: { duk_hstring *h_str = (duk_hstring *) h; duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1)); break; } case DUK_HTYPE_OBJECT: { duk_hobject *h_obj = (duk_hobject *) h; duk_small_uint_t hdr_size; if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { hdr_size = (duk_small_uint_t) sizeof(duk_hcompiledfunction); } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h_obj)) { hdr_size = (duk_small_uint_t) sizeof(duk_hnativefunction); } else if (DUK_HOBJECT_IS_THREAD(h_obj)) { hdr_size = (duk_small_uint_t) sizeof(duk_hthread); } else { hdr_size = (duk_small_uint_t) sizeof(duk_hobject); } duk_push_uint(ctx, (duk_uint_t) hdr_size); duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_E_ALLOC_SIZE(h_obj)); duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ESIZE(h_obj)); /* Note: e_next indicates the number of gc-reachable entries * in the entry part, and also indicates the index where the * next new property would be inserted. It does *not* indicate * the number of non-NULL keys present in the object. That * value could be counted separately but requires a pass through * the key list. */ duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ENEXT(h_obj)); duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ASIZE(h_obj)); duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_HSIZE(h_obj)); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { duk_hbuffer *h_data = (duk_hbuffer *) DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, (duk_hcompiledfunction *) h_obj); if (h_data) { duk_push_uint(ctx, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_data)); } else { duk_push_uint(ctx, 0); } } break; } case DUK_HTYPE_BUFFER: { duk_hbuffer *h_buf = (duk_hbuffer *) h; if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) { if (DUK_HBUFFER_HAS_EXTERNAL(h_buf)) { duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_external))); } else { /* When alloc_size == 0 the second allocation may not * actually exist. */ duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_dynamic))); } duk_push_uint(ctx, (duk_uint_t) (DUK_HBUFFER_GET_SIZE(h_buf))); } else { duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf) + 1)); } break; } } done: /* set values into ret array */ /* XXX: primitive to make array from valstack slice */ n = duk_get_top(ctx); for (i = 2; i < n; i++) { duk_dup(ctx, i); duk_put_prop_index(ctx, 1, i - 2); } duk_dup(ctx, 1); return 1; }
/* in interactive mode, write to stdout */ print_pop_error(ctx, stdout); retval = -1; /* an error 'taints' the execution */ } else { duk_pop(ctx); } } done: if (buffer) { free(buffer); buffer = NULL; } return retval; } #else /* NO_READLINE */ static int handle_interactive(duk_context *ctx) { const char *prompt = "duk> "; char *buffer = NULL; int retval = 0; int rc; duk_eval_string(ctx, GREET_CODE("")); duk_pop(ctx); /* * Note: using readline leads to valgrind-reported leaks inside * readline itself. Execute code from an input file (and not * through stdin) for clean valgrind runs. */ rl_initialize(); for (;;) { if (buffer) { free(buffer); buffer = NULL; } buffer = readline(prompt); if (!buffer) { break; } if (buffer && buffer[0] != (char) 0) { add_history(buffer); } duk_push_pointer(ctx, (void *) buffer); duk_push_uint(ctx, (duk_uint_t) strlen(buffer)); duk_push_string(ctx, "input"); interactive_mode = 1; /* global */ rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/); #if defined(DUK_CMDLINE_AJSHEAP) ajsheap_clear_exec_timeout(); #endif if (buffer) { free(buffer); buffer = NULL; } if (rc != DUK_EXEC_SUCCESS) { /* in interactive mode, write to stdout */ print_pop_error(ctx, stdout); retval = -1; /* an error 'taints' the execution */ } else { duk_pop(ctx); } } if (buffer) { free(buffer); buffer = NULL; } return retval; }
static int handle_interactive(duk_context *ctx) { const char *prompt = "duk> "; char *buffer = NULL; int retval = 0; int rc; int got_eof = 0; duk_eval_string(ctx, GREET_CODE(" [no readline]")); duk_pop(ctx); buffer = (char *) malloc(LINEBUF_SIZE); if (!buffer) { fprintf(stderr, "failed to allocated a line buffer\n"); fflush(stderr); retval = -1; goto done; } while (!got_eof) { size_t idx = 0; fwrite(prompt, 1, strlen(prompt), stdout); fflush(stdout); for (;;) { int c = fgetc(stdin); if (c == EOF) { got_eof = 1; break; } else if (c == '\n') { break; } else if (idx >= LINEBUF_SIZE) { fprintf(stderr, "line too long\n"); fflush(stderr); retval = -1; goto done; } else { buffer[idx++] = (char) c; } } duk_push_pointer(ctx, (void *) buffer); duk_push_uint(ctx, (duk_uint_t) idx); duk_push_string(ctx, "input"); interactive_mode = 1; /* global */ rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/); #if defined(DUK_CMDLINE_AJSHEAP) ajsheap_clear_exec_timeout(); #endif if (rc != DUK_EXEC_SUCCESS) { /* in interactive mode, write to stdout */ print_pop_error(ctx, stdout); retval = -1; /* an error 'taints' the execution */ } else { duk_pop(ctx); } } done: if (buffer) { free(buffer); buffer = NULL; } return retval; }
JavaScriptObject::JavaScriptObject(JavaTypeMap& typeMap, JNIEnv* env, duk_context* context, jstring name, jobjectArray methods) : m_name(JString(env, name).str()) , m_context(context) , m_instance(nullptr) , m_nextFinalizer(nullptr) { CHECK_STACK(m_context); duk_push_global_object(m_context); if (!duk_get_prop_string(m_context, -1, m_name.c_str())) { duk_pop_2(m_context); throw std::invalid_argument("A global JavaScript object called " + m_name + " was not found"); } m_instance = duk_get_heapptr(m_context, -1); if (m_instance == nullptr) { duk_pop_2(m_context); throw std::invalid_argument("JavaScript global called " + m_name + " is not an object"); } // Make sure that the object has all of the methods we want. jmethodID getName = nullptr; const jsize numArgs = env->GetArrayLength(methods); for (jsize i = 0; i < numArgs; ++i) { auto method = env->GetObjectArrayElement(methods, i); if (getName == nullptr) { jclass methodClass = env->GetObjectClass(method); getName = env->GetMethodID(methodClass, "getName", "()Ljava/lang/String;"); } // Sanity check that as of right now, the object we're proxying has a function with this name. const JString methodName(env, static_cast<jstring>(env->CallObjectMethod(method, getName))); if (!duk_get_prop_string(m_context, -1, methodName)) { duk_pop_3(m_context); throw std::runtime_error("JavaScript global " + m_name + " has no method called " + methodName.str()); } else if (!duk_is_callable(m_context, -1)) { duk_pop_3(m_context); throw std::runtime_error("JavaScript property " + m_name + "." + methodName.str() + " not callable"); } try { // Build a call wrapper that handles marshalling the arguments and return value. m_methods.emplace(std::make_pair(env->FromReflectedMethod(method), buildMethodBody(typeMap, env, method, methodName.str()))); } catch (const std::invalid_argument& e) { duk_pop_3(m_context); throw std::invalid_argument("In proxied method \"" + m_name + "." + methodName.str() + "\": " + e.what()); } // Pop the method property. duk_pop(m_context); } // Keep track of any previously registered finalizer. duk_get_finalizer(m_context, -1); m_nextFinalizer = duk_is_c_function(m_context, -1) ? duk_get_c_function(m_context, -1) : nullptr; duk_pop(m_context); duk_push_c_function(m_context, JavaScriptObject::finalizer, 1); duk_set_finalizer(m_context, -2); // Add 'this' to the list of pointers attached to the proxied instance. // TODO: don't use an array here, just use a separate hidden property for each proxy. if (!duk_has_prop_string(m_context, -1, WRAPPER_THIS_PROP_NAME)) { duk_push_array(m_context); } else { duk_get_prop_string(m_context, -1, WRAPPER_THIS_PROP_NAME); } const duk_size_t length = duk_get_length(m_context, -1); duk_push_pointer(m_context, this); duk_put_prop_index(m_context, -2, static_cast<duk_uarridx_t>(length)); // Add the array (back) to the instance. duk_put_prop_string(m_context, -2, WRAPPER_THIS_PROP_NAME); // Pop the global and our instance. duk_pop_2(m_context); }