Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Support try/catch/finally Around an Abstract Function Which Might Throw #1264

Open
sebmarkbage opened this issue Dec 13, 2017 · 5 comments
Open

Comments

@sebmarkbage
Copy link
Contributor

sebmarkbage commented Dec 13, 2017

This is a follow up issue for #1142. In that implementation, we don't support catching an error from an abstract function call. This is not very high priority since I think this will be very rare and we can properly warn for this.

Imagine something like this in a pure context (e.g. in an additional function or React component):

var fn = __abstract('function', 'fn');
var x = 1;
try {
  fn();
  x = 2;
  fn();
  x = 3;
} catch (err) {
}
result = x;

Currently the code will assume that neither call will throw and therefore always result in result = 3.

To solve this I think we need calling an abstract function to be able to conditionally yield an abrupt completion. Yielding a joined completion.

The joinCondition for which branch to take isn't a direct value but whether the invocation worked or not.

I imagine we can have the call generator derive a value, a success flag and a thrown value. So the serialized code would be something like:

var $0_success = true;
var $0_thrown;
var $0;
try {
  $0 = fn();
} catch (thrown) {
  $0_success = false;
  $0_thrown = thrown;
}

The success flag value can be what the joinCondition uses. That way the joinCondition propagates properly when other values get joined. Subsequent emitted statements would get conditional on this value.

So the generated code end up something like this:

var $0_success = true;
try {
  fn();
} catch (_) {
  $0_success = false;
}

var $1_success = true;
if ($1_success) {
  try {
    fn();
  } catch (_) {
    $1_success = false;
  }
}

result = $0_success ? $1_success ? : 3 : 2 : 1;
@hermanventer
Copy link
Contributor

This is pretty hard to do, not least because of that little foot note at the end: "We'd also need to handle the object passed to the catch."

A possible work around that already works, is to wrap the call to fn inside a concrete model function that checks the preconditions of fn and throws an appropriate exception if they fail. Then we can go back to regarding fn as pure and well behaved.

If fn uses exceptions to communicate rare conditions rather than precondition failures, then there is very little we can do statically. This is one of those situations where we throw up our hands and say "please change your code". In this instance you change your code by introducing a non throwing version of fn that returns status codes (or even exceptions) as return results.

@sebmarkbage
Copy link
Contributor Author

I updated with a version where we also store the thrown value which is either undefined or the caught value. This effectively automatically turns this call into something that returns three values.

I'm not sure why this is different from if (condition) throw; which already works fine.

E.g. we can treat one of these calls effectively as:

var {success, exception, value} = callHelper(fn);

We can do that automatically.

In subsequent code:

if (!success) {
  throw exception;
}

This already works because conditional throws in conditional branches are already implemented.

@hermanventer
Copy link
Contributor

A more direct way to fit in with current exception handling logic might be to provide a model function that defines an abstract function that returns not an AbstractValue, but a PossiblyNormalCompletion. The conditions we need for the PNC could well come from model variables that are set in a try catch that wraps the call.

@sebmarkbage
Copy link
Contributor Author

sebmarkbage commented Dec 14, 2017

I can actually model this helper in user space already. :)

(function () {
  function call(fn) {
    var template = {};
    __makePartial(template);
    __makeSimple(template);
    var res = __residual(template, function(fn) {
      var value;
      var exception;
      var success = true;
      try {
        value = fn();
      } catch (e) {
        exception = e;
        success = false;
      }
      return {value, exception, success};
    }, fn);
    if (!res.success) {
      throw res.exception;
    }
    return res.value;
  }
  
  var fn = __abstract('function', 'fn');
  
  var x = 1;
  try {
    call(fn);
    x = 2;
    call(fn);
    x = 3;
  } catch (err) {
  }
  result = x;

})();

It generates some pretty interesting code though.

EDIT: Even more interesting is that the serializer yields incorrect code.

sebmarkbage added a commit to sebmarkbage/prepack that referenced this issue Dec 20, 2017
…a try

This is a follow up to facebookarchive#1142. If we call an unknown function it might throw
which will take a different control Flow. facebookarchive#1264 is the follow up to do this
properly but since we don't have a use case right now, we can just issue
a recoverable error if this happens.
sebmarkbage added a commit to sebmarkbage/prepack that referenced this issue Dec 20, 2017
…a try

This is a follow up to facebookarchive#1142. If we call an unknown function it might throw
which will take a different control Flow. facebookarchive#1264 is the follow up to do this
properly but since we don't have a use case right now, we can just issue
a recoverable error if this happens.
sebmarkbage added a commit to sebmarkbage/prepack that referenced this issue Jan 5, 2018
…a try

This is a follow up to facebookarchive#1142. If we call an unknown function it might throw
which will take a different control Flow. facebookarchive#1264 is the follow up to do this
properly but since we don't have a use case right now, we can just issue
a recoverable error if this happens.
facebook-github-bot pushed a commit that referenced this issue Jan 5, 2018
Summary:
This is a follow up to #1142. If we call an unknown function it might throw which will take a different control flow. #1264 is the follow up to do this properly but since we don't have a use case right now, we can just issue a recoverable error if this happens.
Closes #1284

Differential Revision: D6669732

Pulled By: sebmarkbage

fbshipit-source-id: 32ff40d4afba589ce60193b01034dfb8bc764378
sebmarkbage added a commit to sebmarkbage/prepack that referenced this issue Jan 11, 2018
As outlined in facebookarchive#1264 we know
that it is safe to throw in an pure function since then the return value
will be ignored and therefore its state doesn't matter. The only time
it matters, is if there is a try/catch around it which we already warn
about.

This means that we don't have to error when calling a non-function.

I also plan to add more of these cases like this. So I took the liberty to
also move this category of tests into a separate folder.
sebmarkbage added a commit to sebmarkbage/prepack that referenced this issue Jan 11, 2018
As outlined in facebookarchive#1264 we know
that it is safe to throw in an pure function since then the return value
will be ignored and therefore its state doesn't matter. The only time
it matters, is if there is a try/catch around it which we already warn
about.

This means that we don't have to error when calling a non-function.

I also plan to add more of these cases like this. So I took the liberty to
also move this category of tests into a separate folder.
sebmarkbage added a commit to sebmarkbage/prepack that referenced this issue Jan 11, 2018
As outlined in facebookarchive#1264 we know
that it is safe to throw in an pure function since then the return value
will be ignored and therefore its state doesn't matter. The only time
it matters, is if there is a try/catch around it which we already warn
about.

This means that we don't have to error when calling a non-function.

I also plan to add more of these cases like this. So I took the liberty to
also move this category of tests into a separate folder.
facebook-github-bot pushed a commit that referenced this issue Jan 11, 2018
Summary:
As outlined in #1264 we know that it is safe to throw in an pure function since then the return value will be ignored and therefore its state doesn't matter. The only time it matters, is if there is a try/catch/finally around it which we already warn about.

This means that we don't have to error when calling a non-function.

I also plan to add more of these cases like this. So I took the liberty to also move this category of tests into a separate folder.
Closes #1321

Differential Revision: D6706415

Pulled By: sebmarkbage

fbshipit-source-id: 175ee17a3b02cb8799ee73a4661d194b15ebc40f
sebmarkbage added a commit to sebmarkbage/prepack that referenced this issue Jan 12, 2018
I'm going to need to add a lot of these things that might throw so I wanted
to start preparing the infrastructure for fixing facebookarchive#1264.

The idea is that code inside this scope might emit temporal operations that
might throw. In the future we can wrap these in a try/catch to extract a
caught error and a flag. These can be used to generate a
PossiblyNormalCompletion.

However, for now I just keep the existing strategy of erroring in this case
while I add more callers of this helper.
sebmarkbage added a commit to sebmarkbage/prepack that referenced this issue Jan 12, 2018
I'm going to need to add a lot of these things that might throw so I wanted
to start preparing the infrastructure for fixing facebookarchive#1264.

The idea is that code inside this scope might emit temporal operations that
might throw. In the future we can wrap these in a try/catch to extract a
caught error and a flag. These can be used to generate a
PossiblyNormalCompletion.

However, for now I just keep the existing strategy of erroring in this case
while I add more callers of this helper.
sebmarkbage added a commit to sebmarkbage/prepack that referenced this issue Jan 17, 2018
I'm going to need to add a lot of these things that might throw so I wanted
to start preparing the infrastructure for fixing facebookarchive#1264.

The idea is that code inside this scope might emit temporal operations that
might throw. In the future we can wrap these in a try/catch to extract a
caught error and a flag. These can be used to generate a
PossiblyNormalCompletion.

However, for now I just keep the existing strategy of erroring in this case
while I add more callers of this helper.
sebmarkbage added a commit to sebmarkbage/prepack that referenced this issue Jan 17, 2018
I'm going to need to add a lot of these things that might throw so I wanted
to start preparing the infrastructure for fixing facebookarchive#1264.

The idea is that code inside this scope might emit temporal operations that
might throw. In the future we can wrap these in a try/catch to extract a
caught error and a flag. These can be used to generate a
PossiblyNormalCompletion.

However, for now I just keep the existing strategy of erroring in this case
while I add more callers of this helper.
sebmarkbage added a commit to sebmarkbage/prepack that referenced this issue Jan 17, 2018
I'm going to need to add a lot of these things that might throw so I wanted
to start preparing the infrastructure for fixing facebookarchive#1264.

The idea is that code inside this scope might emit temporal operations that
might throw. In the future we can wrap these in a try/catch to extract a
caught error and a flag. These can be used to generate a
PossiblyNormalCompletion.

However, for now I just keep the existing strategy of erroring in this case
while I add more callers of this helper.
facebook-github-bot pushed a commit that referenced this issue Jan 17, 2018
Summary:
I'm going to need to add a lot of these things that might throw so I wanted to start preparing the infrastructure for #1264. See that issue for more info on the approach.

The idea is that code inside this scope might emit temporal operations that might throw. In the future we can wrap these in a try/catch to extract a caught error and a flag. These can be used to generate a PossiblyNormalCompletion.

However, for now I just keep the existing strategy of erroring in this case while I add more callers of this helper.
Closes #1326

Differential Revision: D6735259

Pulled By: sebmarkbage

fbshipit-source-id: c16a4ed2d4eaf9120dc38d60a4922c7cab0ba59c
@sebmarkbage
Copy link
Contributor Author

Sorry, why is this closed? The original issue is not fixed and we might need it at some point later.

@hermanventer hermanventer reopened this Apr 6, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants