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

Restrict JavaScript environment for Reusing FLEDGE Worklet Contexts #310

Closed
brusshamilton opened this issue Jun 1, 2022 · 3 comments
Closed

Comments

@brusshamilton
Copy link
Contributor

brusshamilton commented Jun 1, 2022

On the Chrome implementation side, we’re looking at other options for reusing the JavaScript context for running worklets. One option we are pursuing and would like some feedback on is freezing the global object (equivalent to calling Object.Freeze recursively on globalThis and its properties) before running any scripts. This, combined with some other changes to script local storage would prevent worklet functions from persisting information between runs, so we can reuse the context for any interest groups that use the same bidding script.

If we freeze the global context, some JavaScript changes will be necessary:

  1. Global variable definitions through var and function definitions function generateBid(...) {..} will not be allowed. Instead use script-level definitions through let or const, defining worklet functions like: const generateBid = function(...) {...};.

Instead of these:

  • var x = …

  • function generateBid(...) {...}

  • globalThis.generateBid = function(...) {...}

Use these:

  • let x = … or const x = …

  • const generateBid = function(...) {...};

  1. "Overwriting" a property on an object whose prototype is frozen may not work as expected.

Instead of this:

let e = Error(“foo”);
e.name = “bar”; // this line will throw a TypeError

Use this:

let e = Error(“foo”);
Object.defineProperty(e, “name”, {value: “foo”});
@sbelov
Copy link

sbelov commented Jun 1, 2022

One clarification related to this option is whether there needs to be a mechanism for buyer bidding and seller scoring scripts to initialize / pre-compute some data structures before running for later reuse, and, consequently, at which point would freeze happen.

For example, would something along the lines of…

let expensiveObject = computeExpensiveObject(someArguments);

… be possible outside of generateBid, such that all generateBid invocations have access to such precomputed expensiveObject?

In this proposal, would freeze happen after expensiveObject assignment takes place?

@brusshamilton
Copy link
Contributor Author

We're still investigating this space. We will want to start with the global context frozen, since recursively freezing it is expensive (~3ms on a developer workstation). We expect we can start with a frozen global context before running the script and then drop any state changes to allow us to reuse the context. That would require the script to be run again (computing expensiveObject) each time, though.

We are also looking into performing a second freeze after running the script, so we don't have to run the overall script again. This would effectively only compute the expensive object once, regardless of how many times we need to call generateBid. We will likely need to impose additional restrictions on variables - basically we can't allow anything we can't reliably freeze. We're still evaluating what the restrictions would need to be, but we likely won't be able to allow non-const primitives that are not properties of objects, iterators, or generator objects to be reachable when we perform the freeze. We are still very early in this investigation so it is possible that something might come up that prevents us from using this approach.

Also, I'm assuming the expensive object does not depend on anything other than the contents of the script. If that is not the case, then it is unlikely we can save it across runs.

@brusshamilton
Copy link
Contributor Author

I've recently landed crrev.com/c/3664757 which adds the "frozen-context" execution mode, so it should be showing up in Canary builds of Chrome. In this mode the context is frozen after the top-level script is run. Freezing the context can add up to 10ms of delay, but our metrics suggest this will be beneficial for most bidders that have at least 2 interest groups and are unable to take advantage of the "group-by-origin" execution mode.

There are a number of restrictions in this "frozen-context" execution mode, due to either limitations in the current implementation or fundamental limitations of the v8 engine. These restrictions are calculated at the time the context is frozen, which is after the top-level script has run and the custom worklet functions have been added. So any variables in scopes that are no longer reachable shouldn't cause problems. Note that some care should be taken because JavaScript closures capture the scope they are declared in. The restrictions are:

  • Variable declarations that use let instead of const or var may cause problems if they are still in scope after the top-level script has run.
  • Some data-types are not able to be frozen. Also, since we need to manually allow-list specific datatypes, some data-types that should be able to be frozen may not be initially supported. Freezing will fail if there are active iterators, generators, or array buffers, since they cannot be frozen. Since we need to evaluate data types one at a time, if there is a data type that you need that seems like it should be allowed, feel free to file feedback here and we will see if we can support it.

Since there is a lot of complexity and ambiguity of these restrictions I'm putting together a basic worklet testing environment here to make it easier to check if your bidding scripts will be supported in this mode.

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

4 participants
@sbelov @JensenPaul @brusshamilton and others