Can we avoid try catch with forOf? #1773

Closed
pflannery opened this Issue Feb 26, 2015 · 19 comments

Projects

None yet

6 participants

@pflannery
Contributor

currently this:

let counter = 0;
let testMap = [];
for (var key of testMap.keys()) {
    counter++;
}

produces this:

  "use strict";
  var counter = 0;
  var testMap = [];
  var $__3 = true;
  var $__4 = false;
  var $__5 = undefined;
  try {
    for (var $__1 = void 0,
        $__0 = (testMap.keys())[$traceurRuntime.toProperty(Symbol.iterator)](); !($__3 = ($__1 = $__0.next()).done); $__3 = true) {
      var key = $__1.value;
      {
        counter++;
      }
    }
  } catch ($__6) {
    $__4 = true;
    $__5 = $__6;
  } finally {
    try {
      if (!$__3 && $__0.return != null) {
        $__0.return();
      }
    } finally {
      if ($__4) {
        throw $__5;
      }
    }
  }

is there a way to opt out of this try catch?
I'm guessing the only way is write the for statement manually for the iteration

@arv
Collaborator
arv commented Feb 26, 2015

The try/catch is needed to handle the return() part.

I agree it is super sucky but the spec mandates it.

I don't think there is a way around this :'(

@arv
Collaborator
arv commented Feb 26, 2015
@mnieper
mnieper commented Feb 26, 2015

There's probably a way to reformulate the transpiled output, but I don't see any reformulation that looks prettier. The point is that the loop body could throw, the iterator could throw and the return function of the iterator could throw.

If Traceur had a sloppy mode, one could think of disabling all exception handling in this mode. But enable such a mode at the global level of a program sounds hazardous.

@pflannery
Contributor

imo we should be able to define our own safe guards for the iterator and anything that happens in the body just like we would a for statement. This would be better than Traceur trying to doing it for us. A good source map will help diagnose back to the source if anything fails.

Also what is this return()? is that not something we too can handle ourselves if we expect a return method to be called?

@pflannery
Contributor

just to add this is what babel outputs

@arv
Collaborator
arv commented Feb 26, 2015

@pflannery That is pretty much what we used to output before we added support for return() which is required for the spec.

Try this:

function* f() {
  try {
    yield 1;
    yield 2;
  } finally {
    console.log('Cleanup');
  }
}

for (var x of f()) {
  console.log(x);
  throw 42;
}

Notice how we print Cleanup in this case.

@sebmck FYI

@kittens
kittens commented Feb 26, 2015

@arv Yeah this was discussed in babel/babel#838, still not sure if the performance penalty is worth is which is unfortunate since I'm a massive believer in spec compliancy. Currently iterator.return() is only called on break.

Edit: Although thinking back on it this would be a scenario for loose mode and the default behaviour should be the try-catch. Thanks for the heads up!

@pflannery
Contributor

I'm still of the opinion that this kind of scenario should be left to the developer to handle when applicable and not constantly by a transpiler.
Closing\disposing just seems like what a developer should ensure and when an unexpected error does occur its down to the garbage collector to ensure all resources are reclaimed.

Also is there a way to detect forOf in native? or is it just a matter of a try/catch test over a forOf statement. Because it would be nice if Traceur at runtime didn't polyfill when forOf is native.

@domenic
Member
domenic commented Feb 26, 2015

There's no way this should be handled by a developer. @arv's test case gives a clear example of behavior that a proper transpiler must support.

@kittens
kittens commented Feb 26, 2015

@pflannery Wait, a transpiler shouldn't be concerned about spec compliancy? I don't understand.

@johnjbarton
Contributor

@pflannery A dev concerned with this level of detail avoid for-of in the rare performance sensitive case. Making these kinds of things optional is too much work for the benefit, compared to other issues.

@pflannery
Contributor

@johnjbarton I'm happy to write for statements manually for the iteration. Would be awesome if native would be used when available instead of poly-filling

Wait, a transpiler shouldn't be concerned about spec compliancy? I don't understand.
@sebmck I've not said that at all.

@kittens
kittens commented Feb 27, 2015

@pflannery

I'm still of the opinion that this kind of scenario should be left to the developer to handle when applicable and not constantly by a transpiler.

@pflannery
Contributor

@sebmck now your just being childish.

@kittens
kittens commented Feb 27, 2015

@pflannery Sorry, was just quoting what lead me to that assumption.

@pflannery
Contributor

@sebmck ah ok no worries

@pflannery pflannery closed this Feb 27, 2015
@pflannery
Contributor

@arv I wanted to amend the transformForOfStatement so that the runtime generated code checks to see if the iterator has a return() implementation and if so then wrap the for-of in a try-catch, otherwise transform without try-catch. Because currently it's rethrowing the exception regardless.

This is what I want to PR

var ${iter} = (${tree.collection})[$traceurRuntime.toProperty(Symbol.iterator)]();
var ${normalCompletion} = true;
if (${iter}.return != null) {
  var ${throwCompletion} = false;
  var ${exception} = undefined;
  try {
    ${innerStatement}
  } catch (${ex}) {
    ${throwCompletion} = true;
    ${exception} = ${ex};
  } finally {
    try {
      if (!${normalCompletion}) {
        ${iter}.return();
      }
    } finally {
      if (${throwCompletion}) {
        throw ${exception};
      }
    }
  }
} else {
  ${innerStatement}
}

I think this leaves people to handle their own exceptions and still very closely meets the spec.

@arv what say you? you ok with me PR'ing this?

@mnieper
mnieper commented Feb 27, 2015

@pflannery When do you want to check whether the iterator has a return() implementation? The iterator could have no return property when the loop begins but could have gained one somewhere in the middle of the loop.

@pflannery
Contributor

@mnieper your right I didnt think about iterators being that wild. Would be nice if the spec had things like Symbol.DisposableIterator, Symbol.ThrowableIterator to let compilers know what they need to do :'(..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment