DUK_LOCAL void duk__mark_finalizable(duk_heap *heap) {
	duk_hthread *thr;
	duk_heaphdr *hdr;
	duk_size_t count_finalizable = 0;

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

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

	hdr = heap->heap_allocated;
	while (hdr) {
		/* A finalizer is looked up from the object and up its prototype chain
		 * (which allows inherited finalizers).  A prototype loop must not cause
		 * an error to be thrown here; duk_hobject_hasprop_raw() will ignore a
		 * prototype loop silently and indicate that the property doesn't exist.
		 */

		if (!DUK_HEAPHDR_HAS_REACHABLE(hdr) &&
		    DUK_HEAPHDR_GET_TYPE(hdr) == DUK_HTYPE_OBJECT &&
		    !DUK_HEAPHDR_HAS_FINALIZED(hdr) &&
		    duk_hobject_hasprop_raw(thr, (duk_hobject *) hdr, DUK_HTHREAD_STRING_INT_FINALIZER(thr))) {

			/* heaphdr:
			 *  - is not reachable
			 *  - is an object
			 *  - is not a finalized object
			 *  - has a finalizer
			 */

			DUK_DD(DUK_DDPRINT("unreachable heap object will be "
			                   "finalized -> mark as finalizable "
			                   "and treat as a reachability root: %p",
			                   (void *) hdr));
			DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(hdr));
			DUK_HEAPHDR_SET_FINALIZABLE(hdr);
			count_finalizable ++;
		}

		hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
	}

	if (count_finalizable == 0) {
		return;
	}

	DUK_DD(DUK_DDPRINT("marked %ld heap objects as finalizable, now mark them reachable",
	                   (long) count_finalizable));

	hdr = heap->heap_allocated;
	while (hdr) {
		if (DUK_HEAPHDR_HAS_FINALIZABLE(hdr)) {
			duk__mark_heaphdr(heap, hdr);
		}

		hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
	}

	/* Caller will finish the marking process if we hit a recursion limit. */
}
static void duk__mark_finalizable(duk_heap *heap) {
	duk_hthread *thr;
	duk_heaphdr *hdr;
	int count_finalizable = 0;

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

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

	hdr = heap->heap_allocated;
	while (hdr) {
		/* A finalizer is looked up from the object and up its prototype chain
		 * (which allows inherited finalizers).
		 */
		if (!DUK_HEAPHDR_HAS_REACHABLE(hdr) &&
		    DUK_HEAPHDR_GET_TYPE(hdr) == DUK_HTYPE_OBJECT &&
		    !DUK_HEAPHDR_HAS_FINALIZED(hdr) &&
		    duk_hobject_hasprop_raw(thr, (duk_hobject *) hdr, DUK_HTHREAD_STRING_INT_FINALIZER(thr))) {

			/* heaphdr:
			 *  - is not reachable
			 *  - is an object
			 *  - is not a finalized object
			 *  - has a finalizer
			 */

			DUK_DD(DUK_DDPRINT("unreachable heap object will be finalized -> mark as finalizable and treat as a reachability root: %p", hdr));
			DUK_HEAPHDR_SET_FINALIZABLE(hdr);
			count_finalizable ++;
		}

		hdr = DUK_HEAPHDR_GET_NEXT(hdr);
	}

	if (count_finalizable == 0) {
		return;
	}

	DUK_DD(DUK_DDPRINT("marked %d heap objects as finalizable, now mark them reachable", count_finalizable));

	hdr = heap->heap_allocated;
	while (hdr) {
		if (DUK_HEAPHDR_HAS_FINALIZABLE(hdr)) {
			duk__mark_heaphdr(heap, hdr);
		}

		hdr = DUK_HEAPHDR_GET_NEXT(hdr);
	}

	/* Caller will finish the marking process if we hit a recursion limit. */
}
Exemple #3
0
DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) {
	duk_hthread *thr;
	duk_heaphdr *curr;
#ifdef DUK_USE_DEBUG
	duk_size_t count_obj = 0;
#endif

	DUK_ASSERT(heap != NULL);
	DUK_ASSERT(heap->heap_thread != NULL);
#ifdef DUK_USE_REFERENCE_COUNTING
	DUK_ASSERT(heap->refzero_list == NULL);  /* refzero not running -> must be empty */
#endif
#ifdef DUK_USE_MARK_AND_SWEEP
	DUK_ASSERT(heap->finalize_list == NULL);  /* mark-and-sweep not running -> must be empty */
#endif

	/* XXX: here again finalizer thread is the heap_thread which needs
	 * to be coordinated with finalizer thread fixes.
	 */
	thr = heap->heap_thread;
	DUK_ASSERT(thr != NULL);

	curr = heap->heap_allocated;
	while (curr) {
		if (DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT) {
			/* Only objects in heap_allocated may have finalizers.  Check that
			 * the object itself has a _Finalizer property so that we don't
			 * execute finalizers for e.g. Proxy objects.
			 */
			DUK_ASSERT(thr != NULL);
			DUK_ASSERT(curr != NULL);

			if (duk_hobject_hasprop_raw(thr, (duk_hobject *) curr, DUK_HTHREAD_STRING_INT_FINALIZER(thr))) {
				duk_hobject_run_finalizer(thr, (duk_hobject *) curr);
			}
#ifdef DUK_USE_DEBUG
			count_obj++;
#endif
		}
		curr = DUK_HEAPHDR_GET_NEXT(curr);
	}

	/* Note: count includes all objects, not only those with an actual finalizer. */
#ifdef DUK_USE_DEBUG
	DUK_D(DUK_DPRINT("checked %ld objects for finalizers before freeing heap", (long) count_obj));
#endif
}
DUK_LOCAL void duk__err_augment_builtin_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_small_int_t noblame_fileline, duk_hobject *obj) {
	duk_context *ctx = (duk_context *) thr;
#if defined(DUK_USE_ASSERTIONS)
	duk_int_t entry_top;
#endif

#if defined(DUK_USE_ASSERTIONS)
	entry_top = duk_get_top(ctx);
#endif
	DUK_ASSERT(obj != NULL);

	DUK_UNREF(obj);  /* unreferenced w/o tracebacks */
	DUK_UNREF(ctx);  /* unreferenced w/o asserts */

	duk__add_compiler_error_line(thr);

#if defined(DUK_USE_TRACEBACKS)
	/* If tracebacks are enabled, the '_Tracedata' property is the only
	 * thing we need: 'fileName' and 'lineNumber' are virtual properties
	 * which use '_Tracedata'.
	 */
	if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_INT_TRACEDATA(thr))) {
		DUK_DDD(DUK_DDDPRINT("error value already has a '_Tracedata' property, not modifying it"));
	} else {
		duk__add_traceback(thr, thr_callstack, c_filename, c_line, noblame_fileline);
	}
#else
	/* Without tracebacks the concrete .fileName and .lineNumber need
	 * to be added directly.
	 */
	duk__add_fileline(thr, thr_callstack, c_filename, c_line, noblame_fileline);
#endif

#if defined(DUK_USE_ASSERTIONS)
	DUK_ASSERT(duk_get_top(ctx) == entry_top);
#endif
}
Exemple #5
0
/*
 *  Returns non-zero if a key and/or value was enumerated, and:
 *
 *   [enum] -> [key]        (get_value == 0)
 *   [enum] -> [key value]  (get_value == 1)
 *
 *  Returns zero without pushing anything on the stack otherwise.
 */
duk_bool_t duk_hobject_enumerator_next(duk_context *ctx, duk_bool_t get_value) {
	duk_hthread *thr = (duk_hthread *) ctx;
	duk_hobject *e;
	duk_hobject *enum_target;
	duk_hstring *res = NULL;
	duk_uint_fast32_t idx;
	duk_bool_t check_existence;

	DUK_ASSERT(ctx != NULL);

	/* [... enum] */

	e = duk_require_hobject(ctx, -1);

	/* XXX use get tval ptr, more efficient */
	duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_NEXT);
	idx = (duk_uint_fast32_t) duk_require_uint(ctx, -1);
	duk_pop(ctx);
	DUK_DDD(DUK_DDDPRINT("enumeration: index is: %ld", (long) idx));

	/* Enumeration keys are checked against the enumeration target (to see
	 * that they still exist).  In the proxy enumeration case _target will
	 * be the proxy, and checking key existence against the proxy is not
	 * required (or sensible, as the keys may be fully virtual).
	 */
	duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TARGET);
	enum_target = duk_require_hobject(ctx, -1);
	DUK_ASSERT(enum_target != NULL);
