Closed Bug 1128110 Opened 9 years ago Closed 9 years ago

Common up the redundant bits of our multiple pointer-like GC classes

Categories

(Core :: JavaScript: GC, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla38
Tracking Status
firefox38 --- fixed

People

(Reporter: terrence, Assigned: terrence)

References

(Blocks 1 open bug)

Details

Attachments

(6 files)

In order to make a pointer-like class in C++, you need a bunch of overloads. Thus, our pointer-like classes have a long list of similar, but not always identical methods. It would be nice to know which of these divergences are actually necessary for correct operation and, better, to have them de-redundantified.
Attachment #8559460 - Flags: review?(sphink)
Surprisingly, the pointer-like reference operations can all be const.
Attachment #8559461 - Flags: review?(sphink)
Attached patch 3_assign-v0.diffSplinter Review
And we can get away with returning wrapped from all assignment.
Attachment #8559463 - Flags: review?(sphink)
The reason is that we use nonpointer methods for mutable access, except for Handle, where such are not used.
Attachment #8559464 - Flags: review?(sphink)
And the operators we use to make (Fake)(Mutable)Handle unassignable.

With this you can pretty much annotate the top of a pointer class with the operations it is allowed to perform and get the expected behavior. Or at least our current behavior.
Attachment #8559466 - Flags: review?(sphink)
This is a really nice tidyup!
Comment on attachment 8559460 [details] [diff] [review]
1_equality-v0.diff

Review of attachment 8559460 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/public/RootingAPI.h
@@ +151,5 @@
> +        return static_cast<const Base<T> *>(self);
> +    }
> +  public:
> +    bool operator==(const T &other) const { return extractEquality(this)->get() == other; }
> +    bool operator!=(const T &other) const { return extractEquality(this)->get() != other; }

The name "extractEquality" seems a little odd. extractForEquality? castForEquality? How about "asBase"? (Though this is the first patch in the stack I've looked at; maybe it'll need to be asConstBase. Or can that be done with const/non-const overrides?)

@@ +403,5 @@
>   * specialization, define a HandleBase<T> specialization containing them.
>   */
>  template <typename T>
> +class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T>,
> +                                 public js::PointerEqualityOpsMixin<Handle, T>

I herd you like templates...
Attachment #8559460 - Flags: review?(sphink) → review+
Comment on attachment 8559461 [details] [diff] [review]
2_const_ref-v0.diff

Review of attachment 8559461 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/public/RootingAPI.h
@@ +167,5 @@
> +    operator const T &() const { return extractConstRef(this)->get(); }
> +    const T &operator->() const { return extractConstRef(this)->get(); }
> +};
> +
> +#undef EXTRACT_FACTORY

I still like "as" better than "extract". But do you need it at all? Is the macro and helper struct really better than something like

struct PointerConstRefOpsMixin {
    typedef const Base<T> *BasePtr;
    operator const T &() const { return static_cast<BasePtr>(this)->get(); }
    const T &operator->() const { return static_cast<BasePtr>(this)->get(); }
};

I'm going to continue making inane comments that I will possibly later retract, and marking things as r+ anyway, as I move through this stack. :-)
Attachment #8559461 - Flags: review?(sphink) → review+
Ohh, looks like the try run indicates that multiple-inheritance requires more space in the instances in visual studio?!? Maybe we should just go all the way to macros for all of these?
Comment on attachment 8559463 [details] [diff] [review]
3_assign-v0.diff

Review of attachment 8559463 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/public/RootingAPI.h
@@ +183,5 @@
> +        set(other.get());                                                                         \
> +        return *this;                                                                             \
> +    }                                                                                             \
> +  private:
> +

This is indeed unfortunate.

I'm not sure about the visibility stuff, though. Why not leave them out of the macro, and put the macro instantiation into the public: section of the actual class? I think you can even put a semicolon after the instantiation that way, too.

Also, _MIXIN seems a little weird for this one, given that you're not inheriting. How about DECLARE_POINTER_ASSIGN_OPS instead?

I understand that you're kind of thinking of this as a magical mixin thing, but personally I think I prefer to have it do less and be more understandable, even if it means

  public:
    DECLARE_POINTER_ASSIGN_OPS(X, T);
  private:

or not having the macro at the very beginning.

@@ +306,5 @@
> +        } else {
> +            ptr = newPtr;
> +        }
> +    }
> +

Just a random thought -- how often do we get self-assignments? Are there ever any correctness issues with them (probably not), or any perf gains from ignoring them (more likely, but still probably not)?

@@ +806,5 @@
>      const T &get() const { return ptr; }
>  
> +    /*
> +     * This method is public for Rooted so that Codegen.py can use a Rooted
> +     * interchangably with a MutableHandleValue.

*interchangeably
Attachment #8559463 - Flags: review?(sphink) → review+
Comment on attachment 8559464 [details] [diff] [review]
4_nonpointer-v0.diff

Review of attachment 8559464 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/public/RootingAPI.h
@@ +188,5 @@
> +#define NONPOINTER_CONST_ACCESSOR_METHODS(ptr)                                                    \
> +  public:                                                                                         \
> +    const T *address() const { return &(ptr); }                                                   \
> +    const T &get() const { return (ptr); }                                                        \
> +  private:

Same comment about public/private.

@@ +191,5 @@
> +    const T &get() const { return (ptr); }                                                        \
> +  private:
> +
> +#define NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr)                                                  \
> +    NONPOINTER_CONST_ACCESSOR_METHODS(ptr)                                                        \

Hmm... so _MUTABLE_ defines both? You don't want to just call it NONPOINTER_ACCESSOR_METHODS? (Or DECLARE_NONPOINTER_ACCESSOR_METHODS)

@@ +260,5 @@
>  class Heap : public js::HeapBase<T>,
>               public js::PointerConstRefOpsMixin<Heap, T>
>  {
>      POINTER_ASSIGN_OPS_MIXIN(Heap, T)
> +    NONPOINTER_CONST_ACCESSOR_METHODS(ptr);

So you *can* use semicolons without it complaining?
Attachment #8559464 - Flags: review?(sphink) → review+
Comment on attachment 8559466 [details] [diff] [review]
5_nonassignable-v0.diff

Review of attachment 8559466 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/public/RootingAPI.h
@@ +187,5 @@
>  
> +#define POINTER_NONASSIGNABLE_OPS_MIXIN(Wrapper, T)                                               \
> +  private:                                                                                        \
> +    template <typename S> Wrapper<T> &operator=(S) = delete;                                      \
> +    Wrapper<T> &operator=(const Wrapper<T> &) = delete;

Lose the private: and call it DELETE_ASSIGNMENT_OPS?
Attachment #8559466 - Flags: review?(sphink) → review+
As discussed on IRC: msvc is only able to apply the empty base class optimization to a single base class, so this violates sizeof(T) == sizeof(Heap<T>) on windows. Thus, we, with great sadness, need to go with an all-MACROs solution. Oh, well. It actually reads a bit cleaner and should have less impact on compile times.

https://treeherder.mozilla.org/#/jobs?repo=try&revision=6fb99c505c6f
Attachment #8560071 - Flags: review?(sphink)
Comment on attachment 8560071 [details] [diff] [review]
6_apply_review_comments-v0.diff

Review of attachment 8560071 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/public/RootingAPI.h
@@ +152,5 @@
>  // Important: Return a reference so passing a Rooted<T>, etc. to
>  // something that takes a |const T&| is not a GC hazard.
> +#define DECLARE_POINTER_CONSTREF_OPS(Wrapper, T)                                                  \
> +    operator const T &() const { return get(); }                                                  \
> +    const T &operator->() const { return get(); }

Neither DECLARE_POINTER_COMPARISON_OPS nor DECLARE_POINTER_CONSTREF_OPS use the Wrapper argument. I thought maybe you were passing it into everything for consistency, but DECLARE_NON_POINTER_ACCESSOR_METHODS only takes 1 arg. Kill it?

@@ +157,5 @@
>  
>  // Assignment operators on a base class are hidden by the implicitly defined
>  // operator= on the derived class. Thus, define the operator= directly on the
>  // class as we would need to manually pass it through anyway.
> +#define DECLARE_POINTER_ASSIGN_OPS(Wrapper, T)                                                    \

It would be cool if you could eliminate Wrapper here and use decltype(*this) or something, but it really doesn't matter.

@@ +170,2 @@
>  
> +#define DECLARE_DELETE_ASSIGNMENT_OPS(Wrapper, T)                                                 \

DECLARE_DELETE_*? Isn't DELETE_* sufficient?
Attachment #8560071 - Flags: review?(sphink) → review+
Note, the final folded count is 79 lines saved.
https://hg.mozilla.org/mozilla-central/rev/e5f4d89fe8a2
Status: ASSIGNED → RESOLVED
Closed: 9 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla38
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: