Skip to content

Commit

Permalink
Split out runtime library for ES6 language features.
Browse files Browse the repository at this point in the history
This can be done separately from the library polyfills since they are injected via a different mechanism.  In their place we leave a collection of "require ..." directives, which works fine since nobody cares about the specific contents of es6_runtime.js anymore - only that the result of the compiler injecting it contains everything that's needed.

One upshot of this is that using ES6 language features will no longer pull in the entire polyfill library.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=125810414
  • Loading branch information
shicks authored and blickly committed Jun 27, 2016
1 parent 4738445 commit eb1913f
Show file tree
Hide file tree
Showing 24 changed files with 505 additions and 484 deletions.
5 changes: 3 additions & 2 deletions src/com/google/javascript/jscomp/CompilerOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -1939,8 +1939,9 @@ public Config.JsDocParsing isParseJsDocDocumentation() {
}

/**
* Skip all passes (other than transpilation, if requested). Don't inject es6_runtime.js
* or do any checks/optimizations (this is useful for per-file transpilation).
* Skip all passes (other than transpilation, if requested). Don't inject any
* runtime libraries (unless explicitly requested) or do any checks/optimizations
* (this is useful for per-file transpilation).
*/
public void setSkipNonTranspilationPasses(boolean skipNonTranspilationPasses) {
this.skipNonTranspilationPasses = skipNonTranspilationPasses;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ private void visitArrayPattern(NodeTraversal t, Node arrayPattern, Node parent)
nodeToDetach.detachFromParent();

if (needsRuntime) {
compiler.ensureLibraryInjected("es6_runtime", false);
compiler.ensureLibraryInjected("es6/util/arrayfromiterator", false);
}
compiler.reportCodeChange();
}
Expand Down
2 changes: 1 addition & 1 deletion src/com/google/javascript/jscomp/Es6RewriteGenerators.java
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ private void visitYieldExpr(Node n, Node parent) {
}