#if defined(DUK_USE_ES6_PROXY)
	check_existence = (!DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(enum_target));
#else
	check_existence = 1;
#endif
	duk_pop(ctx);  /* still reachable */

	DUK_DDD(DUK_DDDPRINT("getting next enum value, enum_target=%!iO, enumerator=%!iT",
	                     (duk_heaphdr *) enum_target, (duk_tval *) duk_get_tval(ctx, -1)));

	/* no array part */
	for (;;) {
		duk_hstring *k;

		if (idx >= e->e_next) {
			DUK_DDD(DUK_DDDPRINT("enumeration: ran out of elements"));
			break;
		}

		/* we know these because enum objects are internally created */
		k = DUK_HOBJECT_E_GET_KEY(e, idx);
		DUK_ASSERT(k != NULL);
		DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(e, idx));
		DUK_ASSERT(!DUK_TVAL_IS_UNDEFINED_UNUSED(&DUK_HOBJECT_E_GET_VALUE(e, idx).v));

		idx++;

		/* recheck that the property still exists */
		if (check_existence && !duk_hobject_hasprop_raw(thr, enum_target, k)) {
			DUK_DDD(DUK_DDDPRINT("property deleted during enumeration, skip"));
			continue;
		}

		DUK_DDD(DUK_DDDPRINT("enumeration: found element, key: %!O", (duk_heaphdr *) k));
		res = k;
		break;
	}

	DUK_DDD(DUK_DDDPRINT("enumeration: updating next index to %ld", (long) idx));

	duk_push_number(ctx, (double) idx);
	duk_put_prop_stridx(ctx, -2, DUK_STRIDX_INT_NEXT);

	/* [... enum] */

	if (res) {
		duk_push_hstring(ctx, res);
		if (get_value) {
			duk_push_hobject(ctx, enum_target);
			duk_dup(ctx, -2);      /* -> [... enum key enum_target key] */
			duk_get_prop(ctx, -2); /* -> [... enum key enum_target val] */
			duk_remove(ctx, -2);   /* -> [... enum key val] */
			duk_remove(ctx, -3);   /* -> [... key val] */
		} else {
			duk_remove(ctx, -2);   /* -> [... key] */
		}
		return 1;
	} else {
		duk_pop(ctx);  /* -> [...] */
		return 0;
	}
}
static void duk__err_augment_builtin_throw(duk_hthread *thr, duk_hthread *thr_callstack, const char *filename, duk_int_t line, duk_small_int_t noblame_fileline, duk_hobject *obj) {
	duk_context *ctx = (duk_context *) thr;
#ifdef DUK_USE_ASSERTIONS
	duk_int_t entry_top;
#endif

#ifdef DUK_USE_ASSERTIONS
	entry_top = duk_get_top(ctx);
#endif
	DUK_ASSERT(obj != NULL);

	DUK_UNREF(obj);  /* unreferenced w/o tracebacks */
	DUK_UNREF(ctx);  /* unreferenced w/ tracebacks */

#ifdef DUK_USE_TRACEBACKS
	/*
	 *  If tracebacks are enabled, the 'tracedata' property is the only
	 *  thing we need: 'fileName' and 'lineNumber' are virtual properties
	 *  which use 'tracedata'.
	 */

	if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_TRACEDATA(thr))) {
		DUK_DDD(DUK_DDDPRINT("error value already has a 'tracedata' property, not modifying it"));
	} else {
		duk__add_traceback(thr, thr_callstack, filename, line, noblame_fileline);
	}
