Skip to content

Conversation

@naugtur
Copy link
Member

@naugtur naugtur commented Nov 20, 2025

Description

Just a test covering an edge-case that's rare but known but often hard to recall.

@naugtur naugtur force-pushed the naugtur/ses-stack-test branch from 14b1571 to a3aa921 Compare November 20, 2025 20:58
@naugtur naugtur force-pushed the naugtur/ses-stack-test branch from a3aa921 to c95461a Compare November 20, 2025 20:58
Copy link
Contributor

@mhofman mhofman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised you managed to build a test reproducing this. I'll try to look how we actually end up in this case.

depth += 1;
evaluate(`1+1`);
// didn't fail yet? keep digging.
return stackBuildUp();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend avoiding tail recursion here as that is an explicitly allowed optional optimization.

Comment on lines 142 to 144
t.throws(() => evaluate('1+1'), {
message: /a handler did not reset allowNextEvalToBeUnsafe \(a RangeError\)/,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I thought we couldn't actually trigger this case. We may have broken the stack overflow protection at some point.

Comment on lines +78 to +84
const allowEvalProperties = freeze({
eval: {
value: props => {
const { eval: evalProp } = props;
delete evalScope.eval;
defineProperties(evalScope, { eval: evalProp });
},
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took me a good 10 minutes of staring at this to figure out why it works.
I suggest we add a lengthy comment to explain it 😅
Something like this maybe:

Suggested change
const allowEvalProperties = freeze({
eval: {
value: props => {
const { eval: evalProp } = props;
delete evalScope.eval;
defineProperties(evalScope, { eval: evalProp });
},
// This conforms to the shape of evalScope properties, but instead of evaluating what arrives as first
// argument, it accepts a new shape to replace eval with.
// As a result, when `apply(evaluate, globalObject, [oneTimeEvalProperties]);` is called below,
// the same exact stack depth is reached and the same optimizations are applied to it before the
// FERAL_EVAL is put in place, making it impossible for a stack overflow error to occur in the process
// of getting eval for the main call to the evaluator if it didn't occur in revealing eval.
const allowEvalProperties = freeze({
eval: {
value: props => {
const { eval: evalProp } = props;
delete evalScope.eval;
defineProperties(evalScope, { eval: evalProp });
},

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants