Browse Source

fix handling of re-declaration of a global function when the offending existing property is in an ancestor; this needs to be reworked later as it is now quite awkward

pull/1/head
Sami Vaarala 12 years ago
parent
commit
68689d09a7
  1. 1
      src/duk_js_bytecode.h
  2. 138
      src/duk_js_var.c

1
src/duk_js_bytecode.h

@ -170,6 +170,7 @@ typedef duk_u32 duk_instr;
/* DUK_OP_DECLVAR flags in A; bottom bits are reserved for propdesc flags (DUK_PROPDESC_FLAG_XXX) */
#define DUK_BC_DECLVAR_FLAG_UNDEF_VALUE (1 << 4) /* use 'undefined' for value automatically */
#define DUK_BC_DECLVAR_FLAG_FUNC_DECL (1 << 5) /* function declaration */
/* misc constants and helper macros */
#define DUK_BC_REGLIMIT 256 /* if B/C is >= this value, refers to a const */

138
src/duk_js_var.c

@ -982,9 +982,9 @@ static int get_identifier_reference(duk_hthread *thr,
/*
* Object environment record.
*
* Binding (target) object is an external,
* uncontrolled object. Identifier may be bound
* in an ancestor property, and may be an accessor.
* Binding (target) object is an external, uncontrolled object.
* Identifier may be bound in an ancestor property, and may be
* an accessor.
*/
/* FIXME: we could save space by using _target OR _this. If _target, assume
@ -1005,6 +1005,9 @@ static int get_identifier_reference(duk_hthread *thr,
/* Note: we must traverse the prototype chain, so use an actual
* hasprop call here. The property may also be an accessor, so
* we can't get an duk_tval pointer here.
*
* out->holder is NOT set to the actual duk_hobject where the
* property is found, but rather the target object.
*/
if (duk_hobject_hasprop_raw(thr, target, name)) {
@ -1456,7 +1459,7 @@ static int declvar_helper(duk_hthread *thr,
duk_tval *tv;
DUK_DDDPRINT("declvar: thr=%p, env=%p, name=%!O, val=%!T, prop_flags=0x%08x, is_func_decl=%d "
"(env -> %!O)",
"(env -> %!iO)",
(void *) thr, (void *) env, (duk_heaphdr *) name,
tv_val, prop_flags, is_func_decl, (duk_heaphdr *) env);
@ -1493,13 +1496,9 @@ static int declvar_helper(duk_hthread *thr,
/*
* Variable already declared, ignore re-declaration.
*
* The only exception is the updated behavior of E5.1 for
* global function declarations, E5.1 Section 10.5, step 5.e.
* This behavior does not apply to global variable declarations.
*
* The implementation for this special case short circuits
* normal property handling and accesses the property
*/
if (!(is_func_decl && env == thr->builtins[DUK_BIDX_GLOBAL_ENV])) {
@ -1507,26 +1506,52 @@ static int declvar_helper(duk_hthread *thr,
return 1; /* 1 -> needs a PUTVAR */
}
/*
* Special behavior in E5.1.
*
* Note that even though parents == 0, the conflicting property
* may be an inherited property (currently our global object's
* prototype is Object.prototype). Step 5.e first operates on
* the existing property (which is potentially in an ancestor)
* and then defines a new property in the global object (and
* never modifies the ancestor).
*
* Also note that this logic would become even more complicated
* if the conflicting property might be a virtual one. Object
* prototype has no virtual properties, though.
*
* FIXME: this is now very awkward, rework.
*/
DUK_DDDPRINT("re-declare a function binding in global object, "
"updated E5.1 processing");
DUK_ASSERT(ref.holder != NULL);
holder = ref.holder;
/* global object doesn't have array part */
DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_ARRAY(holder));
/* global object has no internal prototype, so we must be
* dealing with an own property; this is why direct property
* storage manipulation makes sense.
/* holder will be set to the target object, not the actual object
* where the property was found (see get_identifier_reference()).
*/
DUK_ASSERT(holder->prototype == NULL);
duk_hobject_find_existing_entry(holder, name, &e_idx, &h_idx);
DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(holder) == DUK_HOBJECT_CLASS_GLOBAL);
DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_ARRAY(holder)); /* global object doesn't have array part */
/* FIXME: use a helper for prototype traversal; no loop check here */
/* must be found: was found earlier, and cannot be inherited */
for (;;) {
DUK_ASSERT(holder != NULL);
duk_hobject_find_existing_entry(holder, name, &e_idx, &h_idx);
if (e_idx >= 0) {
break;
}
holder = holder->prototype;
}
DUK_ASSERT(holder != NULL);
DUK_ASSERT(e_idx >= 0);
/* ref.holder is global object, holder is the object with the
* conflicting property.
*/
flags = DUK_HOBJECT_E_GET_FLAGS(holder, e_idx);
if (!(flags & DUK_PROPDESC_FLAG_CONFIGURABLE)) {
if (flags & DUK_PROPDESC_FLAG_ACCESSOR) {
@ -1544,48 +1569,57 @@ static int declvar_helper(duk_hthread *thr,
DUK_DDDPRINT("existing property is not configurable but "
"is plain, enumerable, and writable -> "
"update value but don't touch flags");
prop_flags = flags; /* don't touch attributes */
"allow redeclaration");
}
/* FIXME: might be more efficient to delete and re-add property,
* as long as we ignore the holder configurability when doing so.
*/
if (holder == ref.holder) {
/* FIXME: if duk_hobject_define_property_internal() was updated
* to handle a pre-existing accessor property, this would be
* a simple call (like for the ancestor case).
*/
DUK_DDDPRINT("redefine, offending property in global object itself");
if (flags & DUK_PROPDESC_FLAG_ACCESSOR) {
duk_hobject *tmp;
tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(holder, e_idx);
DUK_HOBJECT_E_SET_VALUE_GETTER(holder, e_idx, NULL);
DUK_HOBJECT_DECREF(thr, tmp);
tmp = tmp; /* suppress warning */
tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(holder, e_idx);
DUK_HOBJECT_E_SET_VALUE_SETTER(holder, e_idx, NULL);
DUK_HOBJECT_DECREF(thr, tmp);
tmp = tmp; /* suppress warning */
} else {
duk_tval tv_tmp;
if (flags & DUK_PROPDESC_FLAG_ACCESSOR) {
duk_hobject *tmp;
tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(holder, e_idx);
DUK_HOBJECT_E_SET_VALUE_GETTER(holder, e_idx, NULL);
DUK_HOBJECT_DECREF(thr, tmp);
tmp = tmp; /* suppress warning */
tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(holder, e_idx);
DUK_HOBJECT_E_SET_VALUE_SETTER(holder, e_idx, NULL);
DUK_HOBJECT_DECREF(thr, tmp);
tmp = tmp; /* suppress warning */
} else {
duk_tval tv_tmp;
tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(holder, e_idx);
DUK_TVAL_SET_TVAL(&tv_tmp, tv);
DUK_TVAL_SET_UNDEFINED_UNUSED(tv);
DUK_TVAL_DECREF(thr, &tv_tmp);
}
tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(holder, e_idx);
DUK_TVAL_SET_TVAL(&tv_tmp, tv);
DUK_TVAL_SET_UNDEFINED_UNUSED(tv);
DUK_TVAL_DECREF(thr, &tv_tmp);
}
/* Here tv_val would be potentially invalid if we didn't make
* a value copy at the caller.
*/
/* Here tv_val would be potentially invalid if we didn't make
* a value copy at the caller.
*/
tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(holder, e_idx);
DUK_TVAL_SET_TVAL(tv, tv_val);
DUK_TVAL_INCREF(thr, tv);
DUK_HOBJECT_E_SET_FLAGS(holder, e_idx, prop_flags);
tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(holder, e_idx);
DUK_TVAL_SET_TVAL(tv, tv_val);
DUK_TVAL_INCREF(thr, tv);
DUK_HOBJECT_E_SET_FLAGS(holder, e_idx, prop_flags);
DUK_DDDPRINT("updated global binding, final result: "
"value -> %!T, prop_flags=0x%08x",
DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(holder, e_idx),
prop_flags);
} else {
DUK_DDDPRINT("redefine, offending property in ancestor");
DUK_ASSERT(ref.holder == thr->builtins[DUK_BIDX_GLOBAL]);
duk_push_tval(ctx, tv_val);
duk_hobject_define_property_internal(thr, ref.holder, name, prop_flags);
}
DUK_DDDPRINT("updated global binding, final result: "
"value -> %!T, prop_flags=0x%08x",
DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(holder, e_idx),
prop_flags);
return 0;
}

Loading…
Cancel
Save