#else
	/*
	 *  If tracebacks are disabled, 'fileName' and 'lineNumber' are added
	 *  as plain own properties.  Since Error.prototype has accessors of
	 *  the same name, we need to define own properties directly (cannot
	 *  just use e.g. duk_put_prop_stridx).  Existing properties are not
	 *  overwritten in case they already exist.
	 */

	if (filename && !noblame_fileline) {
		/* XXX: file/line is disabled in minimal builds, so disable this too
		 * when appropriate.
		 */
		duk_push_string(ctx, filename);
		duk_def_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
		duk_push_int(ctx, line);
		duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
	} else if (thr_callstack->callstack_top > 0) {
		duk_activation *act;
		duk_hobject *func;

		act = thr_callstack->callstack + thr_callstack->callstack_top - 1;
		DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size);
		func = act->func;
		if (func) {
			duk_uint32_t pc;
			duk_uint32_t line;

			/* PC points to next instruction, find offending PC.  Note that
			 * PC == 0 for native code.
			 */
			pc = act->pc;
			if (pc > 0) {
				pc--;
			}
			DUK_ASSERT_DISABLE(pc >= 0);  /* unsigned */
			DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32);  /* assume PC is at most 32 bits and non-negative */
			act = NULL;  /* invalidated by pushes, so get out of the way */

			duk_push_hobject(ctx, func);

			/* [ ... error func ] */

			duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME);
			duk_def_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
			if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
#if 0
				duk_push_number(ctx, pc);
				duk_def_prop_stridx(ctx, -3, DUK_STRIDX_PC, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAGS_NO_OVERWRITE);
#endif
				line = duk_hobject_pc2line_query(ctx, -1, (duk_uint_fast32_t) pc);
				if (line > 0) {
					duk_push_u32(ctx, (duk_uint32_t) line); /* -> [ ... error func line ] */
					duk_def_prop_stridx(ctx, -3, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
				}
			} else {
				/* Native function, no relevant lineNumber. */
			}

			duk_pop(ctx);
		}
	}
#endif  /* DUK_USE_TRACEBACKS */

#ifdef DUK_USE_ASSERTIONS
	DUK_ASSERT(duk_get_top(ctx) == entry_top);
#endif
}
static void duk__refzero_free_pending(duk_hthread *thr) {
	duk_heaphdr *h1, *h2;
	duk_heap *heap;
	duk_int_t count = 0;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(thr->heap != NULL);
	heap = thr->heap;
	DUK_ASSERT(heap != NULL);

	/*
	 *  Detect recursive invocation
	 */

	if (DUK_HEAP_HAS_REFZERO_FREE_RUNNING(heap)) {
		DUK_DDD(DUK_DDDPRINT("refzero free running, skip run"));
		return;
	}

	/*
	 *  Churn refzero_list until empty
	 */

	DUK_HEAP_SET_REFZERO_FREE_RUNNING(heap);
	while (heap->refzero_list) {
		duk_hobject *obj;
		duk_bool_t rescued = 0;

		/*
		 *  Pick an object from the head (don't remove yet).
		 */

		h1 = heap->refzero_list;
		obj = (duk_hobject *) h1;
		DUK_DD(DUK_DDPRINT("refzero processing %p: %!O", (void *) h1, (duk_heaphdr *) h1));
		DUK_ASSERT(DUK_HEAPHDR_GET_PREV(h1) == NULL);
		DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(h1) == DUK_HTYPE_OBJECT);  /* currently, always the case */

		/*
		 *  Finalizer check.
		 *
		 *  Note: running a finalizer may have arbitrary side effects, e.g.
		 *  queue more objects on refzero_list (tail), or even trigger a
		 *  mark-and-sweep.
		 *
		 *  Note: quick reject check should match vast majority of
		 *  objects and must be safe (not throw any errors, ever).
		 */

		/* XXX: If object has FINALIZED, it was finalized by mark-and-sweep on
		 * its previous run.  Any point in running finalizer again here?  If
		 * finalization semantics is changed so that finalizer is only run once,
		 * checking for FINALIZED would happen here.
		 */

		/* A finalizer is looked up from the object and up its prototype chain
		 * (which allows inherited finalizers).
		 */
		if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_INT_FINALIZER(thr))) {
			DUK_DDD(DUK_DDDPRINT("object has a finalizer, run it"));

			DUK_ASSERT(h1->h_refcount == 0);
			h1->h_refcount++;  /* bump refcount to prevent refzero during finalizer processing */

			duk_hobject_run_finalizer(thr, obj);  /* must never longjmp */

			h1->h_refcount--;  /* remove artificial bump */
			DUK_ASSERT_DISABLE(h1->h_refcount >= 0);  /* refcount is unsigned, so always true */

			if (h1->h_refcount != 0) {
				DUK_DDD(DUK_DDDPRINT("-> object refcount after finalization non-zero, object will be rescued"));
				rescued = 1;
			} else {
				DUK_DDD(DUK_DDDPRINT("-> object refcount still zero after finalization, object will be freed"));
			}
		}

  		/* Refzero head is still the same.  This is the case even if finalizer
		 * inserted more refzero objects; they are inserted to the tail.
		 */
		DUK_ASSERT(h1 == heap->refzero_list);

		/*
		 *  Remove the object from the refzero list.  This cannot be done
		 *  before a possible finalizer has been executed; the finalizer
		 *  may trigger a mark-and-sweep, and mark-and-sweep must be able
		 *  to traverse a complete refzero_list.
		 */

		h2 = DUK_HEAPHDR_GET_NEXT(h1);
		if (h2) {
			DUK_HEAPHDR_SET_PREV(h2, NULL);  /* not strictly necessary */
			heap->refzero_list = h2;
		} else {
			heap->refzero_list = NULL;
			heap->refzero_list_tail = NULL;
		}

		/*
		 *  Rescue or free.
		 */

		if (rescued) {
			/* yes -> move back to heap allocated */
			DUK_DD(DUK_DDPRINT("object rescued during refcount finalization: %p", (void *) h1));
			DUK_HEAPHDR_SET_PREV(h1, NULL);
			DUK_HEAPHDR_SET_NEXT(h1, heap->heap_allocated);
			heap->heap_allocated = h1;
		} else {
			/* no -> decref members, then free */
			duk__refcount_finalize_hobject(thr, obj);
			duk_heap_free_heaphdr_raw(heap, h1);
		}

		count++;
	}
	DUK_HEAP_CLEAR_REFZERO_FREE_RUNNING(heap);

	DUK_DDD(DUK_DDDPRINT("refzero processed %ld objects", (long) count));

	/*
	 *  Once the whole refzero cascade has been freed, check for
	 *  a voluntary mark-and-sweep.
	 */

#if defined(DUK_USE_MARK_AND_SWEEP) && defined(DUK_USE_VOLUNTARY_GC)
	/* 'count' is more or less comparable to normal trigger counter update
	 * which happens in memory block (re)allocation.
	 */
	heap->mark_and_sweep_trigger_counter -= count;
	if (heap->mark_and_sweep_trigger_counter <= 0) {
		duk_bool_t rc;
		duk_small_uint_t flags = 0;  /* not emergency */
		DUK_D(DUK_DPRINT("refcount triggering mark-and-sweep"));
		rc = duk_heap_mark_and_sweep(heap, flags);
		DUK_UNREF(rc);
		DUK_D(DUK_DPRINT("refcount triggered mark-and-sweep => rc %ld", (long) rc));
	}
#endif  /* DUK_USE_MARK_AND_SWEEP && DUK_USE_VOLUNTARY_GC */
}
Exemple #8
0
void duk_err_augment_error(duk_hthread *thr, duk_hthread *thr_callstack, int err_index, const char *filename, int line, int noblame_fileline) {
	duk_context *ctx = (duk_context *) thr;
	duk_hobject *obj;

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

	err_index = duk_require_normalize_index(ctx, err_index);

	/*
	 *  Criteria for augmenting:
	 *
	 *   - augmentation enabled in build (naturally)
	 *   - error value is an extensible object
	 *   - error value internal prototype chain contains the built-in
	 *     Error prototype object (i.e. 'val instanceof Error')
	 */

	obj = duk_require_hobject(ctx, err_index);
	if (!obj) {	
		DUK_DDDPRINT("error value not an object, not augmented");
		return;
	}
	if (!DUK_HOBJECT_HAS_EXTENSIBLE(obj)) {
		DUK_DDDPRINT("error value not extensible, not augmented");
		return;
	}
	if (!duk_hobject_prototype_chain_contains(thr, obj, thr->builtins[DUK_BIDX_ERROR_PROTOTYPE])) {
		DUK_DDDPRINT("error value not inherited from Error, not augmented");
		return;
	}

	/* Yes, augment error. */

#ifdef DUK_USE_TRACEBACKS
	/*
	 *  If tracebacks are enabled, the 'tracedata' property is the only
	 *  thing we need: 'fileName' and 'lineNumber' are virtual properties
	 *  which use 'tracedata'.
	 */

	if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_TRACEDATA(thr))) {
		DUK_DDDPRINT("error value already has a 'traceback' property, not modifying it");
	} else {
		add_traceback(thr, thr_callstack, obj, err_index, filename, line, noblame_fileline);
	}
#else
	/*
	 *  If tracebacks are disabled, 'fileName' and 'lineNumber' are added
	 *  as plain own properties.  Since Error.prototype has accessors of
	 *  the same name, we need to define own properties directly (cannot
	 *  just use e.g. duk_put_prop_stridx).  Existing properties are not
	 *  overwritten in case they already exist.
	 */

	if (filename && !noblame_fileline) {
		/* FIXME: file/line is disabled in minimal builds, so disable this too
		 * when appropriate.
		 */
		duk_push_string(ctx, filename);
		duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
		duk_push_int(ctx, line);
		duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
	} else if (thr_callstack->callstack_top > 0) {
		duk_activation *act;
		duk_hobject *func;
		duk_hbuffer *pc2line;

		act = thr_callstack->callstack + thr_callstack->callstack_top - 1;
		DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size);
		func = act->func;
		if (func) {
			int pc;
			duk_uint32_t line;

			/* PC points to next instruction, find offending PC.  Note that
			 * PC == 0 for native code.
			 */
			pc = act->pc;
			if (pc > 0) {
				pc--;
			}
			DUK_ASSERT(pc >= 0 && (double) pc < DUK_DOUBLE_2TO32);  /* assume PC is at most 32 bits and non-negative */
			act = NULL;  /* invalidated by pushes, so get out of the way */

			duk_push_hobject(ctx, func);

			duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME);
			duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
			if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
#if 0
				duk_push_number(ctx, pc);
				duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_PC, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAGS_NO_OVERWRITE);
#endif

				duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_PC2LINE);
				if (duk_is_buffer(ctx, -1)) {
					pc2line = duk_get_hbuffer(ctx, -1);
					DUK_ASSERT(pc2line != NULL);
					DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC(pc2line));
					line = duk_hobject_pc2line_query((duk_hbuffer_fixed *) pc2line, (duk_uint_fast32_t) pc);
					duk_push_number(ctx, (double) line);  /* FIXME: u32 */
					duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
				}
				duk_pop(ctx);
			} else {
				/* Native function, no relevant lineNumber. */
			}

			duk_pop(ctx);
		}
	}
#endif  /* DUK_USE_TRACEBACKS */
}
Exemple #9
0
DUK_LOCAL void duk__free_stringtable(duk_heap *heap) {
	duk_uint_fast32_t i;

	/* strings are only tracked by stringtable */
#if defined(DUK_USE_HEAPPTR16)
	if (heap->strtable16) {
#else
	if (heap->strtable) {
#endif
		for (i = 0; i < (duk_uint_fast32_t) heap->st_size; i++) {
			duk_hstring *e;
#if defined(DUK_USE_HEAPPTR16)
			e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->strtable16[i]);
#else
			e = heap->strtable[i];
#endif
			if (e == NULL || e == DUK_STRTAB_DELETED_MARKER(heap)) {
				continue;
			}
			DUK_ASSERT(e != NULL);

			/* strings may have inner refs (extdata) in some cases */
			duk_free_hstring_inner(heap, (duk_hstring *) e);

			DUK_DDD(DUK_DDDPRINT("FINALFREE (string): %!iO",
			                     (duk_heaphdr *) e));
			DUK_FREE(heap, e);
#if 0  /* not strictly necessary */
			heap->strtable[i] = NULL;
#endif
		}
#if defined(DUK_USE_HEAPPTR16)
		DUK_FREE(heap, heap->strtable16);
#else
		DUK_FREE(heap, heap->strtable);
#endif
#if 0  /* not strictly necessary */
		heap->strtable = NULL;
#endif
	}
}

DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) {
	duk_hthread *thr;
	duk_heaphdr *curr;
#ifdef DUK_USE_DEBUG
	duk_size_t count_obj = 0;
#endif

	DUK_ASSERT(heap != NULL);
	DUK_ASSERT(heap->heap_thread != NULL);
#ifdef DUK_USE_REFERENCE_COUNTING
	DUK_ASSERT(heap->refzero_list == NULL);  /* refzero not running -> must be empty */
#endif
#ifdef DUK_USE_MARK_AND_SWEEP
	DUK_ASSERT(heap->finalize_list == NULL);  /* mark-and-sweep not running -> must be empty */
#endif

	/* XXX: here again finalizer thread is the heap_thread which needs
	 * to be coordinated with finalizer thread fixes.
	 */
	thr = heap->heap_thread;
	DUK_ASSERT(thr != NULL);

	curr = heap->heap_allocated;
	while (curr) {
		if (DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT) {
			/* Only objects in heap_allocated may have finalizers.  Check that
			 * the object itself has a _Finalizer property so that we don't
			 * execute finalizers for e.g. Proxy objects.
			 */
			DUK_ASSERT(thr != NULL);
			DUK_ASSERT(curr != NULL);

			if (duk_hobject_hasprop_raw(thr, (duk_hobject *) curr, DUK_HTHREAD_STRING_INT_FINALIZER(thr))) {
				duk_hobject_run_finalizer(thr, (duk_hobject *) curr);
			}
#ifdef DUK_USE_DEBUG
			count_obj++;
#endif
		}
		curr = DUK_HEAPHDR_GET_NEXT(curr);
	}

	/* Note: count includes all objects, not only those with an actual finalizer. */
#ifdef DUK_USE_DEBUG
	DUK_D(DUK_DPRINT("checked %ld objects for finalizers before freeing heap", (long) count_obj));
#endif
}
/*
 *  Returns non-zero if a key and/or value was enumerated, and:
 *
 *   [enum] -> [key]        (get_value == 0)
 *   [enum] -> [key value]  (get_value == 1)
 *
 *  Returns zero without pushing anything on the stack otherwise.
 */
int duk_hobject_enumerator_next(duk_context *ctx, int get_value) {
	duk_hthread *thr = (duk_hthread *) ctx;
	duk_hobject *e;
	duk_hobject *target;
	duk_hstring *res = NULL;
	duk_uint32_t idx;

	DUK_ASSERT(ctx != NULL);

	/* [... enum] */

	e = duk_require_hobject(ctx, -1);

	/* FIXME: use get tval ptr, more efficient */
	duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_NEXT);
	idx = (duk_uint32_t) duk_require_number(ctx, -1);
	duk_pop(ctx);
	DUK_DDDPRINT("enumeration: index is: %d", idx);

	duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TARGET);
	target = duk_require_hobject(ctx, -1);
	DUK_ASSERT(target != NULL);
	duk_pop(ctx);  /* still reachable */

	DUK_DDDPRINT("getting next enum value, target=%!iO, enumerator=%!iT",
	             target, duk_get_tval(ctx, -1));

	/* no array part */
	for (;;) {
		duk_hstring *k;

		if (idx >= e->e_used) {
			DUK_DDDPRINT("enumeration: ran out of elements");
			break;
		}

		/* we know these because enum objects are internally created */
		k = DUK_HOBJECT_E_GET_KEY(e, idx);
		DUK_ASSERT(k != NULL);
		DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(e, idx));
		DUK_ASSERT(!DUK_TVAL_IS_UNDEFINED_UNUSED(&DUK_HOBJECT_E_GET_VALUE(e, idx).v));

		idx++;

		/* recheck that the property still exists */
		if (!duk_hobject_hasprop_raw(thr, target, k)) {
			DUK_DDDPRINT("property deleted during enumeration, skip");
			continue;
		}

		DUK_DDDPRINT("enumeration: found element, key: %!O", k);
		res = k;
		break;
	}

	DUK_DDDPRINT("enumeration: updating next index to %d", idx);

	duk_push_number(ctx, (double) idx);
	duk_put_prop_stridx(ctx, -2, DUK_STRIDX_INT_NEXT);

	/* [... enum] */

	if (res) {
		duk_push_hstring(ctx, res);
		if (get_value) {
			duk_push_hobject(ctx, target);
			duk_dup(ctx, -2);      /* -> [... enum key target key] */
			duk_get_prop(ctx, -2); /* -> [... enum key target val] */
			duk_remove(ctx, -2);   /* -> [... enum key val] */
			duk_remove(ctx, -3);   /* -> [... key val] */
		} else {
			duk_remove(ctx, -2);   /* -> [... key] */
		}
		return 1;
	} else {
		duk_pop(ctx);  /* -> [...] */
		return 0;
	}
}
DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) {
	duk_hthread *thr;
	duk_heaphdr *curr;
	duk_uint_t round_no;
	duk_size_t count_all;
	duk_size_t count_finalized;
	duk_size_t curr_limit;

	DUK_ASSERT(heap != NULL);
	DUK_ASSERT(heap->heap_thread != NULL);

#if defined(DUK_USE_REFERENCE_COUNTING)
	DUK_ASSERT(heap->refzero_list == NULL);  /* refzero not running -> must be empty */
#endif
#if defined(DUK_USE_MARK_AND_SWEEP)
	DUK_ASSERT(heap->finalize_list == NULL);  /* mark-and-sweep not running -> must be empty */
#endif

	/* XXX: here again finalizer thread is the heap_thread which needs
	 * to be coordinated with finalizer thread fixes.
	 */
	thr = heap->heap_thread;
	DUK_ASSERT(thr != NULL);

	/* Prevent mark-and-sweep for the pending finalizers, also prevents
	 * refzero handling from moving objects away from the heap_allocated
	 * list.  (The flag meaning is slightly abused here.)
	 */
	DUK_ASSERT(!DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap));
	DUK_HEAP_SET_MARKANDSWEEP_RUNNING(heap);

	curr_limit = 0;  /* suppress warning, not used */
	for (round_no = 0; ; round_no++) {
		curr = heap->heap_allocated;
		count_all = 0;
		count_finalized = 0;
		while (curr) {
			count_all++;
			if (DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT) {
				/* Only objects in heap_allocated may have finalizers.  Check that
				 * the object itself has a _Finalizer property (own or inherited)
				 * so that we don't execute finalizers for e.g. Proxy objects.
				 */
				DUK_ASSERT(thr != NULL);
				DUK_ASSERT(curr != NULL);

				if (duk_hobject_hasprop_raw(thr, (duk_hobject *) curr, DUK_HTHREAD_STRING_INT_FINALIZER(thr))) {
					if (!DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) curr)) {
						DUK_ASSERT(DUK_HEAP_HAS_FINALIZER_NORESCUE(heap));  /* maps to finalizer 2nd argument */
						duk_hobject_run_finalizer(thr, (duk_hobject *) curr);
						count_finalized++;
					}
				}
			}
			curr = DUK_HEAPHDR_GET_NEXT(heap, curr);
		}

		/* Each round of finalizer execution may spawn new finalizable objects
		 * which is normal behavior for some applications.  Allow multiple
		 * rounds of finalization, but use a shrinking limit based on the
		 * first round to detect the case where a runaway finalizer creates
		 * an unbounded amount of new finalizable objects.  Finalizer rescue
		 * is not supported: the semantics are unclear because most of the
		 * objects being finalized here are already reachable.  The finalizer
		 * is given a boolean to indicate that rescue is not possible.
		 *
		 * See discussion in: https://github.com/svaarala/duktape/pull/473
		 */

		if (round_no == 0) {
			/* Cannot wrap: each object is at least 8 bytes so count is
			 * at most 1/8 of that.
			 */
			curr_limit = count_all * 2;
		} else {
			curr_limit = (curr_limit * 3) / 4;   /* Decrease by 25% every round */
		}
		DUK_D(DUK_DPRINT("finalizer round %ld complete, %ld objects, tried to execute %ld finalizers, current limit is %ld",
		                 (long) round_no, (long) count_all, (long) count_finalized, (long) curr_limit));

		if (count_finalized == 0) {
			DUK_D(DUK_DPRINT("no more finalizable objects, forced finalization finished"));
			break;
		}
		if (count_finalized >= curr_limit) {
			DUK_D(DUK_DPRINT("finalizer count above limit, potentially runaway finalizer; skip remaining finalizers"));
			break;
		}
	}

	DUK_ASSERT(DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap));
	DUK_HEAP_CLEAR_MARKANDSWEEP_RUNNING(heap);
}