Skip to content

Commit

Permalink
Some testing of on-demand .prototype object
Browse files Browse the repository at this point in the history
  • Loading branch information
svaarala committed Apr 14, 2018
1 parent f88e04b commit 68b873b
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src-input/duk_api_stack.c
Original file line number Diff line number Diff line change
Expand Up @@ -5138,6 +5138,10 @@ DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_hthread *thr, duk_c_function fu

DUK_ASSERT_BIDX_VALID(proto_bidx);
DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[proto_bidx]);

if (flags & DUK_HOBJECT_FLAG_CONSTRUCTABLE) {
DUK_STATS_INC(thr->heap, stats_object_proto_potential);
}
return ret;

api_error:
Expand Down
2 changes: 2 additions & 0 deletions src-input/duk_heap.h
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,8 @@ struct duk_heap {
duk_int_t stats_strtab_litcache_pin;
duk_int_t stats_object_realloc_props;
duk_int_t stats_object_abandon_array;
duk_int_t stats_object_proto_potential;
duk_int_t stats_object_proto_create;
duk_int_t stats_getownpropdesc_count;
duk_int_t stats_getownpropdesc_hit;
duk_int_t stats_getownpropdesc_miss;
Expand Down
6 changes: 4 additions & 2 deletions src-input/duk_heap_markandsweep.c
Original file line number Diff line number Diff line change
Expand Up @@ -1125,8 +1125,10 @@ DUK_LOCAL void duk__dump_stats(duk_heap *heap) {
(long) heap->stats_strtab_resize_check, (long) heap->stats_strtab_resize_grow,
(long) heap->stats_strtab_resize_shrink, (long) heap->stats_strtab_litcache_hit,
(long) heap->stats_strtab_litcache_miss, (long) heap->stats_strtab_litcache_pin));
DUK_D(DUK_DPRINT("stats object: realloc_props=%ld, abandon_array=%ld",
(long) heap->stats_object_realloc_props, (long) heap->stats_object_abandon_array));
DUK_D(DUK_DPRINT("stats object: realloc_props=%ld, abandon_array=%ld, "
"proto_potential=%ld, proto_create=%ld",
(long) heap->stats_object_realloc_props, (long) heap->stats_object_abandon_array,
(long) heap->stats_object_proto_potential, (long) heap->stats_object_proto_create));
DUK_D(DUK_DPRINT("stats getownpropdesc: count=%ld, hit=%ld, miss=%ld",
(long) heap->stats_getownpropdesc_count, (long) heap->stats_getownpropdesc_hit,
(long) heap->stats_getownpropdesc_miss));
Expand Down
84 changes: 84 additions & 0 deletions src-input/duk_hobject_props.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,75 @@ DUK_LOCAL duk_bool_t duk__abandon_array_slow_check_required(duk_uint32_t arr_idx
return (arr_idx > DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT * ((old_size + 7) >> 3));
}

/*
* On-demand .prototype for functions
*
* The automatic .prototype property of function objects is writable, but
* not enumerable or configurable. We create the property on-demand when
* its presence matters:
*
* - On a direct property access with the property key "prototype".
* - Object.defineProperty().
* - Object.getOwnPropertyKeys() or other enumeration which also includes
* non-enumerable keys. In these cases only the string "prototype"
* must be returned, the property itself doesn't (yet) need to be created.
*
* Because .prototype is non-configurable, it cannot be deleted. So it should
* be safe to add the property if it is missing and then never delete it.
*/

/* XXX: separate flag for virtual .prototype, not just any constructable
* function lacking the property?
*/
/* XXX: DUK_DEFPROP_FORCE vs. deletion of .prototype */
/* XXX: enumeration order */

DUK_INTERNAL duk_bool_t duk_hobject_ondemand_proto_check(duk_hthread *thr, duk_hobject *obj) {
duk_int_t e_idx;
duk_int_t h_idx;
duk_hstring **keyptr;
duk_bool_t rc;

/* Automatic .prototype property only applies to constructable
* functions, e.g. function expressions, not e.g. object literal
* getters/setters, arrow functions, etc.
*/
if (!DUK_HOBJECT_HAS_CONSTRUCTABLE(obj)) {
return 0;
}
/* Assumption: caller has already checked that the property doesn't
* exist.
*/
DUK_STATS_INC(thr->heap, stats_object_proto_create);
DUK_D(DUK_DPRINT("create .prototype on-demand for %!O", obj));
duk_push_hobject(thr, obj);
duk_push_object(thr); /* -> [ ... func proto ] */
duk_dup_m2(thr); /* -> [ ... func proto func ] */
duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC | DUK_DEFPROP_FORCE); /* -> [ ... func proto ] */
duk_compact(thr, -1); /* compact the prototype */

