Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Optimized Functions

Christopher Blappert edited this page Jul 27, 2018 · 3 revisions

History

  • Originally built for this use case #799 with some specific constraints:
    • optimized functions would only ever be called once
    • they would only be called after global initialization
    • they would use disjoint sets of state
    • they can assume that state is as it was at the end of global initialization

Current Assumptions

Purity

We default to all __optimized functions being pure. In this case, the assumptions below about mutation are no longer an issue. __optimized functions marked as pure check their purity and emit compiler diagnostics PP1003 and PP1007.

It is possible to specify non-pure optimized functions, but they likely will not work as expected in most cases.

Mutation

For any mutation that results in a concrete value, it is okay to emit code assigning that concrete value to its binding/property

let x = 5;
function residual() { x++; }
function foo() {
  // Some expensive computation
  return x;
}
__optimize(foo);

gets transformed to

var residual, foo;
let x;
(function () {
  var _$0 = this;

  var _1 = function () {
    return 5;
  };

  var _0 = function () {
    x++;
  };

  _$0.residual = _0;
  _$0.foo = _1;
  x = 5;
}).call(this);

Note that the optimized foo function returns 5 and not x.

To solve this, use-cases like the react compiler automatically turn captured mutable values into AbstractValues. For any mutation that results in an abstract value, any interactions with the value will be recorded in the generator so code will be emitted correctly.

let x = __abstract("number", "x");
function residual() { x++; }
function foo() {
  // Some expensive computation
  return x;
}
__optimize(foo);

Gets transformed into

var residual, foo;
let x;
(function () {
  var _$0 = this;

  var _1 = function () {
    return _2;
  };

  var _0 = function () {
    x++;
  };

  _$0.residual = _0;
  var _2 = x;
  _$0.foo = _1;
  x = _2;
}).call(this);

Bindings

Mutations to bindings should only be serialized when the bindings are reachable (used by a rewritten optimized function or in the global scope)

See test/serializer/additional-functions/precise_captures.js

function Bar() {
  return 123;
}

function Foo() {
  return Bar();
}

if (global.__optimize) __optimize(Foo);

global.Foo = Foo;

transforms to

var _$0 = this;

var _0 = function () {
  return 123;
};

_$0.Foo = _0;

Bar gets eliminated even though Foo originally captured it because the value is no longer needed by the new body of Foo