Skip to content

reconciling the disparate function implementation hiding use cases #23

New issue

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

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

Already on GitHub? # to your account

Closed
michaelficarra opened this issue May 16, 2019 · 2 comments
Closed

Comments

@michaelficarra
Copy link
Member

In the previous TC39 meeting, we discovered that there are a number of use cases for "function implementation hiding", and they have some overlapping and some conflicting requirements. Here is my attempt to categorise these requirements so we can decide how we would like to move forward with the proposal more concretely.

Use Cases and Their Requirements

Under each use case for "function implementation hiding", I've listed the individual requirements needed to accomplish the use case's goals.

  1. no information leaks (for security)
    a. the function does not appear in stack traces
    b. the function's source text (including parameter list) is not available through F.p.toString
  2. appear as a built-in (for polyfilling)
    a. the function does appear in stack traces
    b. the function has no file attribution or position info in stack traces
    c. the function is rendered as a NativeFunction by F.p.toString
  3. no unintentional API (for libraries)
    a. the function does appear in stack traces
    b. the function has no file attribution or position info in stack traces
    c. the function's source text (including name and parameter list) is not available through F.p.toString
    d. the function's length property is not necessarily based on its parameter list
    e. the function's name property is not necessarily based on the syntactic binding
  4. virtualisation
    a. the function does not appear in stack traces

Features

A grouping of the requirements such that they can be composed to satisfy each use case produces the following "features":

  • Feature A
    • the function does not appear in stack traces (satisfying 1a, 4a)
  • Feature Ac
    • the function does appear in stack traces (satisfying 2a, 3a)
  • Feature B
    • the function has no file attribution or position info in stack traces (satisfying 2b, 3b)
    • the function is rendered as a NativeFunction by F.p.toString (satisfying 1b, 2c, 3c)
  • Feature C
    • the function's length property is not necessarily based on its parameter list (satisfying 3d)
    • the function's name property is not necessarily based on the syntactic binding (satisfying 3e)

Notes:

  • Features A and Ac are mutually exclusive
  • Feature A is not possible today
  • Feature Ac is possible today, and is the default for all functions
  • Feature B is not possible today
  • Feature C is possible today through the use of Object.defineProperty to override the length and name assigned to the function at creation. This can be made into a function/decorator for ease of use in expression position.

Alignment of Use Cases with Features

The minimum features to accomplish each use case:

  1. no information leaks: A ∪ B
  2. appear as a built-in: Ac ∪ B
  3. no unintentional API: Ac ∪ B ∪ C
  4. virtualisation: A

Options

Assuming I've done everything right up to this point, we need to make the following choices:

  • Choose one:
    • add a directive that enables Feature A
    • add a directive that enables both Feature A and Feature B
    • do not add a directive for Feature A
  • Choose one:
    • add a directive that enables Feature B
    • do not add a directive that enables Feature B
  • Choose one:
    • add a directive that enables Feature C
    • add a directive that enables both Feature B and Feature C
    • do not add a directive that enables Feature C

My preferences:

  • add a directive that enables both Feature A and Feature B ("sensitive implementation"?)
  • add a directive that enables Feature B ("polyfill"?)
  • do not add a directive that enables Feature C

The reason I've chosen a directive for A and B together instead of keeping them distinct is the desire to have a single opt-in for the security use case, as I described in #8. I am not very opinionated on the polyfilling use case, so I could go either way on the directive for Feature B. Feature C does not seem to pay for itself, since it has good alternatives today.

@domenic
Copy link
Member

domenic commented May 21, 2019

no information leaks (for security)

We never did document this case very well. In particular, I am curious as to whether this can apply to "public" functions, or only to functions that are called by other "public" functions.

I say this because there are also encapsulation/polyfilling cases where this makes sense, but they have that public/private divide. E.g., using your names,

Array.prototype.somethingCool = function () {
  "polyfill";
  return someHelper(this);
};

function someHelper(arr) {
  "sensitive implementation";
  // ...
}

virtualisation

I don't quite understand how you are using the term here.

My preferences:

I agree with the overall outcomes, although per my above example, I think the names probably need tweaking. (Maybe "internals hidden" vs. "completely hidden" is a theme to think about.)

I think we may be coming up against a complexity budget; folks were already hesitant about one new directive, and two may be stretching it. I'm wondering if there are other tweaks to the "API" we could make to improve that. Or at least we could present some creative alternatives to the committee, if only to show that two directives is the simplest path.

As a handwavey and imprecise example, we could contemplate something like a single module- or class-level directive that has the combined effects of:

  • All module exports and public class members are "polyfill"
  • Everything else (in particular non-exported functions and private class members) are "sensitive implementation"

@michaelficarra
Copy link
Member Author

The committee went with our recommendation: https://docs.google.com/presentation/d/1lWH97DxTLU3_1EJA-F19uIzagZQx7PZmys7WyNXw3cY/edit#slide=id.g5ded237d17_0_65

  • "hide implementation" for [native code] and no file attribution or position info in stack traces
  • "sensitive" for [native code] and not appearing at all in stack traces

For now, we also have "preserve implementation" for resetting the F.p.toString behaviour, though I'm still hoping we can drop this.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants