Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Examples of stack position information restricting refactoring #40

Open
misterdjules opened this issue Dec 3, 2019 · 12 comments
Open

Comments

@misterdjules
Copy link

This proposal mentions:

Additionally, Error.prototype.stack reveals position (line/column number) information and file attribution information that restricts refactoring in a similar way to Function.prototype.toString's exposure of the function source text.

Do you mind sharing concrete examples of the refactoring efforts that are limited by this type of information?

@michaelficarra
Copy link
Member

  1. Stack entries change (additional entries) when you pull out a helper function:
function f(userCode) {
  // ...
  let result = userCode();
  // ...
}

f(function() { console.log((new Error).stack); });
function f(userCode) {
  // ...
  g(userCode);
}

function g(userCode) {
  let result = userCode();
  // ...
}

f(function() { console.log((new Error).stack); });
  1. Stack traces change (positions) when you add/remove lines of code:
function f(userCode) {
  // ...
  let result = userCode();
  // ...
}

f(function() { console.log((new Error).stack); });
function f(userCode) {
  // ...
  // ... more lines (even just whitespace) ...
  let result = userCode();
  // ...
}

f(function() { console.log((new Error).stack); });
  1. Stack traces change (file name / URL) when you rename files. Source code examples not applicable here.

@misterdjules
Copy link
Author

Right, I understand how position information in stack traces can be changed. I think my question was poorly worded. What I meant to ask was: what use cases rely on position information not changing when performing those types of refactoring?

@bathos
Copy link

bathos commented Dec 3, 2019

I don’t know of any. The example in question specifically mentions it in the context of refactoring hazards, which does seem like a stretch even though it isn’t impossible.

For contexts where untrusted code is given access to select trusted objects, I think no stretch is needed. It provides information/fingerprinting and one may need to have tight control over not just what capabilities but also what information is available to the untrusted code. For example, a script could leverage callsite position information to determine what version of its hosting code is running, and this might in turn reveal whether or not some known exploit could be performed without detection. Maybe this consideration should be reframed?

@jorendorff
Copy link

I think the hazard is when you're the author of a widely used open source JS package.

Your users can do arbitrarily silly things, like relying on individual function names in err.stack. If a significant downstream user does this, you then have to weigh the possible breakage when refactoring.

Ridiculous user code really does block upstream improvements from time to time. Microsoft Dynamics CRM 2011 blocked Array.prototype.values for a while. MooTools made it impossible to add Array.prototype.flatten, forcing the standard committee to rename it to .flat.

Of course, these are web browser examples, not cases where "hide source" is relevant. The big difference, the reason web browsers won't break compatibility now matter how stinky the code, is that the browsers compete on web sites working. If a web site doesn't work in Firefox, users don't care that the JS on that site is horrrific. They'll just switch to Chrome. Open source libraries are not in that kind of fight, right?

So I guess I agree: to me this doesn't seem like a strong motivating use case. I don't know that open source maintainers actually want this. It might not see much use even if we implemented it, since it might trade off with bug report quality and developer experience. I could be wrong.

@ljharb
Copy link
Member

ljharb commented Dec 5, 2019

I am a relatively prolific open source maintainer (200+ packages on npm, in various "top N" lists for downloads, etc) and I desperately want this so that I can prevent refactorings from being observable by consumers of the package.

@misterdjules
Copy link
Author

@ljharb Do you have concrete examples (e.g. a link to a GH repo, or to a Website's source code) of some code that relies on position/file information from stack frames that prevents/prevented you from refactoring code in libraries you maintain?

To be clear: this is not a rhetorical question, I'm not necessarily disagreeing this would be useful, but without concrete non-contrived examples that demonstrate the use case, it's difficult to make a case for it and convince others this is actually needed.

@ljharb
Copy link
Member

ljharb commented Dec 5, 2019

No, I do not have concrete examples; semver operates on theoretical breakage not actual breakage, so I want things that can eliminate theoretical concerns as well as concrete ones.

@michaelficarra
Copy link
Member

@misterdjules Library authors not making changes because they are living with this spectre of observability is a consequence.

@jorendorff
Copy link

Has this spectre of observability in fact caused a library author not to make a change, that we know of?

@jorendorff
Copy link

I am a relatively prolific open source maintainer (200+ packages on npm, in various "top N" lists for downloads, etc) and I desperately want this so that I can prevent refactorings from being observable by consumers of the package.

@ljharb Fantastic! Can you say more about this from a library maintainer's perspective? What will you do, and how will you decide which directive to use for what?

What if your users want the kind of error logging described in #27?

[...]semver operates on theoretical breakage not actual breakage, so I want things that can eliminate theoretical concerns as well as concrete ones.

Well, semver explicitly lets packages say what the public API is, and defines compatibility in terms of that declared public API.

And generally semver is just not all that precise. I mean, define "fixes incorrect behavior"...

All of which is to say, I really think we have to justify changes using concrete concerns.

@ljharb
Copy link
Member

ljharb commented Dec 5, 2019

I would hide whatever can be hidden, in an effort to be as conservative as possible.

I am not convinced by runtime observability concerns, since all of those use cases involve deployed apps with a source rewriting tool pipeline available, which thus are trivially positioned to delete any pragmas the app finds inconvenient.

@jorendorff
Copy link

OK, I think I have to disagree, but I see where you're coming from. Thanks for talking this out. This was really helpful.

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

No branches or pull requests

5 participants