[webkit-reviews] review denied: [Bug 172413] [JSC] Map and Set constructors should have fast path for cloning : [Attachment 310848] Patch

bugzilla-daemon at webkit.org bugzilla-daemon at webkit.org
Mon May 22 18:06:47 PDT 2017


Saam Barati <sbarati at apple.com> has denied Yusuke Suzuki
<utatane.tea at gmail.com>'s request for review:
Bug 172413: [JSC] Map and Set constructors should have fast path for cloning
https://bugs.webkit.org/show_bug.cgi?id=172413

Attachment 310848: Patch

https://bugs.webkit.org/attachment.cgi?id=310848&action=review




--- Comment #15 from Saam Barati <sbarati at apple.com> ---
Comment on attachment 310848
  --> https://bugs.webkit.org/attachment.cgi?id=310848
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=310848&action=review

Mostly LGTM, but I think I found one bug, and have some overall comments. r-
b/c the bug.

> Source/JavaScriptCore/runtime/HashMapImpl.h:348
> +	   if ((Checked<uint32_t>(base->m_keyCount) * 2).unsafeGet() >=
powerOf2)
> +	       powerOf2 = (Checked<uint32_t>(powerOf2) * 2).unsafeGet();

How is this condition ever not true? Is it only when keyCount is 0 or 1?

Also, why *2 here, and not *4? After creating this set, if anybody adds
anything to it, we'll rehash and grow the table. Are we just guessing that
nobody adds to it?

> Source/JavaScriptCore/runtime/HashMapImpl.h:360
> +	   makeAndSetNewBuffer(exec, vm);
> +	   RETURN_IF_EXCEPTION(scope, void());
> +
> +	   m_head.set(vm, this, HashMapBucketType::create(vm));
> +	   m_tail.set(vm, this, HashMapBucketType::create(vm));
> +
> +	   m_head->setNext(vm, m_tail.get());
> +	   m_tail->setPrev(vm, m_head.get());
> +	   m_head->setDeleted(true);
> +	   m_tail->setDeleted(true);

Nit: This is identical to the other finishCreation. It's probably worth having
a helper function.

> Source/JavaScriptCore/runtime/HashMapImpl.h:460
> +    ALWAYS_INLINE void addNormalizedNonExistingForCloning(ExecState* exec,
JSValue key, JSValue value = JSValue())

Why does just calling add not work? This function seems nearly identical to the
regular add, it feels like there should be a way to combine them. Perhaps the
hash lookup loop could take a lambda that does returns a boolean indicating if
we can store to the current bucket.

> Source/JavaScriptCore/runtime/InternalFunction.h:51
> +    ALWAYS_INLINE static Structure* createSubclassStructure(ExecState*,
JSValue newTarget, Structure*);

I thought this needed to be marked ALWAYS_INLINE on the implementation of the
function, not declaration?

> Source/JavaScriptCore/runtime/InternalFunction.h:52
> +    JS_EXPORT_PRIVATE static Structure*
createSubclassStructureSlow(ExecState*, JSValue newTarget, Structure*);

this should be private

> Source/JavaScriptCore/runtime/JSSet.cpp:48
> +bool JSSet::isIteratorProtocolFastAndNonObservable()

The name of this function is misleading since returning true does not mean the
protocol is fast and non observable. Maybe this doesn't even need to be a
function. We can just combine this and "addFastAndNonObservable" into a single
function since they both have just one caller, and it's the same function.
(Same comments for the JSMap variant)

> Source/JavaScriptCore/runtime/JSSet.cpp:81
> +    // This is the fast case. Many sets will be an original set.
> +    if (structure == globalObject->setStructure())
> +	   return true;
> +
> +    if (structure->storedPrototype() != globalObject->jsSetPrototype())
> +	   return false;
> +
> +    return true;

This proof does not look correct. Consider:
```
let x = new Set;
x.add = 42;
```

> Source/JavaScriptCore/runtime/JSSet.h:85
> +template<typename To, typename From>
> +inline typename std::enable_if<std::is_same<typename
std::remove_pointer<To>::type, JSSet>::value, To>::type jsDynamicCast(VM&,
From* from)
> +{
> +    if (LIKELY(from->type() == JSSetType))
> +	   return static_cast<To>(from);
> +    return nullptr;
> +}
> +
> +template<>
> +inline JSSet* jsDynamicCast<JSSet*>(VM&, JSValue from)
> +{
> +    if (LIKELY(from.isCell() && from.asCell()->type() == JSSetType))
> +	   return static_cast<JSSet*>(from.asCell());
> +    return nullptr;
> +}

I'd mark JSSet as "final", and static assert that it's final here, otherwise we
could have a bad time if anybody ever inherits from them. This will future
proof the code with a static assert. (Same for JSMap)


More information about the webkit-reviews mailing list