/** 15.2.4.5 Object.prototype.hasOwnProperty (V) When the hasOwnProperty method is called with argument V, the following steps are taken: 1. Let O be this object. 2. Call ToString(V). 3. If O doesnt have a property with the name given by Result(2), return false. 4. Return true. NOTE Unlike [[HasProperty]] (section 8.6.2.4), this method does not consider objects in the prototype chain. */ bool ObjectClass::_hasOwnProperty(Atom thisAtom, Stringp name) { AvmCore* core = this->core(); name = name ? core->internString(name) : (Stringp)core->knull; Traitsp t = NULL; switch (atomKind(thisAtom)) { case kObjectType: { // ISSUE should this look in traits and dynamic vars, or just dynamic vars. ScriptObject* obj = AvmCore::atomToScriptObject(thisAtom); if (obj->hasStringProperty(name)) return true; t = obj->traits(); break; } case kNamespaceType: case kStringType: case kBooleanType: case kDoubleType: case kIntegerType: t = toplevel()->toTraits(thisAtom); break; default: return false; } return t->getTraitsBindings()->findBinding(name, core->publicNamespace) != BIND_NONE; }
/** 15.2.4.5 Object.prototype.hasOwnProperty (V) When the hasOwnProperty method is called with argument V, the following steps are taken: 1. Let O be this object. 2. Call ToString(V). 3. If O doesn't have a property with the name given by Result(2), return false. 4. Return true. NOTE Unlike [[HasProperty]] (section 8.6.2.4), this method does not consider objects in the prototype chain. */ bool ObjectClass::_hasOwnProperty(Atom thisAtom, Stringp name) { AvmCore* core = this->core(); name = name ? core->internString(name) : (Stringp)core->knull; Traitsp t = NULL; switch (atomKind(thisAtom)) { case kObjectType: { // ISSUE should this look in traits and dynamic vars, or just dynamic vars. ScriptObject* obj = AvmCore::atomToScriptObject(thisAtom); // TODO // The change below is important as otherwise we will throw error in a call to hasAtomProperty for ByteArrayObject. // This gets us back to the behaviour which we had in Marlin. // A bugzilla bug [ 562224 ] has been created to address this issue more cleanly in near future return obj->traits()->getTraitsBindings()->findBinding(name, core->findPublicNamespace()) != BIND_NONE || obj->hasStringProperty(name); } case kNamespaceType: case kStringType: case kBooleanType: case kDoubleType: case kIntptrType: t = toplevel()->toTraits(thisAtom); break; default: return false; } // NOTE use caller's public namespace return t->getTraitsBindings()->findBinding(name, core->findPublicNamespace()) != BIND_NONE; }
ScriptObject* TypeDescriber::describeTraits(Traitsp traits, uint32_t flags) { if (!(flags & INCLUDE_TRAITS)) return NULL; AvmCore* core = m_toplevel->core(); GC* gc = core->GetGC(); TraitsBindingsp tb = traits->getTraitsBindings(); TraitsMetadatap tm = traits->getTraitsMetadata(); ScriptObject* o = new_object(); ArrayObject* bases = NULL; ArrayObject* metadata = NULL; ArrayObject* interfaces = NULL; ArrayObject* methods = NULL; ArrayObject* accessors = NULL; ArrayObject* variables = NULL; ScriptObject* constructor = NULL; if (flags & INCLUDE_BASES) { metadata = new_array(); PoolObject* class_mdpool; const uint8_t* class_md = tm->getMetadataPos(class_mdpool); if (class_md) addDescribeMetadata(metadata, class_mdpool, class_md); } if (flags & INCLUDE_BASES) { bases = new_array(); for (Traitsp b = traits->base; b; b = b->base) pushstr(bases, describeClassName(b)); } if (flags & INCLUDE_INTERFACES) { interfaces = new_array(); // our TraitsBindings only includes our own interfaces, not any we might have inherited, // so walk the tree. there might be redundant interfaces listed in the inheritance, // so use a list to remove dupes List<Traitsp> unique(gc); for (Traitsp b = traits; b; b = b->base) { TraitsBindingsp tbi = b->getTraitsBindings(); for (uint32_t i = 0; i < tbi->interfaceCapacity; ++i) { Traitsp ti = tbi->getInterface(i); if (ti && ti->isInterface && unique.indexOf(ti) < 0) { unique.add(ti); pushstr(interfaces, describeClassName(ti)); } } } } // constructor if (flags & INCLUDE_CONSTRUCTOR) { AbstractFunction* initMethod = traits->init; if (initMethod && initMethod->param_count) { constructor = describeParams(initMethod); } } if (flags & (INCLUDE_ACCESSORS | INCLUDE_METHODS | INCLUDE_VARIABLES)) { // recover slot/method metadata and method-declarer information. // make a flattened set of bindings so we don't have to check for overrides as we go. // This is not terribly efficient, but doesn't need to be. MultinameHashtable* mybind = new (gc) MultinameHashtable(); addBindings(mybind, tb, flags); // Don't want interface methods, so post-process and wipe out any // bindings that were added for (uint32_t i = 0; i < tb->interfaceCapacity; ++i) { Traitsp ti = tb->getInterface(i); if (ti && ti->isInterface) { TraitsBindingsp tbi = ti->getTraitsBindings(); for (int32_t index = 0; (index = tbi->next(index)) != 0; ) { Stringp name = tbi->keyAt(index); Namespacep ns = tbi->nsAt(index); mybind->add(name, ns, BIND_NONE); } } } // yuck, replicate buggy behavior in FP9/10 List<Namespacep> nsremoval(gc); if (flags & HIDE_NSURI_METHODS) { for (uint32_t i = 0; i < tb->interfaceCapacity; ++i) { Traitsp ti = tb->getInterface(i); // already did interfaces, don't need to do them again if (ti && !ti->isInterface) { TraitsBindingsp tbi = ti->getTraitsBindings(); for (int32_t index = 0; (index = tbi->next(index)) != 0; ) { Namespacep ns = tbi->nsAt(index); if (ns->getURI()->length() > 0 && nsremoval.indexOf(ns) < 0) nsremoval.add(ns); } } } } for (int32_t index = 0; (index = mybind->next(index)) != 0; ) { Binding binding = mybind->valueAt(index); Stringp name = mybind->keyAt(index); Namespacep ns = mybind->nsAt(index); Stringp nsuri = ns->getURI(); TraitsMetadata::MetadataPtr md1 = NULL; TraitsMetadata::MetadataPtr md2 = NULL; PoolObject* md1pool = NULL; PoolObject* md2pool = NULL; // We only display public members -- exposing private namespaces could compromise security. if (ns->getType() != Namespace::NS_Public) continue; if ((flags & HIDE_NSURI_METHODS) && nsremoval.indexOf(ns) >= 0) continue; ScriptObject* v = new_object(); const BindingKind bk = AvmCore::bindingKind(binding); switch (bk) { case BKIND_CONST: case BKIND_VAR: { if (!(flags & INCLUDE_VARIABLES)) continue; const uint32_t slotID = AvmCore::bindingToSlotId(binding); const KVPair props[] = { { kstrid_access, strAtom(str(bk == BKIND_CONST ? kstrid_readonly : kstrid_readwrite)) }, { kstrid_type, strAtom(describeClassName(tb->getSlotTraits(slotID))) }, }; setpropmulti(v, props, elem_count(props)); if (!variables) variables = new_array(); pushobj(variables, v); md1 = tm->getSlotMetadataPos(slotID, md1pool); break; } case BKIND_METHOD: { if (!(flags & INCLUDE_METHODS)) continue; const uint32_t methodID = AvmCore::bindingToMethodId(binding); const AbstractFunction* af = tb->getMethod(methodID); Traitsp declaringTraits = af->declaringTraits; const KVPair props[] = { { kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) }, { kstrid_returnType, strAtom(describeClassName(af->returnTraits())) }, { kstrid_parameters, objAtom(describeParams(af)) }, }; setpropmulti(v, props, elem_count(props)); if (!methods) methods = new_array(); pushobj(methods, v); md1 = tm->getMethodMetadataPos(methodID, md1pool); break; } case BKIND_GET: case BKIND_SET: case BKIND_GETSET: { if (!(flags & INCLUDE_ACCESSORS)) continue; const uint32_t methodID = AvmCore::hasGetterBinding(binding) ? AvmCore::bindingToGetterId(binding) : AvmCore::bindingToSetterId(binding); const AbstractFunction* af = tb->getMethod(methodID); Traitsp declaringTraits = af->declaringTraits; Traitsp accessorType = AvmCore::hasGetterBinding(binding) ? af->returnTraits() : af->paramTraits(1); static const uint8_t bk2str[8] = { uint8_t(kstrid_emptyString), // BKIND_NONE uint8_t(kstrid_emptyString), // BKIND_METHOD uint8_t(kstrid_emptyString), // BKIND_VAR uint8_t(kstrid_emptyString), // BKIND_CONST uint8_t(kstrid_emptyString), // BKIND_ITRAMP uint8_t(kstrid_readonly), // BKIND_GET uint8_t(kstrid_writeonly), // BKIND_SET uint8_t(kstrid_readwrite) // BKIND_GETSET }; const KVPair props[] = { { kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) }, { kstrid_access, strAtom(str(StringId(bk2str[bk]))) }, { kstrid_type, strAtom(describeClassName(accessorType)) }, }; setpropmulti(v, props, elem_count(props)); if (AvmCore::hasGetterBinding(binding)) md1 = tm->getMethodMetadataPos(AvmCore::bindingToGetterId(binding), md1pool); if (AvmCore::hasSetterBinding(binding)) md2 = tm->getMethodMetadataPos(AvmCore::bindingToSetterId(binding), md2pool); if (!accessors) accessors = new_array(); pushobj(accessors, v); break; } case BKIND_NONE: break; } ArrayObject* vm = NULL; if ((flags & INCLUDE_METADATA) && (md1 || md2)) { vm = new_array(); addDescribeMetadata(vm, md1pool, md1); addDescribeMetadata(vm, md2pool, md2); } const KVPair props[] = { { kstrid_name, strAtom(name) }, { kstrid_uri, strAtom(nsuri->length() == 0 ? NULL : nsuri) }, { kstrid_metadata, objAtom(vm) }, }; setpropmulti(v, props, elem_count(props)); } } const KVPair props[] = { { kstrid_bases, objAtom(bases) }, { kstrid_interfaces, objAtom(interfaces) }, { kstrid_metadata, objAtom(metadata) }, { kstrid_accessors, objAtom(accessors) }, { kstrid_methods, objAtom(methods) }, { kstrid_variables, objAtom(variables) }, { kstrid_constructor, objAtom(constructor) }, }; setpropmulti(o, props, elem_count(props)); return o; }
ScriptObject* TypeDescriber::describeTraits(Traitsp traits, uint32_t flags, Toplevel* toplevel) { if (!(flags & INCLUDE_TRAITS)) return NULL; AvmCore* core = m_toplevel->core(); GC* gc = core->GetGC(); TraitsBindingsp tb = traits->getTraitsBindings(); TraitsMetadatap tm = traits->getTraitsMetadata(); ScriptObject* o = new_object(); ArrayObject* bases = NULL; ArrayObject* metadata = NULL; ArrayObject* interfaces = NULL; ArrayObject* methods = NULL; ArrayObject* accessors = NULL; ArrayObject* variables = NULL; ScriptObject* constructor = NULL; if (flags & INCLUDE_METADATA) { metadata = new_array(); PoolObject* class_mdpool; const uint8_t* class_md = tm->getMetadataPos(class_mdpool); if (class_md) addDescribeMetadata(metadata, class_mdpool, class_md); } if (flags & INCLUDE_BASES) { bases = new_array(); for (Traitsp b = traits->base; b; b = b->base) pushstr(bases, describeClassName(b)); } if (flags & INCLUDE_INTERFACES) { interfaces = new_array(); for (InterfaceIterator iter(traits); iter.hasNext();) { Traits* ti = iter.next(); pushstr(interfaces, describeClassName(ti)); } } // constructor if (flags & INCLUDE_CONSTRUCTOR) { MethodInfo* initMethod = traits->init; if (initMethod) { initMethod->resolveSignature(toplevel); MethodSignaturep ms = initMethod->getMethodSignature(); if (ms->param_count() > 0) { constructor = describeParams(initMethod, ms); } } } if (flags & (INCLUDE_ACCESSORS | INCLUDE_METHODS | INCLUDE_VARIABLES)) { // recover slot/method metadata and method-declarer information. // make a flattened set of bindings so we don't have to check for overrides as we go. // This is not terribly efficient, but doesn't need to be. MultinameBindingHashtable* mybind = MultinameBindingHashtable::create(gc); addBindings(m_toplevel->core(), mybind, tb, flags); // Don't want interface methods, so post-process and wipe out any // bindings that were added. for (InterfaceIterator ifc_iter(traits); ifc_iter.hasNext();) { Traitsp ti = ifc_iter.next(); TraitsBindingsp tbi = ti->getTraitsBindings(); StTraitsBindingsIterator iter(tbi); while (iter.next()) { if (!iter.key()) continue; mybind->add(iter.key(), iter.ns(), BIND_NONE); } } // yuck, replicate buggy behavior in FP9/10 RCList<Namespace> nsremoval(gc, kListInitialCapacity); if (flags & HIDE_NSURI_METHODS) { for (TraitsBindingsp tbi = tb->base; tbi; tbi = tbi->base) { StTraitsBindingsIterator iter(tbi); while (iter.next()) { if (!iter.key()) continue; Namespacep ns = iter.ns(); if (ns->getURI()->length() > 0 && nsremoval.indexOf(ns) < 0) { nsremoval.add(ns); } } } } StMNHTBindingIterator iter(mybind); while (iter.next()) { if (!iter.key()) continue; Stringp name = iter.key(); Namespacep ns = iter.ns(); Binding binding = iter.value(); Stringp nsuri = ns->getURI(); TraitsMetadata::MetadataPtr md1 = NULL; TraitsMetadata::MetadataPtr md2 = NULL; PoolObject* md1pool = NULL; PoolObject* md2pool = NULL; // We only display public members -- exposing private namespaces could compromise security. if (ns->getType() != Namespace::NS_Public) { continue; } if ((flags & HIDE_NSURI_METHODS) && nsremoval.indexOf(ns) >= 0) { continue; } ScriptObject* v = new_object(); const BindingKind bk = AvmCore::bindingKind(binding); switch (bk) { case BKIND_CONST: case BKIND_VAR: { if (!(flags & INCLUDE_VARIABLES)) continue; const uint32_t slotID = AvmCore::bindingToSlotId(binding); const KVPair props[] = { { kstrid_access, strAtom(str(bk == BKIND_CONST ? kstrid_readonly : kstrid_readwrite)) }, { kstrid_type, strAtom(describeClassName(tb->getSlotTraits(slotID))) }, }; setpropmulti(v, props, elem_count(props)); if (!variables) variables = new_array(); pushobj(variables, v); md1 = tm->getSlotMetadataPos(slotID, md1pool); break; } case BKIND_METHOD: { if (!(flags & INCLUDE_METHODS)) continue; const uint32_t methodID = AvmCore::bindingToMethodId(binding); MethodInfo* mi = tb->getMethod(methodID); mi->resolveSignature(toplevel); MethodSignaturep ms = mi->getMethodSignature(); Traitsp declaringTraits = mi->declaringTraits(); const KVPair props[] = { { kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) }, { kstrid_returnType, strAtom(describeClassName(ms->returnTraits())) }, { kstrid_parameters, objAtom(describeParams(mi, ms)) }, }; setpropmulti(v, props, elem_count(props)); if (!methods) methods = new_array(); pushobj(methods, v); md1 = tm->getMethodMetadataPos(methodID, md1pool); break; } case BKIND_GET: case BKIND_SET: case BKIND_GETSET: { if (!(flags & INCLUDE_ACCESSORS)) continue; const uint32_t methodID = AvmCore::hasGetterBinding(binding) ? AvmCore::bindingToGetterId(binding) : AvmCore::bindingToSetterId(binding); MethodInfo* mi = tb->getMethod(methodID); mi->resolveSignature(toplevel); MethodSignaturep ms = mi->getMethodSignature(); Traitsp declaringTraits = mi->declaringTraits(); // The verifier does not check the signature of a setter // except when it is invoked. We must be prepared for the // case in which it has no arguments. Traitsp accessorType; if (AvmCore::hasGetterBinding(binding)) { accessorType = ms->returnTraits(); } else { // If setter is malformed, just use '*' as a placeholder. accessorType = (ms->param_count() < 1) ? NULL : ms->paramTraits(1); } static const uint8_t bk2str[8] = { uint8_t(kstrid_emptyString), // BKIND_NONE uint8_t(kstrid_emptyString), // BKIND_METHOD uint8_t(kstrid_emptyString), // BKIND_VAR uint8_t(kstrid_emptyString), // BKIND_CONST uint8_t(kstrid_emptyString), // unused uint8_t(kstrid_readonly), // BKIND_GET uint8_t(kstrid_writeonly), // BKIND_SET uint8_t(kstrid_readwrite) // BKIND_GETSET }; const KVPair props[] = { { kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) }, { kstrid_access, strAtom(str(StringId(bk2str[bk]))) }, { kstrid_type, strAtom(describeClassName(accessorType)) }, }; setpropmulti(v, props, elem_count(props)); if (AvmCore::hasGetterBinding(binding)) md1 = tm->getMethodMetadataPos(AvmCore::bindingToGetterId(binding), md1pool); if (AvmCore::hasSetterBinding(binding)) md2 = tm->getMethodMetadataPos(AvmCore::bindingToSetterId(binding), md2pool); if (!accessors) accessors = new_array(); pushobj(accessors, v); break; } case BKIND_NONE: break; default: break; } ArrayObject* vm = NULL; if ((flags & INCLUDE_METADATA) && (md1 || md2)) { vm = new_array(); addDescribeMetadata(vm, md1pool, md1); addDescribeMetadata(vm, md2pool, md2); } const KVPair props[] = { { kstrid_name, strAtom(name) }, { kstrid_uri, strAtom(nsuri->length() == 0 ? NULL : nsuri) }, { kstrid_metadata, objAtom(vm) }, }; setpropmulti(v, props, elem_count(props)); } } const KVPair props[] = { { kstrid_bases, objAtom(bases) }, { kstrid_interfaces, objAtom(interfaces) }, { kstrid_metadata, objAtom(metadata) }, { kstrid_accessors, objAtom(accessors) }, { kstrid_methods, objAtom(methods) }, { kstrid_variables, objAtom(variables) }, { kstrid_constructor, objAtom(constructor) }, }; setpropmulti(o, props, elem_count(props)); return o; }