Re: [webkit-dev] Stop Using Raw Pointers & References in New Code

2023-01-12 Thread Ryosuke Niwa via webkit-dev


> On Jan 12, 2023, at 9:05 PM, Ryosuke Niwa via webkit-dev 
>  wrote:
> 
> 
>> On Jan 12, 2023, at 8:37 PM, Darin Adler  wrote:
>> 
>>> On Jan 12, 2023, at 9:35 PM, Ryosuke Niwa  wrote:
>>> 
>>> One alternative is to make bar() return RefPtr although that would be a bit 
>>> heavy handed in the case of trivial function calls like this: 
>>> document().frame()->ownerElement()
>> 
>> I don’t quite follow. You just said that all arguments including this need 
>> to have RefPtr or something like it.
> 
> Right. The caller should keep an object alive.
> 
>> What makes it OK to call ownerElement without a RefPtr? What is a “trivial 
>> function” and how can we tell which functions are the trivial ones?
> 
> We made an exception to “trivial functions” to allow chaining like the one 
> above possible. A trivial function is any inlined accessor of a member 
> variable the form: Document& document() { return m_document.get(); } or 
> Frame* frame() { return m_frame.get(); }
> 
> Anything more complicated than that, or for any out-of-lined functions, we 
> should be storing “this” on a smart pointer type.
> 
> 
> Here’s an example:
> static void printNavigationErrorMessage(Frame& frame, const URL& activeURL, 
> const char* reason)
> {
> String message = "Unsafe JavaScript attempt to initiate navigation for 
> frame with URL '" + frame.document()->url().string() + "' from frame with URL 
> '" + activeURL.string() + "'. " + reason + "\n";
> 
> // FIXME: should we print to the console of the document performing the 
> navigation instead?
> frame.document()->domWindow()->printErrorMessage(message);
> }
> 
> This function, under our rule, should be rewritten like this:
> static void printNavigationErrorMessage(Frame& frame, const URL& activeURL, 
> const char* reason)
> {
> String message = "Unsafe JavaScript attempt to initiate navigation for 
> frame with URL '" + frame.document()->url().string() + "' from frame with URL 
> '" + activeURL.string() + "'. " + reason + "\n";
> 
> // FIXME: should we print to the console of the document performing the 
> navigation instead?
> RefPtr { frame.document()->domWindow() }->printErrorMessage(message);
> }
> 
> Here, it’s okay to call document() without first storing “frame” in a smart 
> pointer because it is a function argument. By transitive property, the caller 
> of this function should be keeping the frame object alive. It’s okay to call 
> domWindow() because it’s a trivial accessor function of the form:
> 
> DOMWindow* domWindow() const { return m_domWindow.get(); }
> 
> But it’s not okay to call printErrorMessage without first storing DOMWindow 
> in a RefPtr because it’s a non-trivial function, and domWindow() was not an 
> argument to this function so there is no caller to guarantee the lifetime of 
> it.
> 
> 
> Here’s another example:
> int Element::clientLeft()
> {
> document().updateLayoutIgnorePendingStylesheets();
> 
> if (auto* renderer = renderBox()) {
> auto clientLeft = LayoutUnit { roundToInt(renderer->clientLeft()) };
> return 
> convertToNonSubpixelValue(adjustLayoutUnitForAbsoluteZoom(clientLeft, 
> *renderer).toDouble());
> }
> return 0;
> }
> 
> This function should be rewritten to something like this assuming 
> RenderObject is updated to support CheckedPtr:
> int Element::clientLeft()
> {
> Ref { document() }->updateLayoutIgnorePendingStylesheets();
> 
> if (CheckedPtr renderer = renderBox()) {
> auto clientLeft = LayoutUnit { roundToInt(renderer->clientLeft()) };
> return 
> convertToNonSubpixelValue(adjustLayoutUnitForAbsoluteZoom(clientLeft, 
> *renderer).toDouble());
> }
> return 0;
> }
> 
> Here, we must store document() in Ref before calling 
> updateLayoutIgnorePendingStylesheets because 
> updateLayoutIgnorePendingStylesheets is a non-trivial function, and 
> document() is not one of the arguments. Similarly, we need to store 
> renderBox() in a smart pointer since clientLeft() is a non-trivial function. 
> But we don’t have to store “this” in Ref/RefPtr before calling 
> convertToNonSubpixelValue because “this” is an implicit function argument.

Oops, correction. convertToNonSubpixelValue is a static function which takes 
double & LegacyCSSOMElementMetricsRoundingStrategy so this is irrelevant there.

- R. Niwa

___
webkit-dev mailing list
webkit-dev@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-dev


Re: [webkit-dev] Stop Using Raw Pointers & References in New Code

2023-01-12 Thread Ryosuke Niwa via webkit-dev

> On Jan 12, 2023, at 8:37 PM, Darin Adler  wrote:
> 
>> On Jan 12, 2023, at 9:35 PM, Ryosuke Niwa  wrote:
>> 
>> One alternative is to make bar() return RefPtr although that would be a bit 
>> heavy handed in the case of trivial function calls like this: 
>> document().frame()->ownerElement()
> 
> I don’t quite follow. You just said that all arguments including this need to 
> have RefPtr or something like it.

