GithubHelp home page GithubHelp logo

facebookarchive / prepack Goto Github PK

View Code? Open in Web Editor NEW
14.2K 261.0 426.0 18.61 MB

A JavaScript bundle optimizer.

Home Page: http://prepack.io

License: Other

JavaScript 98.44% Shell 0.21% CSS 0.35% HTML 1.00%
javascript optimization

prepack's Introduction

Prepack Circle CI

Prepack is a partial evaluator for JavaScript. Prepack rewrites a JavaScript bundle, resulting in JavaScript code that executes more efficiently. For initialization-heavy code, Prepack works best in an environment where JavaScript parsing is effectively cached.

See the official prepack.io website for an introduction and an interactive REPL playground.

Status

We, the Prepack team at Facebook, have temporarily set down work on Prepack including the React compiler project. You won't see many Prepack PRs while we are currently prioritizing some other projects.

How to use Prepack

Install the CLI via npm,

$ npm install -g prepack

Or if you prefer yarn, make sure you get yarn first,

$ npm install -g yarn

and then install the Prepack CLI via yarn:

$ yarn global add prepack

You may need to prepend (pun intended!) the command with sudo in some cases.

Let the party begin

To compile a file and print the output to the console:

$ prepack script.js

If you want to compile a file and output to another file:

$ prepack script.js --out script-processed.js

Detailed instructions and the API can be found at Prepack CLI: Getting Started

Plugins to other tools

The following are a few plugins to other tools. They have been created and are maintained separately from Prepack itself. If you run into any issues with those plugins, please ask the plugin maintainers for support.

Test Results and Code Coverage

How to get the code

  1. Clone repository and make it your current directory.
  2. git submodule init
  3. git submodule update --init
  4. Get yarn and node, then do yarn

Note: For development work you really need yarn, as many scripts require it.

How to build, lint, type check

  1. Get the code
  2. yarn build
    You can later run yarn watch in the background to just compile changed files on the fly.
  3. yarn lint
  4. yarn flow

How to run tests

  1. Get the code
  2. Make sure the code is built, either by running yarn build or yarn watch
  3. yarn test

You can run individual test suites as follows:

  • yarn test-serializer
    This tests the interpreter and serializer. All tests should pass.
  • yarn test-test262
    This tests conformance against the test262 suite. Not all will pass, increasing conformance is work in progress.

How to run the interpreter

  1. Get the code
  2. Make sure the code is built, either by running yarn build or yarn watch
  3. yarn repl
    This starts an interactive interpreter session.

How to run Prepack

  1. Get the code

  2. Make sure the code is built, either by running yarn build or yarn watch.

  3. Have a JavaScript file handy that you want to prepack, for example:
    echo "function hello() { return 'hello'; } function world() { return 'world'; } s = hello() + ' ' + world();" >/tmp/sample.js

  4. cat /tmp/sample.js | yarn prepack-cli
    Try --help for more options.

How to validate changes

Instead of building, linting, type checking, testing separately, the following does everything together:
yarn validate

How to edit the website

The content for prepack.io resides in the website directory of this repository. To make changes, submit a pull request, just like for any code changes.

In order to run the website locally at localhost:8000:

  1. Build prepack into the website: yarn build && mv prepack.min.js website/js
  2. Run python -m SimpleHTTPServer (Python 2) or python -m http.server (Python 3) from the website/ directory

How to contribute

To read more about the project, check out this suggested reading wiki

For more information about contributing pull requests and issues, see our Contribution Guidelines.

License

Prepack is BSD-licensed. We also provide an additional patent grant.

prepack's People

Contributors

a8m avatar artiebits avatar bakkot avatar baptistemanson avatar caiismyname avatar calebmer avatar cblappert avatar dkrew0213 avatar echo304 avatar gaearon avatar hermanventer avatar hershi avatar intheclouddan avatar jeysal avatar jhalley avatar jwli229 avatar kishore-b-rao avatar lxfind avatar manasjayanth avatar neilmacintosh avatar ntillmann avatar pmrcunha avatar sahands avatar sebmarkbage avatar sophiebits avatar srijitdutt avatar trueadm avatar victorhom avatar wdhorton avatar zertosh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

prepack's Issues

Serializer crashes on abstract array length

Watch it crash:

var x = global.__abstract ? __abstract("boolean", "true") : true;
var a = [];
if (x) a.push(42);
inspect = function() { return a.length; }

