Example #1
0
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_context *ctx) {
	duk_hthread *thr = (duk_hthread *) ctx;
	duk_hobject *h;
	duk_bool_t is_freeze;

	DUK_ASSERT_TOP(ctx, 1);

	is_freeze = (duk_bool_t) duk_get_current_magic(ctx);
	if (duk_is_buffer(ctx, 0)) {
		/* Plain buffer: already sealed, but not frozen (and can't be frozen
		 * because index properties can't be made non-writable.
		 */
		if (is_freeze) {
			goto fail_cannot_freeze;
		}
		return 1;
	} else if (duk_is_lightfunc(ctx, 0)) {
		/* Lightfunc: already sealed and frozen, success. */
		return 1;
	}
#if 0
	/* Seal/freeze are quite rare in practice so it'd be nice to get the
	 * correct behavior simply via automatic promotion (at the cost of some
	 * memory churn).  However, the promoted objects don't behave the same,
	 * e.g. promoted lightfuncs are extensible.
	 */
	h = duk_require_hobject_promote_mask(ctx, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
#endif

	h = duk_get_hobject(ctx, 0);
	if (h == NULL) {
		/* ES2015 Sections 19.1.2.5, 19.1.2.17 */
		return 1;
	}

	if (is_freeze && DUK_HOBJECT_IS_BUFOBJ(h)) {
		/* Buffer objects cannot be frozen because there's no internal
		 * support for making virtual array indices non-writable.
		 */
		DUK_DD(DUK_DDPRINT("cannot freeze a buffer object"));
		goto fail_cannot_freeze;
	}

	duk_hobject_object_seal_freeze_helper(thr, h, is_freeze);

	/* Sealed and frozen objects cannot gain any more properties,
	 * so this is a good time to compact them.
	 */
	duk_hobject_compact_props(thr, h);
	return 1;

 fail_cannot_freeze:
	DUK_DCERROR_TYPE_INVALID_ARGS(thr);  /* XXX: proper error message */
}
DUK_LOCAL void duk__mark_refzero_list(duk_heap *heap) {
	duk_heaphdr *hdr;

	DUK_DD(DUK_DDPRINT("duk__mark_refzero_list: %p", (void *) heap));

	hdr = heap->refzero_list;
	while (hdr) {
		duk__mark_heaphdr(heap, hdr);
		hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
	}
}
Example #3
0
DUK_LOCAL void duk__run_voluntary_gc(duk_heap *heap) {
	if (DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) {
		DUK_DD(DUK_DDPRINT("mark-and-sweep in progress -> skip voluntary mark-and-sweep now"));
	} else {
		duk_small_uint_t flags;
		duk_bool_t rc;

		DUK_D(DUK_DPRINT("triggering voluntary mark-and-sweep"));
		flags = 0;
		rc = duk_heap_mark_and_sweep(heap, flags);
		DUK_UNREF(rc);
	}
}
Example #4
0
DUK_LOCAL duk_ret_t duk__refcount_fake_finalizer(duk_context *ctx) {
	DUK_UNREF(ctx);
	DUK_D(DUK_DPRINT("fake refcount torture finalizer executed"));
#if 0
	DUK_DD(DUK_DDPRINT("fake torture finalizer for: %!T", duk_get_tval(ctx, 0)));
#endif
	/* Require a lot of stack to force a value stack grow/shrink. */
	duk_require_stack(ctx, 100000);

	/* XXX: do something to force a callstack grow/shrink, perhaps
	 * just a manual forced resize?
	 */
	return 0;
}
Example #5
0
DUK_LOCAL void duk__clear_refzero_list_flags(duk_heap *heap) {
	duk_heaphdr *hdr;

	DUK_DD(DUK_DDPRINT("duk__clear_refzero_list_flags: %p", (void *) heap));

	hdr = heap->refzero_list;
	while (hdr) {
		DUK_HEAPHDR_CLEAR_REACHABLE(hdr);
		DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(hdr));
		/* DUK_HEAPHDR_HAS_FINALIZED may or may not be set. */
		DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(hdr));
		hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
	}
}
Example #6
0
DUK_LOCAL void duk__clear_finalize_list_flags(duk_heap *heap) {
	duk_heaphdr *hdr;

	DUK_DD(DUK_DDPRINT("duk__clear_finalize_list_flags: %p", (void *) heap));

	hdr = heap->finalize_list;
	while (hdr) {
		DUK_HEAPHDR_CLEAR_REACHABLE(hdr);
		DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(hdr));
		DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(hdr));
		DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(hdr));
		hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
	}
}
Example #7
0
DUK_LOCAL void duk__compact_object_list(duk_heap *heap, duk_hthread *thr, duk_heaphdr *start, duk_size_t *p_count_check, duk_size_t *p_count_compact, duk_size_t *p_count_bytes_saved) {
#else
DUK_LOCAL void duk__compact_object_list(duk_heap *heap, duk_hthread *thr, duk_heaphdr *start) {
#endif
	duk_heaphdr *curr;
#if defined(DUK_USE_DEBUG)
	duk_size_t old_size, new_size;
#endif
	duk_hobject *obj;

	DUK_UNREF(heap);

	curr = start;
	while (curr) {
		DUK_DDD(DUK_DDDPRINT("mark-and-sweep compact: %p", (void *) curr));

		if (DUK_HEAPHDR_GET_TYPE(curr) != DUK_HTYPE_OBJECT) {
			goto next;
		}
		obj = (duk_hobject *) curr;

#if defined(DUK_USE_DEBUG)
		old_size = DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj),
		                                      DUK_HOBJECT_GET_ASIZE(obj),
		                                      DUK_HOBJECT_GET_HSIZE(obj));
#endif

		DUK_DD(DUK_DDPRINT("compact object: %p", (void *) obj));
		duk_push_hobject((duk_context *) thr, obj);
		/* XXX: disable error handlers for duration of compaction? */
		duk_safe_call((duk_context *) thr, duk__protected_compact_object, NULL, 1, 0);

#if defined(DUK_USE_DEBUG)
		new_size = DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj),
		                                      DUK_HOBJECT_GET_ASIZE(obj),
		                                      DUK_HOBJECT_GET_HSIZE(obj));
#endif

#if defined(DUK_USE_DEBUG)
		(*p_count_compact)++;
		(*p_count_bytes_saved) += (duk_size_t) (old_size - new_size);
#endif

	 next:
		curr = DUK_HEAPHDR_GET_NEXT(heap, curr);
#if defined(DUK_USE_DEBUG)
		(*p_count_check)++;
#endif
	}
}
DUK_LOCAL void duk__sweep_stringtable_chain(duk_heap *heap, duk_size_t *out_count_keep) {
	duk_strtab_entry *e;
	duk_uint_fast32_t i;
	duk_size_t count_free = 0;
	duk_size_t count_keep = 0;
	duk_size_t j, n;
#if defined(DUK_USE_HEAPPTR16)
	duk_uint16_t *lst;
#else
	duk_hstring **lst;
#endif

	DUK_DD(DUK_DDPRINT("duk__sweep_stringtable: %p", (void *) heap));

	/* Non-zero refcounts should not happen for unreachable strings,
	 * because we refcount finalize all unreachable objects which
	 * should have decreased unreachable string refcounts to zero
	 * (even for cycles).
	 */

	for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) {
		e = heap->strtable + i;
		if (e->listlen == 0) {
#if defined(DUK_USE_HEAPPTR16)
			duk__sweep_string_chain16(heap, &e->u.str16, &count_keep, &count_free);
#else
			duk__sweep_string_chain(heap, &e->u.str, &count_keep, &count_free);
#endif
		} else {
#if defined(DUK_USE_HEAPPTR16)
			lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16);
#else
			lst = e->u.strlist;
#endif
			for (j = 0, n = e->listlen; j < n; j++) {
#if defined(DUK_USE_HEAPPTR16)
				duk__sweep_string_chain16(heap, lst + j, &count_keep, &count_free);
#else
				duk__sweep_string_chain(heap, lst + j, &count_keep, &count_free);
#endif
			}
		}
	}

	DUK_D(DUK_DPRINT("mark-and-sweep sweep stringtable: %ld freed, %ld kept",
	                 (long) count_free, (long) count_keep));
	*out_count_keep = count_keep;
}
Example #9
0
static void duk__run_object_finalizers(duk_heap *heap) {
	duk_heaphdr *curr;
	duk_heaphdr *next;
#ifdef DUK_USE_DEBUG
	int count = 0;
#endif
	duk_hthread *thr;

	DUK_DD(DUK_DDPRINT("duk__run_object_finalizers: %p", (void *) heap));

	thr = duk__get_temp_hthread(heap);
	DUK_ASSERT(thr != NULL);

	curr = heap->finalize_list;
	while (curr) {
		DUK_DDD(DUK_DDDPRINT("mark-and-sweep finalize: %p", (void *) curr));

		DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT);  /* only objects have finalizers */
		DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(curr));                /* flags have been already cleared */
		DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(curr));
		DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr));
		DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr));

		/* run the finalizer */
		duk_hobject_run_finalizer(thr, (duk_hobject *) curr);  /* must never longjmp */

		/* mark FINALIZED, for next mark-and-sweep (will collect unless has become reachable;
		 * prevent running finalizer again if reachable)
		 */
		DUK_HEAPHDR_SET_FINALIZED(curr);

		/* queue back to heap_allocated */
		next = DUK_HEAPHDR_GET_NEXT(curr);
		DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, curr);

		curr = next;
#ifdef DUK_USE_DEBUG
		count++;
#endif
	}

	/* finalize_list will always be processed completely */
	heap->finalize_list = NULL;

#ifdef DUK_USE_DEBUG
	DUK_D(DUK_DPRINT("mark-and-sweep finalize objects: %d finalizers called", count));
#endif
}
DUK_INTERNAL void duk_heap_strcache_string_remove(duk_heap *heap, duk_hstring *h) {
    duk_small_int_t i;
    for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) {
        duk_strcache *c = heap->strcache + i;
        if (c->h == h) {
            DUK_DD(DUK_DDPRINT("deleting weak strcache reference to hstring %p from heap %p",
                               (void *) h, (void *) heap));
            c->h = NULL;

            /* XXX: the string shouldn't appear twice, but we now loop to the
             * end anyway; if fixed, add a looping assertion to ensure there
             * is no duplicate.
             */
        }
    }
}
Example #11
0
DUK_LOCAL duk_hstring *duk__do_lookup(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t *out_strhash) {
	duk_hstring *res;

	DUK_ASSERT(out_strhash);

	*out_strhash = duk_heap_hashstring(heap, str, (duk_size_t) blen);

#if defined(DUK_USE_ROM_STRINGS)
	{
		duk_small_uint_t i;
		/* XXX: This is VERY inefficient now, and should be e.g. a
		 * binary search or perfect hash, to be fixed.
		 */
		for (i = 0; i < (duk_small_uint_t) (sizeof(duk_rom_strings) / sizeof(duk_hstring *)); i++) {
			duk_hstring *romstr;
			romstr = (duk_hstring *) duk_rom_strings[i];
			if (blen == DUK_HSTRING_GET_BYTELEN(romstr) &&
			    DUK_MEMCMP(str, (void *) DUK_HSTRING_GET_DATA(romstr), blen) == 0) {
				DUK_DD(DUK_DDPRINT("intern check: rom string: %!O, computed hash 0x%08lx, rom hash 0x%08lx",
				                   romstr, (unsigned long) *out_strhash, (unsigned long) DUK_HSTRING_GET_HASH(romstr)));
				DUK_ASSERT(*out_strhash == DUK_HSTRING_GET_HASH(romstr));
				*out_strhash = DUK_HSTRING_GET_HASH(romstr);
				return romstr;
			}
		}
	}
#endif  /* DUK_USE_ROM_STRINGS */

#if defined(DUK_USE_STRTAB_CHAIN)
	res = duk__find_matching_string_chain(heap, str, blen, *out_strhash);
#elif defined(DUK_USE_STRTAB_PROBE)
	res = duk__find_matching_string_probe(heap,
#if defined(DUK_USE_HEAPPTR16)
	                                      heap->strtable16,
#else
	                                      heap->strtable,
#endif
	                                      heap->st_size,
	                                      str,
	                                      blen,
	                                      *out_strhash);
#else
#error internal error, invalid strtab options
#endif

	return res;
}
Example #12
0
DUK_LOCAL DUK_INLINE void duk__check_voluntary_gc(duk_heap *heap) {
	if (DUK_UNLIKELY(--(heap)->ms_trigger_counter < 0)) {
#if defined(DUK_USE_DEBUG)
		if (heap->ms_prevent_count == 0) {
			DUK_D(DUK_DPRINT("triggering voluntary mark-and-sweep"));
		} else {
			DUK_DD(DUK_DDPRINT("gc blocked -> skip voluntary mark-and-sweep now"));
		}
#endif

		/* Prevention checks in the call target handle cases where
		 * voluntary GC is not allowed.  The voluntary GC trigger
		 * counter is only rewritten if mark-and-sweep actually runs.
		 */
		duk_heap_mark_and_sweep(heap, DUK_MS_FLAG_VOLUNTARY /*flags*/);
	}
}
Example #13
0
static void duk__mark_roots_heap(duk_heap *heap) {
	int i;

	DUK_DD(DUK_DDPRINT("duk__mark_roots_heap: %p", (void *) heap));

	duk__mark_heaphdr(heap, (duk_heaphdr *) heap->heap_thread);
	duk__mark_heaphdr(heap, (duk_heaphdr *) heap->heap_object);
	duk__mark_heaphdr(heap, (duk_heaphdr *) heap->log_buffer);

	for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) {
		duk_hstring *h = heap->strs[i];
		duk__mark_heaphdr(heap, (duk_heaphdr *) h);
	}

	duk__mark_tval(heap, &heap->lj.value1);
	duk__mark_tval(heap, &heap->lj.value2);
}
Example #14
0
DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) {
	DUK_ASSERT(thr != NULL);

	DUK_DD(DUK_DDPRINT("longjmp error: type=%d iserror=%d value1=%!T value2=%!T",
	                   (int) thr->heap->lj.type, (int) thr->heap->lj.iserror,
	                   &thr->heap->lj.value1, &thr->heap->lj.value2));

#if !defined(DUK_USE_CPP_EXCEPTIONS)
	/* If we don't have a jmpbuf_ptr, there is little we can do except
	 * cause a fatal error.  The caller's expectation is that we never
	 * return.
	 *
	 * With C++ exceptions we now just propagate an uncaught error
	 * instead of invoking the fatal error handler.  Because there's
	 * a dummy jmpbuf for C++ exceptions now, this could be changed.
	 */
	if (!thr->heap->lj.jmpbuf_ptr) {
		DUK_D(DUK_DPRINT("uncaught error: type=%d iserror=%d value1=%!T value2=%!T",
		                 (int) thr->heap->lj.type, (int) thr->heap->lj.iserror,
		                 &thr->heap->lj.value1, &thr->heap->lj.value2));

#if defined(DUK_USE_PREFER_SIZE)
		duk__uncaught_minimal(thr);
#else
		duk__uncaught_error_aware(thr);
#endif
		DUK_UNREACHABLE();
	}
#endif  /* DUK_USE_CPP_EXCEPTIONS */

#if defined(DUK_USE_CPP_EXCEPTIONS)
	{
		duk_internal_exception exc;  /* dummy */
		throw exc;
	}
#else  /* DUK_USE_CPP_EXCEPTIONS */
	DUK_LONGJMP(thr->heap->lj.jmpbuf_ptr->jb);
#endif  /* DUK_USE_CPP_EXCEPTIONS */

	DUK_UNREACHABLE();
}
DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap) {
	duk_uint32_t i;
	duk_hstring *h;

	DUK_ASSERT(heap != NULL);
#if defined(DUK_USE_HEAPPTR16)
	DUK_ASSERT(heap->strtable16 != NULL);
#else
	DUK_ASSERT(heap->strtable != NULL);
#endif

	for (i = 0; i < heap->st_size; i++) {
#if defined(DUK_USE_HEAPPTR16)
		h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->strtable16[i]);
#else
		h = heap->strtable[i];
#endif

		DUK_DD(DUK_DDPRINT("[%03d] -> %p", (int) i, (void *) h));
	}
}
Example #16
0
DUK_LOCAL duk_bool_t duk__init_heap_thread(duk_heap *heap) {
	duk_hthread *thr;

	DUK_DD(DUK_DDPRINT("heap init: alloc heap thread"));
	thr = duk_hthread_alloc(heap,
	                        DUK_HOBJECT_FLAG_EXTENSIBLE |
	                        DUK_HOBJECT_FLAG_THREAD |
	                        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD));
	if (!thr) {
		DUK_D(DUK_DPRINT("failed to alloc heap_thread"));
		return 0;
	}
	thr->state = DUK_HTHREAD_STATE_INACTIVE;
#if defined(DUK_USE_ROM_STRINGS)
	/* No strs[] pointer. */
#else  /* DUK_USE_ROM_STRINGS */
#if defined(DUK_USE_HEAPPTR16)
	thr->strs16 = heap->strs16;
#else
	thr->strs = heap->strs;
#endif
#endif  /* DUK_USE_ROM_STRINGS */

	heap->heap_thread = thr;
	DUK_HTHREAD_INCREF(thr, thr);  /* Note: first argument not really used */

	/* 'thr' is now reachable */

	if (!duk_hthread_init_stacks(heap, thr)) {
		return 0;
	}

	/* XXX: this may now fail, and is not handled correctly */
	duk_hthread_create_builtin_objects(thr);

	/* default prototype (Note: 'thr' must be reachable) */
	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) thr, thr->builtins[DUK_BIDX_THREAD_PROTOTYPE]);

	return 1;
}
Example #17
0
DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) {
	DUK_ASSERT(thr != NULL);

	DUK_DD(DUK_DDPRINT("longjmp error: type=%d iserror=%d value1=%!T value2=%!T",
	                   (int) thr->heap->lj.type, (int) thr->heap->lj.iserror,
	                   &thr->heap->lj.value1, &thr->heap->lj.value2));

#if defined(DUK_USE_CPP_EXCEPTIONS)
	/* XXX: detecting uncaught exception case for C++ case; perhaps need
	 * some marker in heap->lj state that a try-catch is active.  For now,
	 * invokes C++ uncaught exception handling.
	 */
#else
	if (!thr->heap->lj.jmpbuf_ptr) {
		/*
		 *  If we don't have a jmpbuf_ptr, there is little we can do
		 *  except panic.  The caller's expectation is that we never
		 *  return.
		 */

		DUK_D(DUK_DPRINT("uncaught error: type=%d iserror=%d value1=%!T value2=%!T",
		                 (int) thr->heap->lj.type, (int) thr->heap->lj.iserror,
		                 &thr->heap->lj.value1, &thr->heap->lj.value2));

		duk_fatal((duk_context *) thr, DUK_ERR_UNCAUGHT_ERROR, "uncaught error");
		DUK_UNREACHABLE();
	}
#endif

#if defined(DUK_USE_CPP_EXCEPTIONS)
	{
		duk_internal_exception exc;  /* dummy */
		throw exc;
	}
#else
	DUK_LONGJMP(thr->heap->lj.jmpbuf_ptr->jb);
#endif
	DUK_UNREACHABLE();
}
Example #18
0
DUK_INTERNAL void duk_hthread_catchstack_grow(duk_hthread *thr) {
	duk_catcher *new_ptr;
	duk_size_t old_size;
	duk_size_t new_size;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT_DISABLE(thr->catchstack_top);  /* avoid warning (unsigned) */
	DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top);

	if (thr->catchstack_top < thr->catchstack_size) {
		return;
	}

	old_size = thr->catchstack_size;
	new_size = old_size + DUK_CATCHSTACK_GROW_STEP;

	/* this is a bit approximate (errors out before max is reached); this is OK */
	if (new_size >= thr->catchstack_max) {
		DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, DUK_STR_CATCHSTACK_LIMIT);
	}

	DUK_DD(DUK_DDPRINT("growing catchstack %ld -> %ld", (long) old_size, (long) new_size));

	/*
	 *  Note: must use indirect variant of DUK_REALLOC() because underlying
	 *  pointer may be changed by mark-and-sweep.
	 */

	DUK_ASSERT(new_size > 0);
	new_ptr = (duk_catcher *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_catchstack_ptr, (void *) thr, sizeof(duk_catcher) * new_size);
	if (!new_ptr) {
		/* No need for a NULL/zero-size check because new_size > 0) */
		DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, DUK_STR_REALLOC_FAILED);
	}
	thr->catchstack = new_ptr;
	thr->catchstack_size = new_size;

	/* note: any entries above the catchstack top are garbage and not zeroed */
}
Example #19
0
DUK_LOCAL void duk__mark_roots_heap(duk_heap *heap) {
	duk_small_uint_t i;

	DUK_DD(DUK_DDPRINT("duk__mark_roots_heap: %p", (void *) heap));

	duk__mark_heaphdr(heap, (duk_heaphdr *) heap->heap_thread);
	duk__mark_heaphdr(heap, (duk_heaphdr *) heap->heap_object);

	for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) {
		duk_hstring *h = DUK_HEAP_GET_STRING(heap, i);
		duk__mark_heaphdr(heap, (duk_heaphdr *) h);
	}

	duk__mark_tval(heap, &heap->lj.value1);
	duk__mark_tval(heap, &heap->lj.value2);

#if defined(DUK_USE_DEBUGGER_SUPPORT)
	for (i = 0; i < heap->dbg_breakpoint_count; i++) {
		duk__mark_heaphdr(heap, (duk_heaphdr *) heap->dbg_breakpoints[i].filename);
	}
#endif
}
Example #20
0
DUK_LOCAL duk_bool_t duk__init_heap_strings(duk_heap *heap) {
#if defined(DUK_USE_ASSERTIONS)
	duk_small_uint_t i;
#endif

	/* With ROM-based strings, heap->strs[] and thr->strs[] are omitted
	 * so nothing to initialize for strs[].
	 */

#if defined(DUK_USE_ASSERTIONS)
	for (i = 0; i < sizeof(duk_rom_strings) / sizeof(const duk_hstring *); i++) {
		duk_uint32_t hash;
		const duk_hstring *h;
		h = duk_rom_strings[i];
		DUK_ASSERT(h != NULL);
		hash = duk_heap_hashstring(heap, (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h));
		DUK_DD(DUK_DDPRINT("duk_rom_strings[%d] -> hash 0x%08lx, computed 0x%08lx",
		                   (int) i, (unsigned long) DUK_HSTRING_GET_HASH(h), (unsigned long) hash));
		DUK_ASSERT(hash == (duk_uint32_t) DUK_HSTRING_GET_HASH(h));
	}
#endif
	return 1;
}
Example #21
0
DUK_INTERNAL void duk_hthread_callstack_shrink_check(duk_hthread *thr) {
	duk_size_t new_size;
	duk_activation *p;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT_DISABLE(thr->callstack_top >= 0);  /* avoid warning (unsigned) */
	DUK_ASSERT(thr->callstack_size >= thr->callstack_top);

	if (thr->callstack_size - thr->callstack_top < DUK_CALLSTACK_SHRINK_THRESHOLD) {
		return;
	}

	new_size = thr->callstack_top + DUK_CALLSTACK_SHRINK_SPARE;
	DUK_ASSERT(new_size >= thr->callstack_top);

	DUK_DD(DUK_DDPRINT("shrinking callstack %ld -> %ld", (long) thr->callstack_size, (long) new_size));

	/*
	 *  Note: must use indirect variant of DUK_REALLOC() because underlying
	 *  pointer may be changed by mark-and-sweep.
	 */

	/* shrink failure is not fatal */
	p = (duk_activation *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_callstack_ptr, (void *) thr, sizeof(duk_activation) * new_size);
	if (p) {
		thr->callstack = p;
		thr->callstack_size = new_size;
	} else {
		/* Because new_size != 0, if condition doesn't need to be
		 * (p != NULL || new_size == 0).
		 */
		DUK_ASSERT(new_size != 0);
		DUK_D(DUK_DPRINT("callstack shrink failed, ignoring"));
	}

	/* note: any entries above the callstack top are garbage and not zeroed */
}
Example #22
0
DUK_LOCAL void duk__refcount_run_torture_finalizer(duk_hthread *thr, duk_hobject *obj) {
	duk_context *ctx;
	duk_int_t rc;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(obj != NULL);
	ctx = (duk_context *) thr;

	/* Avoid fake finalization for the duk__refcount_fake_finalizer function
	 * itself, otherwise we're in infinite recursion.
	 */
	if (DUK_HOBJECT_HAS_NATIVEFUNCTION(obj)) {
		if (((duk_hnativefunction *) obj)->func == duk__refcount_fake_finalizer) {
			DUK_DD(DUK_DDPRINT("avoid fake torture finalizer for duk__refcount_fake_finalizer itself"));
			return;
		}
	}
	/* Avoid fake finalization when callstack limit has been reached.
	 * Otherwise a callstack limit error will be created, then refzero'ed,
	 * and we're in an infinite loop.
	 */
	if (thr->heap->call_recursion_depth >= thr->heap->call_recursion_limit ||
	    thr->callstack_size + 2 * DUK_CALLSTACK_GROW_STEP >= thr->callstack_max /*approximate*/) {
		DUK_D(DUK_DPRINT("call recursion depth reached, avoid fake torture finalizer"));
		return;
	}

	/* Run fake finalizer.  Avoid creating new refzero queue entries
	 * so that we are not forced into a forever loop.
	 */
	duk_push_c_function(ctx, duk__refcount_fake_finalizer, 1 /*nargs*/);
	duk_push_hobject(ctx, obj);
	rc = duk_pcall(ctx, 1);
	DUK_UNREF(rc);  /* ignored */
	duk_pop(ctx);
}
Example #23
0
DUK_LOCAL void duk__mark_finalize_list(duk_heap *heap) {
	duk_heaphdr *hdr;
#if defined(DUK_USE_DEBUG)
	duk_size_t count_finalize_list = 0;
#endif

	DUK_DD(DUK_DDPRINT("duk__mark_finalize_list: %p", (void *) heap));

	hdr = heap->finalize_list;
	while (hdr) {
		duk__mark_heaphdr(heap, hdr);
		hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
#if defined(DUK_USE_DEBUG)
		count_finalize_list++;
#endif
	}

#if defined(DUK_USE_DEBUG)
	if (count_finalize_list > 0) {
		DUK_D(DUK_DPRINT("marked %ld objects on the finalize_list as reachable (previous finalizer run skipped)",
		                 (long) count_finalize_list));
	}
#endif
}
Example #24
0
DUK_LOCAL void duk__err_augment_user(duk_hthread *thr, duk_small_uint_t stridx_cb) {
	duk_context *ctx = (duk_context *) thr;
	duk_tval *tv_hnd;
	duk_small_uint_t call_flags;
	duk_int_t rc;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(thr->heap != NULL);
	DUK_ASSERT_DISABLE(stridx_cb >= 0);  /* unsigned */
	DUK_ASSERT(stridx_cb < DUK_HEAP_NUM_STRINGS);

	if (DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap)) {
		DUK_DD(DUK_DDPRINT("recursive call to error handler, ignore"));
		return;
	}

	/*
	 *  Check whether or not we have an error handler.
	 *
	 *  We must be careful of not triggering an error when looking up the
	 *  property.  For instance, if the property is a getter, we don't want
	 *  to call it, only plain values are allowed.  The value, if it exists,
	 *  is not checked.  If the value is not a function, a TypeError happens
	 *  when it is called and that error replaces the original one.
	 */

	DUK_ASSERT_VALSTACK_SPACE(thr, 4);  /* 3 entries actually needed below */

	/* [ ... errval ] */

	if (thr->builtins[DUK_BIDX_DUKTAPE] == NULL) {
		/* When creating built-ins, some of the built-ins may not be set
		 * and we want to tolerate that when throwing errors.
		 */
		DUK_DD(DUK_DDPRINT("error occurred when DUK_BIDX_DUKTAPE is NULL, ignoring"));
		return;
	}
	tv_hnd = duk_hobject_find_existing_entry_tval_ptr(thr->heap,
	                                                  thr->builtins[DUK_BIDX_DUKTAPE],
	                                                  DUK_HTHREAD_GET_STRING(thr, stridx_cb));
	if (tv_hnd == NULL) {
		DUK_DD(DUK_DDPRINT("error handler does not exist or is not a plain value: %!T",
		                   (duk_tval *) tv_hnd));
		return;
	}
	DUK_DDD(DUK_DDDPRINT("error handler dump (callability not checked): %!T",
	                     (duk_tval *) tv_hnd));
	duk_push_tval(ctx, tv_hnd);

	/* [ ... errval errhandler ] */

	duk_insert(ctx, -2);  /* -> [ ... errhandler errval ] */
	duk_push_undefined(ctx);
	duk_insert(ctx, -2);  /* -> [ ... errhandler undefined(= this) errval ] */

	/* [ ... errhandler undefined errval ] */

	/*
	 *  DUK_CALL_FLAG_IGNORE_RECLIMIT causes duk_handle_call() to ignore C
	 *  recursion depth limit (and won't increase it either).  This is
	 *  dangerous, but useful because it allows the error handler to run
	 *  even if the original error is caused by C recursion depth limit.
	 *
	 *  The heap level DUK_HEAP_FLAG_ERRHANDLER_RUNNING is set for the
	 *  duration of the error handler and cleared afterwards.  This flag
	 *  prevents the error handler from running recursively.  The flag is
	 *  heap level so that the flag properly controls even coroutines
	 *  launched by an error handler.  Since the flag is heap level, it is
	 *  critical to restore it correctly.
	 *
	 *  We ignore errors now: a success return and an error value both
	 *  replace the original error value.  (This would be easy to change.)
	 */

	DUK_ASSERT(!DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap));  /* since no recursive error handler calls */
	DUK_HEAP_SET_ERRHANDLER_RUNNING(thr->heap);

	call_flags = DUK_CALL_FLAG_IGNORE_RECLIMIT;  /* ignore reclimit, not constructor */

	rc = duk_handle_call_protected(thr,
	                               1,            /* num args */
	                               call_flags);  /* call_flags */
	DUK_UNREF(rc);  /* no need to check now: both success and error are OK */

	DUK_ASSERT(DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap));
	DUK_HEAP_CLEAR_ERRHANDLER_RUNNING(thr->heap);

	/* [ ... errval ] */
}
Example #25
0
DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) {
	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(thr->heap != NULL);

	DUK_DD(DUK_DDPRINT("longjmp error: type=%d iserror=%d value1=%!T value2=%!T",
	                   (int) thr->heap->lj.type, (int) thr->heap->lj.iserror,
	                   &thr->heap->lj.value1, &thr->heap->lj.value2));

	/* Prevent finalizer execution during error handling.  All error
	 * handling sites will process pending finalizers once error handling
	 * is complete and we're ready for the side effects.  Does not prevent
	 * refzero freeing or mark-and-sweep during error handling.
	 *
	 * NOTE: when we come here some calling code may have used DECREF
	 * NORZ macros without an explicit DUK_REFZERO_CHECK_xxx() call.
	 * We don't want to do it here because it would just check for
	 * pending finalizers and we prevent that explicitly.  Instead,
	 * the error catcher will run the finalizers once error handling
	 * is complete.
	 */

	DUK_ASSERT_LJSTATE_SET(thr->heap);

	thr->heap->pf_prevent_count++;
	DUK_ASSERT(thr->heap->pf_prevent_count != 0);  /* Wrap. */

#if defined(DUK_USE_ASSERTIONS)
	/* XXX: set this immediately when longjmp state is set */
	DUK_ASSERT(thr->heap->error_not_allowed == 0);  /* Detect error within critical section. */
	thr->heap->error_not_allowed = 1;
#endif

	DUK_DD(DUK_DDPRINT("about to longjmp, pf_prevent_count=%ld", (long) thr->heap->pf_prevent_count));

#if !defined(DUK_USE_CPP_EXCEPTIONS)
	/* If we don't have a jmpbuf_ptr, there is little we can do except
	 * cause a fatal error.  The caller's expectation is that we never
	 * return.
	 *
	 * With C++ exceptions we now just propagate an uncaught error
	 * instead of invoking the fatal error handler.  Because there's
	 * a dummy jmpbuf for C++ exceptions now, this could be changed.
	 */
	if (!thr->heap->lj.jmpbuf_ptr) {
		DUK_D(DUK_DPRINT("uncaught error: type=%d iserror=%d value1=%!T value2=%!T",
		                 (int) thr->heap->lj.type, (int) thr->heap->lj.iserror,
		                 &thr->heap->lj.value1, &thr->heap->lj.value2));

#if defined(DUK_USE_PREFER_SIZE)
		duk__uncaught_minimal(thr);
#else
		duk__uncaught_error_aware(thr);
#endif
		DUK_UNREACHABLE();
	}
#endif  /* DUK_USE_CPP_EXCEPTIONS */

#if defined(DUK_USE_CPP_EXCEPTIONS)
	{
		duk_internal_exception exc;  /* dummy */
		throw exc;
	}
#else  /* DUK_USE_CPP_EXCEPTIONS */
	DUK_LONGJMP(thr->heap->lj.jmpbuf_ptr->jb);
#endif  /* DUK_USE_CPP_EXCEPTIONS */

	DUK_UNREACHABLE();
}
Example #26
0
DUK_INTERNAL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr cb, void *ud, duk_size_t newsize) {
	void *res;
	duk_bool_t rc;
	duk_small_int_t i;

	DUK_ASSERT(heap != NULL);
	DUK_ASSERT_DISABLE(newsize >= 0);

	/*
	 *  Voluntary periodic GC (if enabled)
	 */

	DUK__VOLUNTARY_PERIODIC_GC(heap);

	/*
	 *  First attempt
	 */

#if defined(DUK_USE_GC_TORTURE)
	/* simulate alloc failure on every realloc (except when mark-and-sweep is running) */
	if (!DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) {
		DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first indirect realloc attempt fails"));
		res = NULL;
		DUK_UNREF(res);
		goto skip_attempt;
	}
#endif
	res = heap->realloc_func(heap->heap_udata, cb(heap, ud), newsize);
	if (res || newsize == 0) {
		/* for zero size allocations NULL is allowed */
		return res;
	}
#if defined(DUK_USE_GC_TORTURE)
 skip_attempt:
#endif

	DUK_D(DUK_DPRINT("first indirect realloc attempt failed, attempt to gc and retry"));

	/*
	 *  Avoid a GC if GC is already running.  See duk_heap_mem_alloc().
	 */

	if (DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) {
		DUK_D(DUK_DPRINT("duk_heap_mem_realloc_indirect() failed, gc in progress (gc skipped), alloc size %ld", (long) newsize));
		return NULL;
	}

	/*
	 *  Retry with several GC attempts.  Initial attempts are made without
	 *  emergency mode; later attempts use emergency mode which minimizes
	 *  memory allocations forcibly.
	 */

	for (i = 0; i < DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT; i++) {
		duk_small_uint_t flags;

#if defined(DUK_USE_ASSERTIONS)
		void *ptr_pre;  /* ptr before mark-and-sweep */
		void *ptr_post;
#endif

#if defined(DUK_USE_ASSERTIONS)
		ptr_pre = cb(heap, ud);
#endif
		flags = 0;
		if (i >= DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_EMERGENCY_LIMIT - 1) {
			flags |= DUK_MS_FLAG_EMERGENCY;
		}

		rc = duk_heap_mark_and_sweep(heap, flags);
		DUK_UNREF(rc);
#if defined(DUK_USE_ASSERTIONS)
		ptr_post = cb(heap, ud);
		if (ptr_pre != ptr_post) {
			/* useful for debugging */
			DUK_DD(DUK_DDPRINT("note: base pointer changed by mark-and-sweep: %p -> %p",
			                   (void *) ptr_pre, (void *) ptr_post));
		}
#endif

		/* Note: key issue here is to re-lookup the base pointer on every attempt.
		 * The pointer being reallocated may change after every mark-and-sweep.
		 */

		res = heap->realloc_func(heap->heap_udata, cb(heap, ud), newsize);
		if (res || newsize == 0) {
			DUK_D(DUK_DPRINT("duk_heap_mem_realloc_indirect() succeeded after gc (pass %ld), alloc size %ld",
			                 (long) (i + 1), (long) newsize));
			return res;
		}
	}

	DUK_D(DUK_DPRINT("duk_heap_mem_realloc_indirect() failed even after gc, alloc size %ld", (long) newsize));
	return NULL;
}
DUK_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t expect_eof, duk__re_disjunction_info *out_atom_info) {
    duk_int32_t atom_start_offset = -1;                   /* negative -> no atom matched on previous round */
    duk_int32_t atom_char_length = 0;                     /* negative -> complex atom */
    duk_uint32_t atom_start_captures = re_ctx->captures;  /* value of re_ctx->captures at start of atom */
    duk_int32_t unpatched_disjunction_split = -1;
    duk_int32_t unpatched_disjunction_jump = -1;
    duk_uint32_t entry_offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx);
    duk_int32_t res_charlen = 0;  /* -1 if disjunction is complex, char length if simple */
    duk__re_disjunction_info tmp_disj;

    DUK_ASSERT(out_atom_info != NULL);

    if (re_ctx->recursion_depth >= re_ctx->recursion_limit) {
        DUK_ERROR_RANGE(re_ctx->thr, DUK_STR_REGEXP_COMPILER_RECURSION_LIMIT);
    }
    re_ctx->recursion_depth++;

#if 0
    out_atom_info->start_captures = re_ctx->captures;
#endif

    for (;;) {
        /* atom_char_length, atom_start_offset, atom_start_offset reflect the
         * atom matched on the previous loop.  If a quantifier is encountered
         * on this loop, these are needed to handle the quantifier correctly.
         * new_atom_char_length etc are for the atom parsed on this round;
         * they're written to atom_char_length etc at the end of the round.
         */
        duk_int32_t new_atom_char_length;   /* char length of the atom parsed in this loop */
        duk_int32_t new_atom_start_offset;  /* bytecode start offset of the atom parsed in this loop
		                                     * (allows quantifiers to copy the atom bytecode)
		                                     */
        duk_uint32_t new_atom_start_captures;  /* re_ctx->captures at the start of the atom parsed in this loop */

        duk_lexer_parse_re_token(&re_ctx->lex, &re_ctx->curr_token);

        DUK_DD(DUK_DDPRINT("re token: %ld (num=%ld, char=%c)",
                           (long) re_ctx->curr_token.t,
                           (long) re_ctx->curr_token.num,
                           (re_ctx->curr_token.num >= 0x20 && re_ctx->curr_token.num <= 0x7e) ?
                           (int) re_ctx->curr_token.num : (int) '?'));

        /* set by atom case clauses */
        new_atom_start_offset = -1;
        new_atom_char_length = -1;
        new_atom_start_captures = re_ctx->captures;

        switch (re_ctx->curr_token.t) {
        case DUK_RETOK_DISJUNCTION: {
            /*
             *  The handling here is a bit tricky.  If a previous '|' has been processed,
             *  we have a pending split1 and a pending jump (for a previous match).  These
             *  need to be back-patched carefully.  See docs for a detailed example.
             */

            /* patch pending jump and split */
            if (unpatched_disjunction_jump >= 0) {
                duk_uint32_t offset;

                DUK_ASSERT(unpatched_disjunction_split >= 0);
                offset = unpatched_disjunction_jump;
                offset += duk__insert_jump_offset(re_ctx,
                                                  offset,
                                                  (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - offset));
                /* offset is now target of the pending split (right after jump) */
                duk__insert_jump_offset(re_ctx,
                                        unpatched_disjunction_split,
                                        offset - unpatched_disjunction_split);
            }

            /* add a new pending split to the beginning of the entire disjunction */
            (void) duk__insert_u32(re_ctx,
                                   entry_offset,
                                   DUK_REOP_SPLIT1);   /* prefer direct execution */
            unpatched_disjunction_split = entry_offset + 1;   /* +1 for opcode */

            /* add a new pending match jump for latest finished alternative */
            duk__append_u32(re_ctx, DUK_REOP_JUMP);
            unpatched_disjunction_jump = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);

            /* 'taint' result as complex */
            res_charlen = -1;
            break;
        }
        case DUK_RETOK_QUANTIFIER: {
            if (atom_start_offset < 0) {
                DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_INVALID_QUANTIFIER_NO_ATOM);
            }
            if (re_ctx->curr_token.qmin > re_ctx->curr_token.qmax) {
                DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_INVALID_QUANTIFIER_VALUES);
            }
            if (atom_char_length >= 0) {
                /*
                 *  Simple atom
                 *
                 *  If atom_char_length is zero, we'll have unbounded execution time for e.g.
                 *  /()*x/.exec('x').  We can't just skip the match because it might have some
                 *  side effects (for instance, if we allowed captures in simple atoms, the
                 *  capture needs to happen).  The simple solution below is to force the
                 *  quantifier to match at most once, since the additional matches have no effect.
                 *
                 *  With a simple atom there can be no capture groups, so no captures need
                 *  to be reset.
                 */
                duk_int32_t atom_code_length;
                duk_uint32_t offset;
                duk_uint32_t qmin, qmax;

                qmin = re_ctx->curr_token.qmin;
                qmax = re_ctx->curr_token.qmax;
                if (atom_char_length == 0) {
                    /* qmin and qmax will be 0 or 1 */
                    if (qmin > 1) {
                        qmin = 1;
                    }
                    if (qmax > 1) {
                        qmax = 1;
                    }
                }

                duk__append_u32(re_ctx, DUK_REOP_MATCH);   /* complete 'sub atom' */
                atom_code_length = (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - atom_start_offset);

                offset = atom_start_offset;
                if (re_ctx->curr_token.greedy) {
                    offset += duk__insert_u32(re_ctx, offset, DUK_REOP_SQGREEDY);
                    offset += duk__insert_u32(re_ctx, offset, qmin);
                    offset += duk__insert_u32(re_ctx, offset, qmax);
                    offset += duk__insert_u32(re_ctx, offset, atom_char_length);
                    offset += duk__insert_jump_offset(re_ctx, offset, atom_code_length);
                } else {
                    offset += duk__insert_u32(re_ctx, offset, DUK_REOP_SQMINIMAL);
                    offset += duk__insert_u32(re_ctx, offset, qmin);
                    offset += duk__insert_u32(re_ctx, offset, qmax);
                    offset += duk__insert_jump_offset(re_ctx, offset, atom_code_length);
                }
                DUK_UNREF(offset);  /* silence scan-build warning */
            } else {
                /*
                 *  Complex atom
                 *
                 *  The original code is used as a template, and removed at the end
                 *  (this differs from the handling of simple quantifiers).
                 *
                 *  NOTE: there is no current solution for empty atoms in complex
                 *  quantifiers.  This would need some sort of a 'progress' instruction.
                 *
                 *  XXX: impose limit on maximum result size, i.e. atom_code_len * atom_copies?
                 */
                duk_int32_t atom_code_length;
                duk_uint32_t atom_copies;
                duk_uint32_t tmp_qmin, tmp_qmax;

                /* pre-check how many atom copies we're willing to make (atom_copies not needed below) */
                atom_copies = (re_ctx->curr_token.qmax == DUK_RE_QUANTIFIER_INFINITE) ?
                              re_ctx->curr_token.qmin : re_ctx->curr_token.qmax;
                if (atom_copies > DUK_RE_MAX_ATOM_COPIES) {
                    DUK_ERROR_RANGE(re_ctx->thr, DUK_STR_QUANTIFIER_TOO_MANY_COPIES);
                }

                /* wipe the capture range made by the atom (if any) */
                DUK_ASSERT(atom_start_captures <= re_ctx->captures);
                if (atom_start_captures != re_ctx->captures) {
                    DUK_ASSERT(atom_start_captures < re_ctx->captures);
                    DUK_DDD(DUK_DDDPRINT("must wipe ]atom_start_captures,re_ctx->captures]: ]%ld,%ld]",
                                         (long) atom_start_captures, (long) re_ctx->captures));

                    /* insert (DUK_REOP_WIPERANGE, start, count) in reverse order so the order ends up right */
                    duk__insert_u32(re_ctx, atom_start_offset, (re_ctx->captures - atom_start_captures) * 2);
                    duk__insert_u32(re_ctx, atom_start_offset, (atom_start_captures + 1) * 2);
                    duk__insert_u32(re_ctx, atom_start_offset, DUK_REOP_WIPERANGE);
                } else {
                    DUK_DDD(DUK_DDDPRINT("no need to wipe captures: atom_start_captures == re_ctx->captures == %ld",
                                         (long) atom_start_captures));
                }

                atom_code_length = (duk_int32_t) DUK__RE_BUFLEN(re_ctx) - atom_start_offset;

                /* insert the required matches (qmin) by copying the atom */
                tmp_qmin = re_ctx->curr_token.qmin;
                tmp_qmax = re_ctx->curr_token.qmax;
                while (tmp_qmin > 0) {
                    duk__append_slice(re_ctx, atom_start_offset, atom_code_length);
                    tmp_qmin--;
                    if (tmp_qmax != DUK_RE_QUANTIFIER_INFINITE) {
                        tmp_qmax--;
                    }
                }
                DUK_ASSERT(tmp_qmin == 0);

                /* insert code for matching the remainder - infinite or finite */
                if (tmp_qmax == DUK_RE_QUANTIFIER_INFINITE) {
                    /* reuse last emitted atom for remaining 'infinite' quantifier */

                    if (re_ctx->curr_token.qmin == 0) {
                        /* Special case: original qmin was zero so there is nothing
                         * to repeat.  Emit an atom copy but jump over it here.
                         */
                        duk__append_u32(re_ctx, DUK_REOP_JUMP);
                        duk__append_jump_offset(re_ctx, atom_code_length);
                        duk__append_slice(re_ctx, atom_start_offset, atom_code_length);
                    }
                    if (re_ctx->curr_token.greedy) {
                        duk__append_u32(re_ctx, DUK_REOP_SPLIT2);   /* prefer jump */
                    } else {
                        duk__append_u32(re_ctx, DUK_REOP_SPLIT1);   /* prefer direct */
                    }
                    duk__append_jump_offset(re_ctx, -atom_code_length - 1);  /* -1 for opcode */
                } else {
                    /*
                     *  The remaining matches are emitted as sequence of SPLITs and atom
                     *  copies; the SPLITs skip the remaining copies and match the sequel.
                     *  This sequence needs to be emitted starting from the last copy
                     *  because the SPLITs are variable length due to the variable length
                     *  skip offset.  This causes a lot of memory copying now.
                     *
                     *  Example structure (greedy, match maximum # atoms):
                     *
                     *      SPLIT1 LSEQ
                     *      (atom)
                     *      SPLIT1 LSEQ    ; <- the byte length of this instruction is needed
                     *      (atom)         ; to encode the above SPLIT1 correctly
                     *      ...
                     *   LSEQ:
                     */
                    duk_uint32_t offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx);
                    while (tmp_qmax > 0) {
                        duk__insert_slice(re_ctx, offset, atom_start_offset, atom_code_length);
                        if (re_ctx->curr_token.greedy) {
                            duk__insert_u32(re_ctx, offset, DUK_REOP_SPLIT1);   /* prefer direct */
                        } else {
                            duk__insert_u32(re_ctx, offset, DUK_REOP_SPLIT2);   /* prefer jump */
                        }
                        duk__insert_jump_offset(re_ctx,
                                                offset + 1,   /* +1 for opcode */
                                                (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - (offset + 1)));
                        tmp_qmax--;
                    }
                }

                /* remove the original 'template' atom */
                duk__remove_slice(re_ctx, atom_start_offset, atom_code_length);
            }

            /* 'taint' result as complex */
            res_charlen = -1;
            break;
        }
        case DUK_RETOK_ASSERT_START: {
            duk__append_u32(re_ctx, DUK_REOP_ASSERT_START);
            break;
        }
        case DUK_RETOK_ASSERT_END: {
            duk__append_u32(re_ctx, DUK_REOP_ASSERT_END);
            break;
        }
        case DUK_RETOK_ASSERT_WORD_BOUNDARY: {
            duk__append_u32(re_ctx, DUK_REOP_ASSERT_WORD_BOUNDARY);
            break;
        }
        case DUK_RETOK_ASSERT_NOT_WORD_BOUNDARY: {
            duk__append_u32(re_ctx, DUK_REOP_ASSERT_NOT_WORD_BOUNDARY);
            break;
        }
        case DUK_RETOK_ASSERT_START_POS_LOOKAHEAD:
        case DUK_RETOK_ASSERT_START_NEG_LOOKAHEAD: {
            duk_uint32_t offset;
            duk_uint32_t opcode = (re_ctx->curr_token.t == DUK_RETOK_ASSERT_START_POS_LOOKAHEAD) ?
                                  DUK_REOP_LOOKPOS : DUK_REOP_LOOKNEG;

            offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx);
            duk__parse_disjunction(re_ctx, 0, &tmp_disj);
            duk__append_u32(re_ctx, DUK_REOP_MATCH);

            (void) duk__insert_u32(re_ctx, offset, opcode);
            (void) duk__insert_jump_offset(re_ctx,
                                           offset + 1,   /* +1 for opcode */
                                           (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - (offset + 1)));

            /* 'taint' result as complex -- this is conservative,
             * as lookaheads do not backtrack.
             */
            res_charlen = -1;
            break;
        }
        case DUK_RETOK_ATOM_PERIOD: {
            new_atom_char_length = 1;
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__append_u32(re_ctx, DUK_REOP_PERIOD);
            break;
        }
        case DUK_RETOK_ATOM_CHAR: {
            /* Note: successive characters could be joined into string matches
             * but this is not trivial (consider e.g. '/xyz+/); see docs for
             * more discussion.
             */
            duk_uint32_t ch;

            new_atom_char_length = 1;
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__append_u32(re_ctx, DUK_REOP_CHAR);
            ch = re_ctx->curr_token.num;
            if (re_ctx->re_flags & DUK_RE_FLAG_IGNORE_CASE) {
                ch = duk_unicode_re_canonicalize_char(re_ctx->thr, ch);
            }
            duk__append_u32(re_ctx, ch);
            break;
        }
        case DUK_RETOK_ATOM_DIGIT:
        case DUK_RETOK_ATOM_NOT_DIGIT: {
            new_atom_char_length = 1;
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__append_u32(re_ctx,
                            (re_ctx->curr_token.t == DUK_RETOK_ATOM_DIGIT) ?
                            DUK_REOP_RANGES : DUK_REOP_INVRANGES);
            duk__append_u32(re_ctx, sizeof(duk_unicode_re_ranges_digit) / (2 * sizeof(duk_uint16_t)));
            duk__append_u16_list(re_ctx, duk_unicode_re_ranges_digit, sizeof(duk_unicode_re_ranges_digit) / sizeof(duk_uint16_t));
            break;
        }
        case DUK_RETOK_ATOM_WHITE:
        case DUK_RETOK_ATOM_NOT_WHITE: {
            new_atom_char_length = 1;
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__append_u32(re_ctx,
                            (re_ctx->curr_token.t == DUK_RETOK_ATOM_WHITE) ?
                            DUK_REOP_RANGES : DUK_REOP_INVRANGES);
            duk__append_u32(re_ctx, sizeof(duk_unicode_re_ranges_white) / (2 * sizeof(duk_uint16_t)));
            duk__append_u16_list(re_ctx, duk_unicode_re_ranges_white, sizeof(duk_unicode_re_ranges_white) / sizeof(duk_uint16_t));
            break;
        }
        case DUK_RETOK_ATOM_WORD_CHAR:
        case DUK_RETOK_ATOM_NOT_WORD_CHAR: {
            new_atom_char_length = 1;
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__append_u32(re_ctx,
                            (re_ctx->curr_token.t == DUK_RETOK_ATOM_WORD_CHAR) ?
                            DUK_REOP_RANGES : DUK_REOP_INVRANGES);
            duk__append_u32(re_ctx, sizeof(duk_unicode_re_ranges_wordchar) / (2 * sizeof(duk_uint16_t)));
            duk__append_u16_list(re_ctx, duk_unicode_re_ranges_wordchar, sizeof(duk_unicode_re_ranges_wordchar) / sizeof(duk_uint16_t));
            break;
        }
        case DUK_RETOK_ATOM_BACKREFERENCE: {
            duk_uint32_t backref = (duk_uint32_t) re_ctx->curr_token.num;
            if (backref > re_ctx->highest_backref) {
                re_ctx->highest_backref = backref;
            }
            new_atom_char_length = -1;   /* mark as complex */
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__append_u32(re_ctx, DUK_REOP_BACKREFERENCE);
            duk__append_u32(re_ctx, backref);
            break;
        }
        case DUK_RETOK_ATOM_START_CAPTURE_GROUP: {
            duk_uint32_t cap;

            new_atom_char_length = -1;   /* mark as complex (capture handling) */
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            cap = ++re_ctx->captures;
            duk__append_u32(re_ctx, DUK_REOP_SAVE);
            duk__append_u32(re_ctx, cap * 2);
            duk__parse_disjunction(re_ctx, 0, &tmp_disj);  /* retval (sub-atom char length) unused, tainted as complex above */
            duk__append_u32(re_ctx, DUK_REOP_SAVE);
            duk__append_u32(re_ctx, cap * 2 + 1);
            break;
        }
        case DUK_RETOK_ATOM_START_NONCAPTURE_GROUP: {
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__parse_disjunction(re_ctx, 0, &tmp_disj);
            new_atom_char_length = tmp_disj.charlen;
            break;
        }
        case DUK_RETOK_ATOM_START_CHARCLASS:
        case DUK_RETOK_ATOM_START_CHARCLASS_INVERTED: {
            /*
             *  Range parsing is done with a special lexer function which calls
             *  us for every range parsed.  This is different from how rest of
             *  the parsing works, but avoids a heavy, arbitrary size intermediate
             *  value type to hold the ranges.
             *
             *  Another complication is the handling of character ranges when
             *  case insensitive matching is used (see docs for discussion).
             *  The range handler callback given to the lexer takes care of this
             *  as well.
             *
             *  Note that duplicate ranges are not eliminated when parsing character
             *  classes, so that canonicalization of
             *
             *    [0-9a-fA-Fx-{]
             *
             *  creates the result (note the duplicate ranges):
             *
             *    [0-9A-FA-FX-Z{-{]
             *
             *  where [x-{] is split as a result of canonicalization.  The duplicate
             *  ranges are not a semantics issue: they work correctly.
             */

            duk_uint32_t offset;

            DUK_DD(DUK_DDPRINT("character class"));

            /* insert ranges instruction, range count patched in later */
            new_atom_char_length = 1;
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__append_u32(re_ctx,
                            (re_ctx->curr_token.t == DUK_RETOK_ATOM_START_CHARCLASS) ?
                            DUK_REOP_RANGES : DUK_REOP_INVRANGES);
            offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx);    /* patch in range count later */

            /* parse ranges until character class ends */
            re_ctx->nranges = 0;    /* note: ctx-wide temporary */
            duk_lexer_parse_re_ranges(&re_ctx->lex, duk__generate_ranges, (void *) re_ctx);

            /* insert range count */
            duk__insert_u32(re_ctx, offset, re_ctx->nranges);
            break;
        }
        case DUK_RETOK_ATOM_END_GROUP: {
            if (expect_eof) {
                DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_UNEXPECTED_CLOSING_PAREN);
            }
            goto done;
        }
        case DUK_RETOK_EOF: {
            if (!expect_eof) {
                DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_UNEXPECTED_END_OF_PATTERN);
            }
            goto done;
        }
        default: {
            DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_UNEXPECTED_REGEXP_TOKEN);
        }
        }

        /* a complex (new) atom taints the result */
        if (new_atom_start_offset >= 0) {
            if (new_atom_char_length < 0) {
                res_charlen = -1;
            } else if (res_charlen >= 0) {
                /* only advance if not tainted */
                res_charlen += new_atom_char_length;
            }
        }

        /* record previous atom info in case next token is a quantifier */
        atom_start_offset = new_atom_start_offset;
        atom_char_length = new_atom_char_length;
        atom_start_captures = new_atom_start_captures;
    }

done:

    /* finish up pending jump and split for last alternative */
    if (unpatched_disjunction_jump >= 0) {
        duk_uint32_t offset;

        DUK_ASSERT(unpatched_disjunction_split >= 0);
        offset = unpatched_disjunction_jump;
        offset += duk__insert_jump_offset(re_ctx,
                                          offset,
                                          (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - offset));
        /* offset is now target of the pending split (right after jump) */
        duk__insert_jump_offset(re_ctx,
                                unpatched_disjunction_split,
                                offset - unpatched_disjunction_split);
    }

#if 0
    out_atom_info->end_captures = re_ctx->captures;
#endif
    out_atom_info->charlen = res_charlen;
    DUK_DDD(DUK_DDDPRINT("parse disjunction finished: charlen=%ld",
                         (long) out_atom_info->charlen));

    re_ctx->recursion_depth--;
}
DUK_INTERNAL void duk_regexp_compile(duk_hthread *thr) {
    duk_context *ctx = (duk_context *) thr;
    duk_re_compiler_ctx re_ctx;
    duk_lexer_point lex_point;
    duk_hstring *h_pattern;
    duk_hstring *h_flags;
    duk__re_disjunction_info ign_disj;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(ctx != NULL);

    /*
     *  Args validation
     */

    /* TypeError if fails */
    h_pattern = duk_require_hstring(ctx, -2);
    h_flags = duk_require_hstring(ctx, -1);

    /*
     *  Create normalized 'source' property (E5 Section 15.10.3).
     */

    /* [ ... pattern flags ] */

    duk__create_escaped_source(thr, -2);

    /* [ ... pattern flags escaped_source ] */

    /*
     *  Init compilation context
     */

    /* [ ... pattern flags escaped_source buffer ] */

    DUK_MEMZERO(&re_ctx, sizeof(re_ctx));
    DUK_LEXER_INITCTX(&re_ctx.lex);  /* duplicate zeroing, expect for (possible) NULL inits */
    re_ctx.thr = thr;
    re_ctx.lex.thr = thr;
    re_ctx.lex.input = DUK_HSTRING_GET_DATA(h_pattern);
    re_ctx.lex.input_length = DUK_HSTRING_GET_BYTELEN(h_pattern);
    re_ctx.lex.token_limit = DUK_RE_COMPILE_TOKEN_LIMIT;
    re_ctx.recursion_limit = DUK_USE_REGEXP_COMPILER_RECLIMIT;
    re_ctx.re_flags = duk__parse_regexp_flags(thr, h_flags);

    DUK_BW_INIT_PUSHBUF(thr, &re_ctx.bw, DUK__RE_INITIAL_BUFSIZE);

    DUK_DD(DUK_DDPRINT("regexp compiler ctx initialized, flags=0x%08lx, recursion_limit=%ld",
                       (unsigned long) re_ctx.re_flags, (long) re_ctx.recursion_limit));

    /*
     *  Init lexer
     */

    lex_point.offset = 0;  /* expensive init, just want to fill window */
    lex_point.line = 1;
    DUK_LEXER_SETPOINT(&re_ctx.lex, &lex_point);

    /*
     *  Compilation
     */

    DUK_DD(DUK_DDPRINT("starting regexp compilation"));

    duk__append_u32(&re_ctx, DUK_REOP_SAVE);
    duk__append_u32(&re_ctx, 0);
    duk__parse_disjunction(&re_ctx, 1 /*expect_eof*/, &ign_disj);
    duk__append_u32(&re_ctx, DUK_REOP_SAVE);
    duk__append_u32(&re_ctx, 1);
    duk__append_u32(&re_ctx, DUK_REOP_MATCH);

    /*
     *  Check for invalid backreferences; note that it is NOT an error
     *  to back-reference a capture group which has not yet been introduced
     *  in the pattern (as in /\1(foo)/); in fact, the backreference will
     *  always match!  It IS an error to back-reference a capture group
     *  which will never be introduced in the pattern.  Thus, we can check
     *  for such references only after parsing is complete.
     */

    if (re_ctx.highest_backref > re_ctx.captures) {
        DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_BACKREFS);
    }

    /*
     *  Emit compiled regexp header: flags, ncaptures
     *  (insertion order inverted on purpose)
     */

    duk__insert_u32(&re_ctx, 0, (re_ctx.captures + 1) * 2);
    duk__insert_u32(&re_ctx, 0, re_ctx.re_flags);

    /* [ ... pattern flags escaped_source buffer ] */

    DUK_BW_COMPACT(thr, &re_ctx.bw);
    duk_to_string(ctx, -1);  /* coerce to string */

    /* [ ... pattern flags escaped_source bytecode ] */

    /*
     *  Finalize stack
     */

    duk_remove(ctx, -4);     /* -> [ ... flags escaped_source bytecode ] */
    duk_remove(ctx, -3);     /* -> [ ... escaped_source bytecode ] */

    DUK_DD(DUK_DDPRINT("regexp compilation successful, bytecode: %!T, escaped source: %!T",
                       (duk_tval *) duk_get_tval(ctx, -1), (duk_tval *) duk_get_tval(ctx, -2)));
}
Example #29
0
DUK_LOCAL void duk__remove_matching_hstring_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, duk_hstring *h) {
#else
DUK_LOCAL void duk__remove_matching_hstring_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_hstring *h) {
#endif
	duk_uint32_t i;
	duk_uint32_t step;
	duk_uint32_t hash;
#if defined(DUK_USE_HEAPPTR16)
	duk_uint16_t null16 = heap->heapptr_null16;
	duk_uint16_t h16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h);
#endif

	DUK_ASSERT(size > 0);

	hash = DUK_HSTRING_GET_HASH(h);
	i = DUK__HASH_INITIAL(hash, size);
	step = DUK__HASH_PROBE_STEP(hash);
	for (;;) {
#if defined(DUK_USE_HEAPPTR16)
		duk_uint16_t e16 = entries16[i];
#else
		duk_hstring *e = entries[i];
#endif

#if defined(DUK_USE_HEAPPTR16)
		if (e16 == null16) {
#else
		if (!e) {
#endif
			DUK_UNREACHABLE();
			break;
		}
#if defined(DUK_USE_HEAPPTR16)
		if (e16 == h16) {
#else
		if (e == h) {
#endif
			/* st_used remains the same, DELETED is counted as used */
			DUK_DDD(DUK_DDDPRINT("free matching hit: %ld", (long) i));
#if defined(DUK_USE_HEAPPTR16)
			entries16[i] = heap->heapptr_deleted16;
#else
			entries[i] = DUK__DELETED_MARKER(heap);
#endif
			break;
		}

		DUK_DDD(DUK_DDDPRINT("free matching miss: %ld", (long) i));
		i = (i + step) % size;

		/* looping should never happen */
		DUK_ASSERT(i != DUK__HASH_INITIAL(hash, size));
	}
}

DUK_LOCAL duk_bool_t duk__resize_strtab_raw_probe(duk_heap *heap, duk_uint32_t new_size) {
#ifdef DUK_USE_MARK_AND_SWEEP
	duk_small_uint_t prev_mark_and_sweep_base_flags;
#endif
#ifdef DUK_USE_DEBUG
	duk_uint32_t old_used = heap->st_used;
#endif
	duk_uint32_t old_size = heap->st_size;
#if defined(DUK_USE_HEAPPTR16)
	duk_uint16_t *old_entries = heap->strtable16;
	duk_uint16_t *new_entries = NULL;
#else
	duk_hstring **old_entries = heap->strtable;
	duk_hstring **new_entries = NULL;
#endif
	duk_uint32_t new_used = 0;
	duk_uint32_t i;

#ifdef DUK_USE_DEBUG
	DUK_UNREF(old_used);  /* unused with some debug level combinations */
#endif

#ifdef DUK_USE_DDDPRINT
	DUK_DDD(DUK_DDDPRINT("attempt to resize stringtable: %ld entries, %ld bytes, %ld used, %ld%% load -> %ld entries, %ld bytes, %ld used, %ld%% load",
	                     (long) old_size, (long) (sizeof(duk_hstring *) * old_size), (long) old_used,
	                     (long) (((double) old_used) / ((double) old_size) * 100.0),
	                     (long) new_size, (long) (sizeof(duk_hstring *) * new_size), (long) duk__count_used_probe(heap),
	                     (long) (((double) duk__count_used_probe(heap)) / ((double) new_size) * 100.0)));
#endif

	DUK_ASSERT(new_size > (duk_uint32_t) duk__count_used_probe(heap));  /* required for rehash to succeed, equality not that useful */
	DUK_ASSERT(old_entries);
#ifdef DUK_USE_MARK_AND_SWEEP
	DUK_ASSERT((heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE) == 0);
#endif

	/*
	 *  The attempt to allocate may cause a GC.  Such a GC must not attempt to resize
	 *  the stringtable (though it can be swept); finalizer execution and object
	 *  compaction must also be postponed to avoid the pressure to add strings to the
	 *  string table.
	 */

#ifdef DUK_USE_MARK_AND_SWEEP
	prev_mark_and_sweep_base_flags = heap->mark_and_sweep_base_flags;
	heap->mark_and_sweep_base_flags |= \
	        DUK_MS_FLAG_NO_STRINGTABLE_RESIZE |  /* avoid recursive call here */
	        DUK_MS_FLAG_NO_FINALIZERS |          /* avoid pressure to add/remove strings */
	        DUK_MS_FLAG_NO_OBJECT_COMPACTION;    /* avoid array abandoning which interns strings */
#endif

#if defined(DUK_USE_HEAPPTR16)
	new_entries = (duk_uint16_t *) DUK_ALLOC(heap, sizeof(duk_uint16_t) * new_size);
#else
	new_entries = (duk_hstring **) DUK_ALLOC(heap, sizeof(duk_hstring *) * new_size);
#endif

#ifdef DUK_USE_MARK_AND_SWEEP
	heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags;
#endif

	if (!new_entries) {
		goto resize_error;
	}

#ifdef DUK_USE_EXPLICIT_NULL_INIT
	for (i = 0; i < new_size; i++) {
#if defined(DUK_USE_HEAPPTR16)
		new_entries[i] = heap->heapptr_null16;
#else
		new_entries[i] = NULL;
#endif
	}
#else
#if defined(DUK_USE_HEAPPTR16)
	/* Relies on NULL encoding to zero. */
	DUK_MEMZERO(new_entries, sizeof(duk_uint16_t) * new_size);
#else
	DUK_MEMZERO(new_entries, sizeof(duk_hstring *) * new_size);
#endif
#endif

	/* Because new_size > duk__count_used_probe(heap), guaranteed to work */
	for (i = 0; i < old_size; i++) {
		duk_hstring *e;

#if defined(DUK_USE_HEAPPTR16)
		e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, old_entries[i]);
#else
		e = old_entries[i];
#endif
		if (e == NULL || e == DUK__DELETED_MARKER(heap)) {
			continue;
		}
		/* checking for DUK__DELETED_MARKER is not necessary here, but helper does it now */
		duk__insert_hstring_probe(heap, new_entries, new_size, &new_used, e);
	}

#ifdef DUK_USE_DDPRINT
	DUK_DD(DUK_DDPRINT("resized stringtable: %ld entries, %ld bytes, %ld used, %ld%% load -> %ld entries, %ld bytes, %ld used, %ld%% load",
	                   (long) old_size, (long) (sizeof(duk_hstring *) * old_size), (long) old_used,
	                   (long) (((double) old_used) / ((double) old_size) * 100.0),
	                   (long) new_size, (long) (sizeof(duk_hstring *) * new_size), (long) new_used,
	                   (long) (((double) new_used) / ((double) new_size) * 100.0)));
#endif

#if defined(DUK_USE_HEAPPTR16)
	DUK_FREE(heap, heap->strtable16);
	heap->strtable16 = new_entries;
#else
	DUK_FREE(heap, heap->strtable);
	heap->strtable = new_entries;
#endif
	heap->st_size = new_size;
	heap->st_used = new_used;  /* may be less, since DELETED entries are NULLed by rehash */

	return 0;  /* OK */

 resize_error:
	DUK_FREE(heap, new_entries);
	return 1;  /* FAIL */
}

DUK_LOCAL duk_bool_t duk__resize_strtab_probe(duk_heap *heap) {
	duk_uint32_t new_size;
	duk_bool_t ret;

	new_size = (duk_uint32_t) duk__count_used_probe(heap);
	if (new_size >= 0x80000000UL) {
		new_size = DUK_STRTAB_HIGHEST_32BIT_PRIME;
	} else {
		new_size = duk_util_get_hash_prime(DUK_STRTAB_GROW_ST_SIZE(new_size));
		new_size = duk_util_get_hash_prime(new_size);
	}
	DUK_ASSERT(new_size > 0);

	/* rehash even if old and new sizes are the same to get rid of
	 * DELETED entries.
	*/

	ret = duk__resize_strtab_raw_probe(heap, new_size);

	return ret;
}
Example #30
0
DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap) {
	duk_strtab_entry *e;
	duk_small_uint_t i;
	duk_size_t j, n, used;
#if defined(DUK_USE_HEAPPTR16)
	duk_uint16_t *lst;
	duk_uint16_t null16 = heap->heapptr_null16;
#else
	duk_hstring **lst;
#endif

	DUK_ASSERT(heap != NULL);

	for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) {
		e = heap->strtable + i;

		if (e->listlen == 0) {
#if defined(DUK_USE_HEAPPTR16)
			DUK_DD(DUK_DDPRINT("[%03d] -> plain %d", (int) i, (int) (e->u.str16 != null16 ? 1 : 0)));
#else
			DUK_DD(DUK_DDPRINT("[%03d] -> plain %d", (int) i, (int) (e->u.str ? 1 : 0)));
#endif
		} else {
			used = 0;
#if defined(DUK_USE_HEAPPTR16)
			lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16);
#else
			lst = e->u.strlist;
#endif
			DUK_ASSERT(lst != NULL);
			for (j = 0, n = e->listlen; j < n; j++) {
#if defined(DUK_USE_HEAPPTR16)
				if (lst[j] != null16) {
#else
				if (lst[j] != NULL) {
#endif
					used++;
				}
			}
			DUK_DD(DUK_DDPRINT("[%03d] -> array %d/%d", (int) i, (int) used, (int) e->listlen));
		}
	}
}
#endif  /* DUK_USE_DEBUG */

#endif  /* DUK_USE_STRTAB_CHAIN */

/*
 *  String table algorithm: closed hashing with a probe sequence
 *
 *  This is the default algorithm and works fine for environments with
 *  minimal memory constraints.
 */

#if defined(DUK_USE_STRTAB_PROBE)

/* Count actually used (non-NULL, non-DELETED) entries. */
DUK_LOCAL duk_int_t duk__count_used_probe(duk_heap *heap) {
	duk_int_t res = 0;
	duk_uint_fast32_t i, n;
#if defined(DUK_USE_HEAPPTR16)
	duk_uint16_t null16 = heap->heapptr_null16;
	duk_uint16_t deleted16 = heap->heapptr_deleted16;
#endif

	n = (duk_uint_fast32_t) heap->st_size;
	for (i = 0; i < n; i++) {
#if defined(DUK_USE_HEAPPTR16)
		if (heap->strtable16[i] != null16 && heap->strtable16[i] != deleted16) {
#else
		if (heap->strtable[i] != NULL && heap->strtable[i] != DUK__DELETED_MARKER(heap)) {
#endif
			res++;
		}
	}
	return res;
}

#if defined(DUK_USE_HEAPPTR16)
DUK_LOCAL void duk__insert_hstring_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) {
#else
DUK_LOCAL void duk__insert_hstring_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) {
#endif
	duk_uint32_t i;
	duk_uint32_t step;
#if defined(DUK_USE_HEAPPTR16)
	duk_uint16_t null16 = heap->heapptr_null16;
	duk_uint16_t deleted16 = heap->heapptr_deleted16;
#endif

	DUK_ASSERT(size > 0);

	i = DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size);
	step = DUK__HASH_PROBE_STEP(DUK_HSTRING_GET_HASH(h));
	for (;;) {
#if defined(DUK_USE_HEAPPTR16)
		duk_uint16_t e16 = entries16[i];
#else
		duk_hstring *e = entries[i];
#endif

#if defined(DUK_USE_HEAPPTR16)
		/* XXX: could check for e16 == 0 because NULL is guaranteed to
		 * encode to zero.
		 */
		if (e16 == null16) {
#else
		if (e == NULL) {
#endif
			DUK_DDD(DUK_DDDPRINT("insert hit (null): %ld", (long) i));
#if defined(DUK_USE_HEAPPTR16)
			entries16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h);
#else
			entries[i] = h;
#endif
			(*p_used)++;
			break;
#if defined(DUK_USE_HEAPPTR16)
		} else if (e16 == deleted16) {
#else
		} else if (e == DUK__DELETED_MARKER(heap)) {
#endif
			/* st_used remains the same, DELETED is counted as used */
			DUK_DDD(DUK_DDDPRINT("insert hit (deleted): %ld", (long) i));
#if defined(DUK_USE_HEAPPTR16)
			entries16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h);
#else
			entries[i] = h;
#endif
			break;
		}
		DUK_DDD(DUK_DDDPRINT("insert miss: %ld", (long) i));
		i = (i + step) % size;

		/* looping should never happen */
		DUK_ASSERT(i != DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size));
	}
}

#if defined(DUK_USE_HEAPPTR16)
DUK_LOCAL duk_hstring *duk__find_matching_string_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) {
#else
DUK_LOCAL duk_hstring *duk__find_matching_string_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) {
#endif
	duk_uint32_t i;
	duk_uint32_t step;

	DUK_ASSERT(size > 0);

	i = DUK__HASH_INITIAL(strhash, size);
	step = DUK__HASH_PROBE_STEP(strhash);
	for (;;) {
		duk_hstring *e;
#if defined(DUK_USE_HEAPPTR16)
		e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, entries16[i]);
#else
		e = entries[i];
#endif

		if (!e) {
			return NULL;
		}
		if (e != DUK__DELETED_MARKER(heap) && DUK_HSTRING_GET_BYTELEN(e) == blen) {
			if (DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(e), (size_t) blen) == 0) {
				DUK_DDD(DUK_DDDPRINT("find matching hit: %ld (step %ld, size %ld)",
				                     (long) i, (long) step, (long) size));
				return e;
			}
		}
		DUK_DDD(DUK_DDDPRINT("find matching miss: %ld (step %ld, size %ld)",
		                     (long) i, (long) step, (long) size));
		i = (i + step) % size;

		/* looping should never happen */
		DUK_ASSERT(i != DUK__HASH_INITIAL(strhash, size));
	}
	DUK_UNREACHABLE();
}