Right. The caller should keep an object alive.

> What makes it OK to call ownerElement without a RefPtr? What is a “trivial 
> function” and how can we tell which functions are the trivial ones?

We made an exception to “trivial functions” to allow chaining like the one 
above possible. A trivial function is any inlined accessor of a member variable 
the form: Document& document() { return m_document.get(); } or Frame* frame() { 
return m_frame.get(); }

Anything more complicated than that, or for any out-of-lined functions, we 
should be storing “this” on a smart pointer type.


Here’s an example:
static void printNavigationErrorMessage(Frame& frame, const URL& activeURL, 
const char* reason)
{
String message = "Unsafe JavaScript attempt to initiate navigation for 
frame with URL '" + frame.document()->url().string() + "' from frame with URL 
'" + activeURL.string() + "'. " + reason + "\n";

// FIXME: should we print to the console of the document performing the 
navigation instead?
frame.document()->domWindow()->printErrorMessage(message);
}

This function, under our rule, should be rewritten like this:
static void printNavigationErrorMessage(Frame& frame, const URL& activeURL, 
const char* reason)
{
String message = "Unsafe JavaScript attempt to initiate navigation for 
frame with URL '" + frame.document()->url().string() + "' from frame with URL 
'" + activeURL.string() + "'. " + reason + "\n";

// FIXME: should we print to the console of the document performing the 
navigation instead?
RefPtr { frame.document()->domWindow() }->printErrorMessage(message);
}

Here, it’s okay to call document() without first storing “frame” in a smart 
pointer because it is a function argument. By transitive property, the caller 
of this function should be keeping the frame object alive. It’s okay to call 
domWindow() because it’s a trivial accessor function of the form:

DOMWindow* domWindow() const { return m_domWindow.get(); }

But it’s not okay to call printErrorMessage without first storing DOMWindow in 
a RefPtr because it’s a non-trivial function, and domWindow() was not an 
argument to this function so there is no caller to guarantee the lifetime of it.


Here’s another example:
int Element::clientLeft()
{
document().updateLayoutIgnorePendingStylesheets();

if (auto* renderer = renderBox()) {
auto clientLeft = LayoutUnit { roundToInt(renderer->clientLeft()) };
return 
convertToNonSubpixelValue(adjustLayoutUnitForAbsoluteZoom(clientLeft, 
*renderer).toDouble());
}
return 0;
}

This function should be rewritten to something like this assuming RenderObject 
is updated to support CheckedPtr:
int Element::clientLeft()
{
Ref { document() }->updateLayoutIgnorePendingStylesheets();

if (CheckedPtr renderer = renderBox()) {
auto clientLeft = LayoutUnit { roundToInt(renderer->clientLeft()) };
return 
convertToNonSubpixelValue(adjustLayoutUnitForAbsoluteZoom(clientLeft, 
*renderer).toDouble());
}
return 0;
}

Here, we must store document() in Ref before calling 
updateLayoutIgnorePendingStylesheets because 
updateLayoutIgnorePendingStylesheets is a non-trivial function, and document() 
is not one of the arguments. Similarly, we need to store renderBox() in a smart 
pointer since clientLeft() is a non-trivial function. But we don’t have to 
store “this” in Ref/RefPtr before calling convertToNonSubpixelValue because 
“this” is an implicit function argument.

- R. Niwa

___
webkit-dev mailing list
webkit-dev@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-dev


Re: [webkit-dev] Stop Using Raw Pointers & References in New Code

2023-01-12 Thread Darin Adler via webkit-dev

> On Jan 12, 2023, at 9:35 PM, Ryosuke Niwa  wrote:
> 
> One alternative is to make bar() return RefPtr although that would be a bit 
> heavy handed in the case of trivial function calls like this: 
> document().frame()->ownerElement()

I don’t quite follow. You just said that all arguments including this need to 
have RefPtr or something like it. What makes it OK to call ownerElement without 
a RefPtr? What is a “trivial function” and how can we tell which functions are 
the trivial ones?

— Darin___
webkit-dev mailing list
webkit-dev@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-dev


Re: [webkit-dev] Stop Using Raw Pointers & References in New Code

2023-01-12 Thread Ryosuke Niwa via webkit-dev

> On Jan 12, 2023, at 6:50 PM, Michael Catanzaro  wrote:
> 
> On Thu, Jan 12 2023 at 12:35:09 PM -0800, Ryosuke Niwa via webkit-dev 
>  wrote:
>> So… instead of:
>> foo(bar());
>> do:
>> foo(RefPtr { bar() }.get());
> 
> What's the value of creating a temporary RefPtr just to get at the underlying 
> raw pointer? Isn't this overkill?

The benefit is that bar() will be kept alive while the duration of call to foo. 
Without, whatever bar() returns can be dead before foo() finishes running, 
which can result in use-after-free.