To fix, _serializeValueArray in serializer.js needs to be modified as follows:

  • Don't just call ToLength on the "length" property, but first check if it's an abstract value. If not, proceed as always.
  • If it's an abstract value, emit code that...
  1. Initializes the array to the empty array literal.
  2. Sets the array length to abstract length value.
  3. And then emits all properties (including numeric keys) via addProperties.
    Ideally, fuse the old and the new code in an elegant way.

Add a regression test to test/serializer/abstract.

Native Values That Are Not Allowed to Serialize

I used to have a magic string for this but we can do better. It would be good to have a materialized value that can be read and used as any other value, but if it ever ends up in the serialized output it should throw.

An example of this is a native file handle. It can be valid to use it during compilation such as in Node's start up phase. However, it will be invalid by the time the application revives. So it can only be temporary.

I used to add this to the module system cache so that if it ever got serialized, I'd know to fix it so that it can be fully cleaned up at the end rather than accidentally live with a bloated output.

Look into turning all closures created from a single node into metaprogramming at runtime

var a = [1, 2].map(function (i) {
  return function () { return i; };
});

is going to produce something like:

var _0 = 1;
var _1 = 2;

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

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

var a = [_3, _4];

Some engines may perform some optimisations to reduce the amount of bytecode for the original closure. We could turn this into something that's cheap to create at runtime. This would be beneficial for code size too.

Wrong code generation for Top Level Variable Declarations

Given a top level like:

var x = 1;
x = 2;

Prepack turns it into:

var x = undefined;
var x = 1;
var x = 2;

I guess multiple "set" operations is correct but it is not correct to "set" it to undefined first. It would invoke a setter unnecessarily. It is also a bit unnecessary to print it as multiple var so it looks like an error.

Retain specific polyfill branching

Will need to special case specific patterns. Will need to analyse popular polyfills.

  1. Detect branching - ensure it's execution is side effect free. We could do this by having a read-only mode on realm and just execute the branches anyway and throw an error on writes.
  2. Add it to the end of builds.

Reduce number of cycles

flow check --profile indicates that we have a giant cycle involving >211 files.
Flow conceptually concatenates all files involved in a cycle, which causes it to become slow when changing any file in the cycle.
We need to 1) drastically reduce the number of files involved large cycles, and 2) put a mechanism in place to prevent cycles from creeping in again.

Add option to profile Prepack

At least the following sections should be timed:

  • How long does interpretation of the first initialization phase take?
  • How long does speculative initialization of more modules take?
  • How long does first serialization pass take?
  • How long does second serialization pass take?

Implement Destructuring

This isn't really more important than any other ES2015 feature other than that I had to work around this specifically for Node.

Seems like it should be easy. I might look into it if nobody else does.

Reads of intrinsics can happen after they've already been mutated

(function() {
  let old = Array.prototype.forEach;
  Array.prototype.forEach = function() {};
  Array.prototype.forEach = old;
})();

Becomes

(function () {
  var _$0 = Object.defineProperty;
  var _0 = Array.prototype;

  function _1() {}

  _$0(_0, "forEach", {
    enumerable: false,
    configurable: true,
    writable: true,
    value: _1
  });

  _$0(_0, "forEach", {
    enumerable: false,
    configurable: true,
    writable: true,
    value: Array.prototype.forEach
  });
}).call(this);

In the original Prepack reads and writes were ordered in a single queue for this reason so that reads can be done at the right time.

Stack traces only contain call stack, but not location of actual crash

When prepacking the following program



(function() {
  function f() {



    t



  }
  f();
})();

we get an error like this:

t is not defined
ReferenceError
    at repl:13:3
    at repl:3:1

where 3:1 is the beginning of first called function, and 13:3 the beginning of the second function. However, the position within the second function is lost. It should be on top of the stack. Look at Error.js/build to see how the error is constructed. We probably need to pass in an AST node with location information.

Invocation of an abstract function yields residual functions

(function() {
  var o = __abstract('number', 'foo');
  var obj = {};
  function bar(x) {
    if (o > 1) {
      obj.foo = function() { return 1 + x; }
    } else {
      obj.foo = function() { return 2 + x; }
    }
  }
  bar(5);
  window.x = obj.foo();
})();

Prepacks to:

(function () {
  function _3() {
    return 1 + 5;
  }

  function _4() {
    return 2 + 5;
  }

  var _0 = foo > 1 ? _3 : _4;

  var _$0 = _0();

  x = _$0;
}).call(this);

It seems like these functions could just collapse into their values or side-effects?

Should this be done at the abstract value level or some other future pass?

Don't serialize reserved names in helpers

In the object creation helpers that are generated. It is possible to have property names that are invalid identifiers or reserved words.

I don't have a simple repro because I don't yet understand when these helpers are generated but the result is something like this:

  function _2AQ(default, __esModule) {
    return {
      "default": default,
      __esModule: __esModule
    };
  }

Which is invalid code.

Refactor common pattern of throwing of errors.

Currently, the codebase is littered with code of the following pattern:

        throw new ThrowCompletion(
          Construct(realm, realm.intrinsics.SOMEERROR, [new StringValue(realm, SOMETEXT)])
        );

All of these terribly long constructs should be replaced by

throw realm.createErrorThrowCompletion(realm.intrinsics.SOMEERROR, SOMETEXT)

This will make it easier to reason about errors.

Memoize references to global properties early

Currently, when generating code, the serializer assumes that names like "RegExp", "Symbol", "Proxy" have a certain meaning. However, those global properties could have been redefined. When needed, the serializer should obtain references to such global properties via the prelude.

Consider what happens when prepacking the following code:

(function() {
  let re = RegExp;
  RegExp = function() { throw new Error(); }
  RegExp = new re('ab+c');
})();

empty wrongly becomes undefined

Prepack this test to trigger the issue:

(function() {
var c = global.__abstract ? __abstract("boolean", "false") : false;
a = {};
if (c) a.f = a;
})();

inspect = function() { return 'f' in a; }

Add option to omit second serialization pass

We currently do two passes in order compute usage counts for values, and then avoid generating ids for single-use value. However, this makes Prepack slower, and it would be beneficial for quick functional tests to avoid the second pass.

Multi-pass serialization doesn't respect observable object identity in residual function

Prepacking

(function() {
  var a = {x: 4};
  function f() {
    return a;
  }
  function g() {
    return a;
  }
  inspect = function() { return f() === g(); }
})();

yields

(function () {
  function _1() {
    return {
      x: 4
    };
  }

  function _3() {
    return {
      x: 4
    };
  }

  function _0() {
    return _1() === _3();
  }

  inspect = _0;
}).call(this);

Object identity must be preserved if it can be observed by residual functions. This is a concrete issue relating to #422.

Make sure all generated names are unique

The serializer and generator create new names of the form _XXX. However, such names might also appear in residual functions. Prepack needs to make sure that all names are unique. Consider prepacking the following code:

(function() {
  function bar() {
    try {
      return _0.name;
    } catch(e) {
      return "exception";
    }
  }
  inspect = function() { return bar(); }
})();

At the very least, we should detect this and fail. Or better, we make those ids unique which collide with any local variable names.

Serialization of objects with internal slots

Currently, the serializer doesn't properly handle all kinds of objects with internal slots.
Generally, internal slots are properties named "$...".
In serializer.js, look at how it properly handles a "Number" which has the internal slot "$NumberData".
In a similar manner, we need to handle...

  • $StringData => String
  • $BooleanData => Boolean
  • $SymbolData => Symbol (not yet fully supported anyway)
  • $DateValue => Date

Check ObjectValue.js to see what other internal slots might exist. Make the serializer log an error when it comes across any objects that have unsupported internal slots set.

Incorrect Serialization of Functions with Default Arguments

Ok, this rabbit hole was a bit tricky to figure out...

This is a quirk of the spec that was added to better support lazy parser. Basically, you can't make a function that has default arguments ("non simple") go into "use strict" mode.

This is invalid:

function foo(bar = 1) { "use strict"; }

Luckily Babylon will complain if you do this so no code is written this way. But...

It is possible for Prepack to create a mixed mode environment:

(function() {
  function foo() {
    "use strict";
    return function(bar = 1) {
      return bar;
    };
  }
  x = foo();
  y = function() {};
})();

Which turns into this invalid code by flattening the scopes:

(function () {
  function _0(bar = 1) {
    "use strict";

    return bar;
  }

  x = _0;

  function _1() {}

  y = _1;
}).call(this);

Arguably this is something that Babel could handle on its side.

However, in these scenarios Prepack will also generate very bloated code because "use strict" is repeated for every new function. Maybe we should instead group all strict functions into a single "strict" scope?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.