/* When we add the .prototype property we don't want to do a property
* descriptor lookup because it'd cause infinite recursion. For now,
* define the property using a different name and then rename it in
* place.
*/
duk_xdef_prop_stridx(thr, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_W | DUK_DEFPROP_FORCE); /* -> [ ... func ] */

rc = duk_hobject_find_existing_entry(thr->heap, obj, DUK_HTHREAD_STRING_INT_TARGET(thr), &e_idx, &h_idx);
DUK_UNREF(rc);
DUK_ASSERT(rc != 0);
DUK_ASSERT(e_idx >= 0);
keyptr = DUK_HOBJECT_E_GET_KEY_PTR(thr->heap, obj, e_idx);
DUK_ASSERT(keyptr != NULL);
DUK_ASSERT(*keyptr == DUK_HTHREAD_STRING_INT_TARGET(thr));
*keyptr = DUK_HTHREAD_STRING_PROTOTYPE(thr);
DUK_HSTRING_INCREF(thr, DUK_HTHREAD_STRING_PROTOTYPE(thr));
DUK_HSTRING_DECREF(thr, DUK_HTHREAD_STRING_INT_TARGET(thr));

duk_pop_unsafe(thr);
return 1;
}

/*
* Proxy helpers
*/
Expand Down Expand Up @@ -1753,6 +1822,21 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob
* Not found as a concrete property, check for virtual properties.
*/

/* If object has an on-demand .prototype property, create it now and
* retry via recursion.
*/
/* XXX: faster check for key, e.g. by adding DUK_HSTRING_IS_PROTOTYPE */
/* XXX: enable "has virtual properties" for at least constructable functions,
* so that we can avoid this check in the fast path?
*/
if (DUK_UNLIKELY(key == DUK_HTHREAD_STRING_PROTOTYPE(thr))) {
DUK_D(DUK_DPRINT("ondemand .property, missing -> create?"));
if (duk_hobject_ondemand_proto_check(thr, obj)) {
DUK_D(DUK_DPRINT("ondemand .property, retry own propdesc lookup"));
return duk__get_own_propdesc_raw(thr, obj, key, arr_idx, out_desc, flags);
}
}

if (!DUK_HOBJECT_HAS_VIRTUAL_PROPERTIES(obj)) {
/* Quick skip. */
goto prop_not_found;
Expand Down
1 change: 1 addition & 0 deletions src-input/duk_hstring.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
/* With lowmem builds the high 16 bits of duk_heaphdr are used for other
* purposes, so this leaves 7 duk_heaphdr flags and 9 duk_hstring flags.
*/
/* FIXME: eval_or_arguments -> FLAG_PROTOTYPE */
#define DUK_HSTRING_FLAG_ASCII DUK_HEAPHDR_USER_FLAG(0) /* string is ASCII, clen == blen */
#define DUK_HSTRING_FLAG_ARRIDX DUK_HEAPHDR_USER_FLAG(1) /* string is a valid array index */
#define DUK_HSTRING_FLAG_SYMBOL DUK_HEAPHDR_USER_FLAG(2) /* string is a symbol (invalid utf-8) */
Expand Down
5 changes: 5 additions & 0 deletions src-input/duk_js_var.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,11 +395,14 @@ void duk_js_push_closure(duk_hthread *thr,
/* [ ... closure template ] */

if (add_auto_proto) {
DUK_STATS_INC(thr->heap, stats_object_proto_potential);
#if 0 /* FIXME: testing */
duk_push_object(thr); /* -> [ ... closure template newobj ] */
duk_dup_m3(thr); /* -> [ ... closure template newobj closure ] */
duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC); /* -> [ ... closure template newobj ] */
duk_compact(thr, -1); /* compact the prototype */
duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W); /* -> [ ... closure template ] */
#endif
}

/*
Expand Down Expand Up @@ -470,7 +473,9 @@ void duk_js_push_closure(duk_hthread *thr,
DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, &fun_clos->obj) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(&fun_clos->obj));
DUK_ASSERT(duk_has_prop_stridx(thr, -2, DUK_STRIDX_LENGTH) != 0);
#if 0 /* FIXME: has check triggers creation of .prototype! */
DUK_ASSERT(add_auto_proto == 0 || duk_has_prop_stridx(thr, -2, DUK_STRIDX_PROTOTYPE) != 0);
#endif
/* May be missing .name */
DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(&fun_clos->obj) ||
duk_has_prop_stridx(thr, -2, DUK_STRIDX_CALLER) != 0);
Expand Down

0 comments on commit 68b873b

Please sign in to comment.