An obvious alternative is to use smart pointer types on each function argument. 
But this has a few drawbacks:
The same rule can’t be applied to “this” since passing of “this" pointer is 
implicit in C++.
Ref churn when multiple functions are called with the same object; e.g.
foo = foo()
bar(foo); // ref/deref here
baz(foo); // ref/deref here again
Ref churn when a function argument is passed to another function; e.g.
void foo(RefPtr&& obj)
{
bar(obj); // ref/deref here even though obj is guaranteed to be alive 
throughout this function
}

- R. Niwa

___
webkit-dev mailing list
webkit-dev@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-dev


Re: [webkit-dev] Stop Using Raw Pointers & References in New Code

2023-01-12 Thread Michael Catanzaro via webkit-dev
On Thu, Jan 12 2023 at 12:35:09 PM -0800, Ryosuke Niwa via webkit-dev 
 wrote:

So… instead of:
foo(bar());

do:
foo(RefPtr { bar() }.get());


What's the value of creating a temporary RefPtr just to get at the 
underlying raw pointer? Isn't this overkill?



___
webkit-dev mailing list
webkit-dev@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-dev


Re: [webkit-dev] Stop Using Raw Pointers & References in New Code

2023-01-12 Thread Ryosuke Niwa via webkit-dev


> On Jan 12, 2023, at 1:27 PM, Darin Adler  wrote:
> 
>> On Jan 12, 2023, at 3:35 PM, Ryosuke Niwa via webkit-dev 
>>  wrote:
>> 
>>> On Jan 12, 2023, at 6:13 AM, Darin Adler  wrote:
>>> 
 On Jan 12, 2023, at 12:21 AM, Ryosuke Niwa via webkit-dev 
  wrote:
 
 assuming every local variable / variable in stack is stored in a smart 
 pointer, function arguments are safe to be raw pointers / references via 
 transitive property
>>> 
>>> What about the case where the function argument is the return value from 
>>> another function?
>> 
>> In those cases, the value should be stored in a local variable using a smart 
>> pointer first.
>> 
>> So… instead of:
>> foo(bar());
>> 
>> do:
>> foo(RefPtr { bar() }.get());
> 
> This seems impractical. How will I remember to do this?

The smart pointer analysis tool I've been working with the clang team should be 
able to tell you that once it becomes available. Until then, we probably need 
to rely on reviewers to notice these patterns.

One alternative is to make bar() return RefPtr although that would be a bit 
heavy handed in the case of trivial function calls like this: 
document().frame()->ownerElement()

- R. Niwa

___
webkit-dev mailing list
webkit-dev@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-dev


Re: [webkit-dev] Stop Using Raw Pointers & References in New Code

2023-01-12 Thread Darin Adler via webkit-dev
> On Jan 12, 2023, at 3:35 PM, Ryosuke Niwa via webkit-dev 
>  wrote:
> 
>> On Jan 12, 2023, at 6:13 AM, Darin Adler  wrote:
>> 
>>> On Jan 12, 2023, at 12:21 AM, Ryosuke Niwa via webkit-dev 
>>>  wrote:
>>> 
>>> assuming every local variable / variable in stack is stored in a smart 
>>> pointer, function arguments are safe to be raw pointers / references via 
>>> transitive property
>> 
>> What about the case where the function argument is the return value from 
>> another function?
> 
> In those cases, the value should be stored in a local variable using a smart 
> pointer first.
> 
> So… instead of:
> foo(bar());
> 
> do:
> foo(RefPtr { bar() }.get());

This seems impractical. How will I remember to do this?

— Darin
___
webkit-dev mailing list
webkit-dev@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-dev


Re: [webkit-dev] Stop Using Raw Pointers & References in New Code

2023-01-12 Thread Ryosuke Niwa via webkit-dev

> On Jan 12, 2023, at 6:13 AM, Darin Adler  wrote:
> 
>> On Jan 12, 2023, at 12:21 AM, Ryosuke Niwa via webkit-dev 
>>  wrote:
>> 
>> assuming every local variable / variable in stack is stored in a smart 
>> pointer, function arguments are safe to be raw pointers / references via 
>> transitive property
> 
> What about the case where the function argument is the return value from 
> another function?

In those cases, the value should be stored in a local variable using a smart 
pointer first.

So… instead of:
foo(bar());

do:
foo(RefPtr { bar() }.get());

- R. Niwa

___
webkit-dev mailing list
webkit-dev@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-dev


Re: [webkit-dev] Stop Using Raw Pointers & References in New Code

2023-01-12 Thread Darin Adler via webkit-dev
> On Jan 12, 2023, at 12:21 AM, Ryosuke Niwa via webkit-dev 
>  wrote:
> 
> assuming every local variable / variable in stack is stored in a smart 
> pointer, function arguments are safe to be raw pointers / references via 
> transitive property

What about the case where the function argument is the return value from 
another function?

— Darin
___
webkit-dev mailing list
webkit-dev@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-dev