private void visitGenerator(Node n, Node parent) {
compiler.ensureLibraryInjected("es6_runtime", false);
compiler.ensureLibraryInjected("es6/symbol", false);
hasTranslatedTry = false;
Node genBlock = compiler.parseSyntheticCode(Joiner.on('\n').join(
"function generatorBody() {",
Expand Down
11 changes: 6 additions & 5 deletions src/com/google/javascript/jscomp/Es6ToEs3Converter.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -89,7 +90,7 @@ public final class Es6ToEs3Converter implements NodeTraversal.Callback, HotSwapC

private static final String ITER_RESULT = "$jscomp$key$";

// These functions are defined in js/es6_runtime.js
// This function is defined in js/es6/util/inherits.js
static final String INHERITS = "$jscomp.inherits";

public Es6ToEs3Converter(AbstractCompiler compiler) {
Expand Down Expand Up @@ -205,7 +206,7 @@ private boolean isGlobalSymbol(NodeTraversal t, Node n) {
* Inserts a call to $jscomp.initSymbol() before {@code n}.
*/
private void initSymbolBefore(Node n) {
compiler.ensureLibraryInjected("es6_runtime", false);
compiler.ensureLibraryInjected("es6/symbol", false);
Node statement = NodeUtil.getEnclosingStatement(n);
Node initSymbol = IR.exprResult(IR.call(NodeUtil.newQName(compiler, "$jscomp.initSymbol")));
statement.getParent().addChildBefore(initSymbol.useSourceInfoFromForTree(statement), statement);
Expand All @@ -218,7 +219,7 @@ private void visitGetprop(NodeTraversal t, Node n) {
return;
}
if (isGlobalSymbol(t, n.getFirstChild())) {
compiler.ensureLibraryInjected("es6_runtime", false);
compiler.ensureLibraryInjected("es6/symbol", false);
Node statement = NodeUtil.getEnclosingStatement(n);
Node init = IR.exprResult(IR.call(NodeUtil.newQName(compiler, "$jscomp.initSymbolIterator")));
statement.getParent().addChildBefore(init.useSourceInfoFromForTree(statement), statement);
Expand Down Expand Up @@ -635,7 +636,7 @@ private void visitClass(final Node classNode, final Node parent) {
NodeUtil.newQName(compiler, metadata.fullClassName),
NodeUtil.newQName(compiler, superClassString));
Node inheritsCall = IR.exprResult(inherits);
compiler.ensureLibraryInjected("es6_runtime", false);
compiler.ensureLibraryInjected("es6/util/inherits", false);

inheritsCall.useSourceInfoIfMissingFromForTree(classNode);
enclosingStatement.getParent().addChildAfter(inheritsCall, enclosingStatement);
Expand Down Expand Up @@ -972,7 +973,7 @@ private static Node arrayFromIterable(AbstractCompiler compiler, Node iterable)

private static Node callEs6RuntimeFunction(
AbstractCompiler compiler, Node iterable, String function) {
compiler.ensureLibraryInjected("es6_runtime", false);
compiler.ensureLibraryInjected("es6/util/" + function.toLowerCase(Locale.US), false);
return IR.call(
NodeUtil.newQName(compiler, "$jscomp." + function),
iterable);
Expand Down
2 changes: 1 addition & 1 deletion src/com/google/javascript/jscomp/NodeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2483,7 +2483,7 @@ static boolean isTryCatchNodeContainer(Node n) {
&& parent.getSecondChild() == n;
}

// TODO(tbreisacher): Add a method for detecting nodes under es6_runtime.js
// TODO(tbreisacher): Add a method for detecting nodes injected as runtime libraries.
static boolean isInSyntheticScript(Node n) {
return n.getSourceFileName() != null && n.getSourceFileName().startsWith(" [synthetic:");
}
Expand Down
53 changes: 0 additions & 53 deletions src/com/google/javascript/jscomp/js/es6/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,59 +21,6 @@
$jscomp.array = $jscomp.array || {};


/**
* Creates an iterator from an array-like, with a transformation function.
* @param {!IArrayLike<INPUT>} array
* @param {function(number, INPUT): OUTPUT} transform
* @return {!IteratorIterable<OUTPUT>}
* @template INPUT, OUTPUT
* @suppress {checkTypes}
*/
$jscomp.iteratorFromArray = function(array, transform) {
$jscomp.initSymbolIterator();
// NOTE: IE8 doesn't support indexing from boxed Strings.
if (array instanceof String) array = array + '';
var i = 0;
var iter = {
next: function() {
if (i < array.length) {
var index = i++;
return {value: transform(index, array[index]), done: false};
}
iter.next = function() { return {done: true, value: void 0}; };
return iter.next();
}
};
iter[Symbol.iterator] = function() { return iter; };
return iter;
};


// TODO(sdh): would be nice to template on the ARRAY type as well,
// so that the third arg type of callback can be refined to be
// exactly the same as the array type, but then there's no way to
// enforce that it must, in fact, be an array.
/**
* Internal implementation of find.
* @param {!IArrayLike<VALUE>} array
* @param {function(this: THIS, VALUE, number, !IArrayLike<VALUE>): *} callback
* @param {THIS} thisArg
* @return {{i: number, v: (VALUE|undefined)}}
* @template THIS, VALUE
*/
$jscomp.findInternal = function(array, callback, thisArg) {
if (array instanceof String) {
array = /** @type {!IArrayLike} */ (String(array));
}
var len = array.length;
for (var i = 0; i < len; i++) {
var value = array[i];
if (callback.call(thisArg, value, i, array)) return {i: i, v: value};
}
return {i: -1, v: void 0};
};


/**
* Creates a new Array from an array-like or iterable object.
*
Expand Down
198 changes: 4 additions & 194 deletions src/com/google/javascript/jscomp/js/es6/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,197 +14,7 @@
* limitations under the License.
*/

/**
* Runtime functions required for transpilation from ES6 to ES3.
*
* @author mattloring@google.com (Matthew Loring)
*/
'require base';
'declare window global';


/**
* @param {!Object} maybeGlobal
* @return {!Object} The global object.
* @suppress {undefinedVars}
*/
$jscomp.getGlobal = function(maybeGlobal) {
return (typeof window != 'undefined' && window === maybeGlobal) ?
maybeGlobal :
(typeof global != 'undefined') ? global : maybeGlobal;
};


// TODO(sdh): This should be typed as "the global object", but there's not
// currently any way to do this in the existing type system.
/**
* The global object. For browsers we could just use `this` but in Node that
* doesn't work.
* @const {?}
*/
$jscomp.global = $jscomp.getGlobal(this);


/**
* Initializes the Symbol function.
* @suppress {reportUnknownTypes}
*/
$jscomp.initSymbol = function() {
if (!$jscomp.global.Symbol) {
$jscomp.global.Symbol = $jscomp.Symbol;
}

// Only need to do this once. All future calls are no-ops.
$jscomp.initSymbol = function() {};
};


/** @private {number} */
$jscomp.symbolCounter_ = 0;


/**
* Produces "symbols" (actually just unique strings).
* @param {string} description
* @return {symbol}
* @suppress {reportUnknownTypes}
*/
$jscomp.Symbol = function(description) {
return /** @type {symbol} */ (
'jscomp_symbol_' + description + ($jscomp.symbolCounter_++));
};


/**
* Initializes Symbol.iterator, if it's not already defined.
* @suppress {reportUnknownTypes}
*/
$jscomp.initSymbolIterator = function() {
$jscomp.initSymbol();
if (!$jscomp.global.Symbol.iterator) {
$jscomp.global.Symbol.iterator = $jscomp.global.Symbol('iterator');
}

// Only need to do this once. All future calls are no-ops.
$jscomp.initSymbolIterator = function() {};
};


/**
* Creates an iterator for the given iterable.
*
* @param {string|!Array<T>|!Iterable<T>|!Iterator<T>|!Arguments<T>} iterable
* @return {!Iterator<T>}
* @template T
* @suppress {reportUnknownTypes}
*/
$jscomp.makeIterator = function(iterable) {
$jscomp.initSymbolIterator();

// NOTE: Disabling typechecking because [] not allowed on @struct.
var iteratorFunction = /** @type {?} */ (iterable)[Symbol.iterator];
if (iteratorFunction) {
return iteratorFunction.call(iterable);
}

var index = 0;
var arr = /** @type {!Array} */ (iterable);
return /** @type {!Iterator} */ ({
next: function() {
if (index < arr.length) {
return {
done: false,
value: arr[index++],
};
} else {
return {done: true};
}
}
});
};


/**
* Copies the values from an Iterator into an Array. The important difference
* between this and $jscomp.arrayFromIterable is that if the iterator's
* next() method has already been called one or more times, this method returns
* only the values that haven't been yielded yet.
* @param {!Iterator<T>} iterator
* @return {!Array<T>}
* @template T
*/
$jscomp.arrayFromIterator = function(iterator) {
var i;
var arr = [];
while (!(i = iterator.next()).done) {
arr.push(i.value);
}
return arr;
};


/**
* Copies the values from an Iterable into an Array.
* @param {string|!Array<T>|!Iterable<T>|!Arguments<T>} iterable
* @return {!Array<T>}
* @template T
*/
$jscomp.arrayFromIterable = function(iterable) {
if (iterable instanceof Array) {
return iterable;
} else {
return $jscomp.arrayFromIterator($jscomp.makeIterator(iterable));
}
};


/**
* Inherit the prototype methods and static methods from one constructor
* into another.
*
* This wires up the prototype chain (like goog.inherits) and copies static
* properties, for ES6-to-ES{3,5} transpilation.
*
* Usage:
* <pre>
* function ParentClass() {}
*
* // Regular method.
* ParentClass.prototype.foo = function(a) {};
*
* // Static method.
* ParentClass.bar = function() {};
*
* function ChildClass() {
* ParentClass.call(this);
* }
* $jscomp.inherits(ChildClass, ParentClass);
*
* var child = new ChildClass();
* child.foo();
* ChildClass.bar(); // Static inheritance.
* </pre>
*
* @param {!Function} childCtor Child class.
* @param {!Function} parentCtor Parent class.
*/
$jscomp.inherits = function(childCtor, parentCtor) {
/** @constructor */
function tempCtor() {}
tempCtor.prototype = parentCtor.prototype;
childCtor.prototype = new tempCtor();
/** @override */
childCtor.prototype.constructor = childCtor;

for (var p in parentCtor) {
if (Object.defineProperties) {
var descriptor = Object.getOwnPropertyDescriptor(parentCtor, p);
if (descriptor) {
Object.defineProperty(childCtor, p, descriptor);
}
} else {
// Pre-ES5 browser. Just copy with an assignment.
childCtor[p] = parentCtor[p];
}
}
};
'require es6/symbol es6/util/arrayfromiterable';
'require es6/util/arrayfromiterator es6/util/inherits';
'require es6/util/iteratorfromarray es6/util/makeiterator';
'require util/checkstringargs util/findinternal';
22 changes: 0 additions & 22 deletions src/com/google/javascript/jscomp/js/es6/string.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,6 @@
$jscomp.string = $jscomp.string || {};


/**
* Throws if the argument is a RegExp, or if thisArg is undefined.
* @param {?} thisArg The 'this' arg, which must be defined.
* @param {*} arg The first argument of the function, which mustn't be a RegExp.
* @param {string} func Name of the function, for reporting.
* @return {string} The thisArg, coerced to a string.
*/
$jscomp.checkStringArgs = function(thisArg, arg, func) {
if (thisArg == null) {
throw new TypeError(
"The 'this' value for String.prototype." + func +
' must not be null or undefined');
}
if (arg instanceof RegExp) {
throw new TypeError(
'First argument to String.prototype.' + func +
' must not be a regular expression');
}
return thisArg + '';
};


/**
* Creates a new string from the given codepoints.
*
Expand Down

0 comments on commit eb1913f

Please sign in to comment.