From 381156863c8841ee159305d6518541f4a6d22f03 Mon Sep 17 00:00:00 2001 From: Erik Arvidsson Date: Sat, 18 Jul 2015 19:13:01 -0700 Subject: [PATCH] Allow the runtime to be imported as modules When using `--modules=commonjs --import-runtime` or `--modules=parse --import-runtime` the generated code will output require/import statements that import the runtime function as a module. This means that if you are compiling to commonjs or es6 modules you can get a minimal runtime and no external runtime file is needed. Fixes #1628 --- Makefile | 4 +- src/Options.js | 2 + .../AsyncGeneratorTransformPass.js | 13 +- .../AsyncGeneratorTransformer.js | 7 +- .../AsyncToGeneratorTransformer.js | 10 +- src/codegeneration/ClassTransformer.js | 36 ++--- .../DestructuringTransformer.js | 7 +- src/codegeneration/ForOnTransformer.js | 3 +- src/codegeneration/GeneratorTransformPass.js | 30 ++-- src/codegeneration/ImportRuntimeTrait.js | 134 ++++++++++++++++++ src/codegeneration/InnerForOnTransformer.js | 6 +- src/codegeneration/JsxTransformer.js | 21 ++- src/codegeneration/ModuleTransformer.js | 10 +- .../ProperTailCallTransformer.js | 19 ++- .../RewriteTailExpressionsTransformer.js | 17 ++- .../SpreadPropertiesTransformer.js | 17 ++- src/codegeneration/SpreadTransformer.js | 12 +- src/codegeneration/SuperTransformer.js | 12 +- src/codegeneration/SymbolTransformer.js | 19 ++- .../TemplateLiteralTransformer.js | 17 ++- .../generator/AsyncTransformer.js | 7 +- .../generator/GeneratorTransformer.js | 9 +- 22 files changed, 313 insertions(+), 99 deletions(-) create mode 100644 src/codegeneration/ImportRuntimeTrait.js diff --git a/Makefile b/Makefile index bec0b24da..6d8b3d514 100644 --- a/Makefile +++ b/Makefile @@ -304,9 +304,9 @@ updateSemver: # unless the package.json has been manually edited. git diff --quiet -- package.json && node build/incrementSemver.js dist/commonjs: bin/traceur.js - ./traceur --dir src/ dist/commonjs/ --modules=commonjs + ./traceur --dir src/ dist/commonjs/ --modules=commonjs --import-runtime -prepublish: bin/traceur.js bin/traceur-runtime.js +prepublish: bin/traceur.js bin/traceur-runtime.js dist/commonjs/ WIKI_OUT = \ test/wiki/CompilingOffline/out/greeter.js diff --git a/src/Options.js b/src/Options.js index 4c3746567..2cf038742 100644 --- a/src/Options.js +++ b/src/Options.js @@ -43,6 +43,7 @@ export const optionsV01 = enumerableOnlyObject({ freeVariableChecker: false, generatorComprehension: false, generators: true, + importRuntime: false, inputSourceMap: false, jsx: false, lowResolutionSourceMap: false, @@ -151,6 +152,7 @@ addFeatureOption('exponentiation', EXPERIMENTAL); addFeatureOption('exportFromExtended', EXPERIMENTAL); addFeatureOption('forOn', EXPERIMENTAL); addFeatureOption('generatorComprehension', EXPERIMENTAL); +addFeatureOption('importRuntime', EXPERIMENTAL); addFeatureOption('jsx', EXPERIMENTAL); addFeatureOption('memberVariables', EXPERIMENTAL); addFeatureOption('require', EXPERIMENTAL); diff --git a/src/codegeneration/AsyncGeneratorTransformPass.js b/src/codegeneration/AsyncGeneratorTransformPass.js index 4b9129787..854e97f2b 100644 --- a/src/codegeneration/AsyncGeneratorTransformPass.js +++ b/src/codegeneration/AsyncGeneratorTransformPass.js @@ -19,6 +19,7 @@ import { FunctionDeclaration, FunctionExpression } from '../syntax/trees/ParseTrees.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import { createBindingIdentifier, createIdentifierExpression as id, @@ -29,7 +30,8 @@ import { parseStatement } from './PlaceholderParser.js'; -export class AsyncGeneratorTransformPass extends TempVarTransformer { +export class AsyncGeneratorTransformPass extends + ImportRuntimeTrait(TempVarTransformer) { // TODO: This class is modelled after GeneratorTransformPass. When the spec // for async generators will have stabilized, it may be an option to merge // this class into GeneratorTransformPass. However, note: This transformer @@ -50,9 +52,10 @@ export class AsyncGeneratorTransformPass extends TempVarTransformer { return super.transformFunctionDeclaration(tree); let nameIdExpression = id(tree.name.identifierToken); - + let initAsyncGeneratorFunction = + this.getRuntimeExpression('initAsyncGeneratorFunction'); let setupPrototypeExpression = parseExpression - `$traceurRuntime.initAsyncGeneratorFunction(${nameIdExpression})`; + `${initAsyncGeneratorFunction}(${nameIdExpression})`; // Function declarations in blocks do not hoist. In that case we add the // variable declaration after the function declaration. @@ -89,8 +92,10 @@ export class AsyncGeneratorTransformPass extends TempVarTransformer { let functionExpression = this.transformFunction_(tree, FunctionExpression, id(name)); + let initAsyncGeneratorFunction = + this.getRuntimeExpression('initAsyncGeneratorFunction'); return parseExpression - `$traceurRuntime.initAsyncGeneratorFunction(${functionExpression })`; + `${initAsyncGeneratorFunction}(${functionExpression })`; } transformFunction_(tree, constructor, nameExpression) { diff --git a/src/codegeneration/AsyncGeneratorTransformer.js b/src/codegeneration/AsyncGeneratorTransformer.js index 3f5127e21..9ea7dfa9d 100644 --- a/src/codegeneration/AsyncGeneratorTransformer.js +++ b/src/codegeneration/AsyncGeneratorTransformer.js @@ -25,6 +25,7 @@ import { createVariableStatement } from './ParseTreeFactory.js'; import {parseStatement} from './PlaceholderParser.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import {TempVarTransformer} from './TempVarTransformer.js'; import { AwaitExpression, @@ -37,7 +38,7 @@ import {ARGUMENTS} from '../syntax/PredefinedName.js'; import {VAR} from '../syntax/TokenType.js'; export class AsyncGeneratorTransformer extends - SkipFunctionsTransformerTrait(TempVarTransformer) { + SkipFunctionsTransformerTrait(ImportRuntimeTrait(TempVarTransformer)) { constructor(identifierGenerator, reporter, options) { super(identifierGenerator, reporter, options); this.variableDeclarations_ = []; @@ -78,8 +79,10 @@ export class AsyncGeneratorTransformer extends createVariableDeclarationList(VAR, this.variableDeclarations_))); } let body = createBlock(tree.statements); + let createAsyncGeneratorInstance = + this.getRuntimeExpression('createAsyncGeneratorInstance'); statements.push(parseStatement ` - return $traceurRuntime.createAsyncGeneratorInstance( + return ${createAsyncGeneratorInstance}( async function (${this.ctx_}) { ${body} }, ${name});`); diff --git a/src/codegeneration/AsyncToGeneratorTransformer.js b/src/codegeneration/AsyncToGeneratorTransformer.js index b268e3730..d737c667b 100644 --- a/src/codegeneration/AsyncToGeneratorTransformer.js +++ b/src/codegeneration/AsyncToGeneratorTransformer.js @@ -21,6 +21,7 @@ import { Method, YieldExpression } from '../syntax/trees/ParseTrees.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import {ParenTrait} from './ParenTrait.js'; import {parseStatement} from './PlaceholderParser.js'; import {TempVarTransformer} from './TempVarTransformer.js'; @@ -30,7 +31,7 @@ import { } from './ParseTreeFactory.js'; export class AsyncToGeneratorTransformer extends - ParenTrait(TempVarTransformer) { + ImportRuntimeTrait(ParenTrait(TempVarTransformer)) { constructor(identifierGenerator, reporter, options) { super(identifierGenerator, reporter, options); this.inAsyncFunction_ = false; @@ -63,7 +64,8 @@ export class AsyncToGeneratorTransformer extends const inAsyncFunction = this.inAsyncFunction_; this.inAsyncFunction_ = true; body = this.transformFunctionBody(body); - body = wrapBodyInSpawn(body); + const spawn = this.getRuntimeExpression('spawn'); + body = wrapBodyInSpawn(body, spawn); this.inAsyncFunction_ = inAsyncFunction; return body; } @@ -91,13 +93,13 @@ export class AsyncToGeneratorTransformer extends } } -function wrapBodyInSpawn(body) { +function wrapBodyInSpawn(body, spawn) { const visitor = new FindArguments(); visitor.visitAny(body); const argExpr = visitor.found ? createIdentifierExpression(ARGUMENTS) : createNullLiteral(); const statement = parseStatement - `return $traceurRuntime.spawn(this, ${argExpr}, function*() { ${body} });` + `return ${spawn}(this, ${argExpr}, function*() { ${body} });` return new FunctionBody(body.location, [statement]) } diff --git a/src/codegeneration/ClassTransformer.js b/src/codegeneration/ClassTransformer.js index 9325d5a21..3308d211f 100644 --- a/src/codegeneration/ClassTransformer.js +++ b/src/codegeneration/ClassTransformer.js @@ -46,6 +46,7 @@ import { } from '../syntax/TokenType.js'; import {MakeStrictTransformer} from './MakeStrictTransformer.js'; import {ParenTrait} from './ParenTrait.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import { createBindingIdentifier, createIdentifierExpression as id, @@ -88,16 +89,6 @@ import { // // The super property and super calls are transformed in the SuperTransformer. -function classCall(func, object, staticObject, superClass) { - if (superClass) { - return parseExpression - `($traceurRuntime.createClass)(${func}, ${object}, ${staticObject}, - ${superClass})`; - } - return parseExpression - `($traceurRuntime.createClass)(${func}, ${object}, ${staticObject})`; -} - function methodNameFromTree(tree) { // COMPUTED_PROPERTY_NAME such as [Symbol.iterator] if (tree.type === COMPUTED_PROPERTY_NAME) { @@ -147,7 +138,7 @@ function removeStaticModifier(tree) { } } -export default function isConstructor(tree) { +function isConstructor(tree) { if (tree.type !== METHOD || tree.isStatic || tree.functionKind !== null) { return false; @@ -157,7 +148,8 @@ export default function isConstructor(tree) { name.literalToken.value === CONSTRUCTOR; } -export class ClassTransformer extends ParenTrait(TempVarTransformer) { +export class ClassTransformer extends + ImportRuntimeTrait(ParenTrait(TempVarTransformer)) { /** * @param {UniqueIdentifierGenerator} identifierGenerator * @param {ErrorReporter} reporter @@ -234,6 +226,7 @@ export class ClassTransformer extends ParenTrait(TempVarTransformer) { constructor.body); let expression; + let createClass = this.getRuntimeExpression('createClass'); if (tree.name) { let functionStatement; let name = tree.name.identifierToken; @@ -249,18 +242,23 @@ export class ClassTransformer extends ParenTrait(TempVarTransformer) { if (superClass) { expression = parseExpression `function($__super) { ${functionStatement}; - return ($traceurRuntime.createClass)(${nameId}, ${protoObject}, - ${staticObject}, $__super); + return (${createClass})(${nameId}, ${protoObject}, + ${staticObject}, $__super); }(${superClass})`; } else { expression = parseExpression `function() { ${functionStatement}; - return ($traceurRuntime.createClass)(${nameId}, ${protoObject}, - ${staticObject}); + return (${createClass})(${nameId}, ${protoObject}, ${staticObject}); }()`; } } else { - expression = classCall(func, protoObject, staticObject, superClass); + if (superClass) { + expression = parseExpression + `(${createClass})(${func}, ${protoObject}, ${staticObject}, ${superClass})`; + } else { + expression = parseExpression + `(${createClass})(${func}, ${protoObject}, ${staticObject})`; + } } return this.makeStrict_(expression); @@ -290,10 +288,12 @@ export class ClassTransformer extends ParenTrait(TempVarTransformer) { } getDefaultConstructor_(tree) { + // TODO(arv): Move this to SuperTransformer. if (tree.superClass) { let name = id(tree.name.identifierToken); + let superConstructor = this.getRuntimeExpression('superConstructor'); return parsePropertyDefinition `constructor() { - $traceurRuntime.superConstructor(${name}).apply(this, arguments) + ${superConstructor}(${name}).apply(this, arguments) }`; } return parsePropertyDefinition `constructor() {}`; diff --git a/src/codegeneration/DestructuringTransformer.js b/src/codegeneration/DestructuringTransformer.js index 4ec9d37bf..2fb508a2c 100644 --- a/src/codegeneration/DestructuringTransformer.js +++ b/src/codegeneration/DestructuringTransformer.js @@ -44,6 +44,7 @@ import { VariableDeclarationList, VariableStatement, } from '../syntax/trees/ParseTrees.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import {TempVarTransformer} from './TempVarTransformer.js'; import { EQUAL, @@ -164,7 +165,8 @@ class VariableDeclarationDesugaring extends Desugaring { * * @see harmony:destructuring */ -export class DestructuringTransformer extends TempVarTransformer { +export class DestructuringTransformer extends + ImportRuntimeTrait(TempVarTransformer) { /** * @param {UniqueIdentifierGenerator} identifierGenerator */ @@ -563,9 +565,10 @@ export class DestructuringTransformer extends TempVarTransformer { continue; } else if (lvalue.isSpreadPatternElement()) { // Rest of the array, for example [x, ...y] = [1, 2, 3] + let iteratorToArray = this.getRuntimeExpression('iteratorToArray'); desugaring.assign( lvalue.lvalue, - parseExpression `$traceurRuntime.iteratorToArray(${iterId})`); + parseExpression `${iteratorToArray}(${iterId})`); } else { if (lvalue.initializer) { initializerFound = true; diff --git a/src/codegeneration/ForOnTransformer.js b/src/codegeneration/ForOnTransformer.js index e25cb5c68..354e70156 100644 --- a/src/codegeneration/ForOnTransformer.js +++ b/src/codegeneration/ForOnTransformer.js @@ -16,6 +16,7 @@ import { FOR_ON_STATEMENT, LABELLED_STATEMENT } from '../syntax/trees/ParseTreeType.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import {TempVarTransformer} from './TempVarTransformer.js'; import {InnerForOnTransformer} from './InnerForOnTransformer.js'; @@ -60,7 +61,7 @@ import {InnerForOnTransformer} from './InnerForOnTransformer.js'; /** * Desugars for-on statement. */ -export class ForOnTransformer extends TempVarTransformer { +export class ForOnTransformer extends ImportRuntimeTrait(TempVarTransformer) { /** * @param {ForOnStatement} original * @return {ParseTree} diff --git a/src/codegeneration/GeneratorTransformPass.js b/src/codegeneration/GeneratorTransformPass.js index 4a21030e1..83e2d6917 100644 --- a/src/codegeneration/GeneratorTransformPass.js +++ b/src/codegeneration/GeneratorTransformPass.js @@ -21,6 +21,7 @@ import { parseStatement } from './PlaceholderParser.js'; import {TempVarTransformer} from './TempVarTransformer.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import {FindInFunctionScope} from './FindInFunctionScope.js'; import { AnonBlock, @@ -48,7 +49,8 @@ function needsTransform(tree, transformOptions) { * This pass just finds function bodies with yields in them and passes them * off to the GeneratorTransformer for the heavy lifting. */ -export class GeneratorTransformPass extends TempVarTransformer { +export class GeneratorTransformPass extends + ImportRuntimeTrait(TempVarTransformer) { /** * @param {UniqueIdentifierGenerator} identifierGenerator * @param {ErrorReporter} reporter @@ -75,9 +77,9 @@ export class GeneratorTransformPass extends TempVarTransformer { transformGeneratorDeclaration_(tree) { let nameIdExpression = id(tree.name.identifierToken); - + let initGeneratorFunction = this.getRuntimeExpression('initGeneratorFunction'); let setupPrototypeExpression = parseExpression - `$traceurRuntime.initGeneratorFunction(${nameIdExpression})`; + `${initGeneratorFunction}(${nameIdExpression})`; // Function declarations in blocks do not hoist. In that case we add the // variable declaration after the function declaration. @@ -124,8 +126,9 @@ export class GeneratorTransformPass extends TempVarTransformer { let functionExpression = this.transformFunction_(tree, FunctionExpression, id(name)); - return parseExpression - `$traceurRuntime.initGeneratorFunction(${functionExpression })`; + let initGeneratorFunction = + this.getRuntimeExpression('initGeneratorFunction'); + return parseExpression `${initGeneratorFunction}(${functionExpression })`; } transformFunction_(tree, constructor, nameExpression) { @@ -141,13 +144,20 @@ export class GeneratorTransformPass extends TempVarTransformer { } if (this.tranformOptions_.generators && tree.isGenerator()) { - body = GeneratorTransformer.transformGeneratorBody( - this.identifierGenerator, this.reporter, this.options, body, - nameExpression); + let transformer = new GeneratorTransformer(this.identifierGenerator, + this.reporter, this.options); + body = transformer.transformGeneratorBody(body, nameExpression); + transformer.requiredNames.forEach((n) => { + this.addImportedName(n); + }); } else if (this.tranformOptions_.asyncFunctions && tree.isAsyncFunction()) { - body = AsyncTransformer.transformAsyncBody( - this.identifierGenerator, this.reporter, this.options, body); + let transformer = new AsyncTransformer(this.identifierGenerator, + this.reporter, this.options); + body = transformer.transformAsyncBody(body, nameExpression); + transformer.requiredNames.forEach((n) => { + this.addImportedName(n); + }); } // The generator has been transformed away. diff --git a/src/codegeneration/ImportRuntimeTrait.js b/src/codegeneration/ImportRuntimeTrait.js new file mode 100644 index 000000000..49a6d273e --- /dev/null +++ b/src/codegeneration/ImportRuntimeTrait.js @@ -0,0 +1,134 @@ +// Copyright 2016 Traceur Authors. +// +// Licensed under the Apache License, Version 2.0 (the 'License'); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an 'AS IS' BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {CONST, VAR} from '../syntax/TokenType.js'; +import { + ImportDeclaration, + ImportedBinding, + ImportSpecifier, + ImportSpecifierSet, + Module, + ModuleSpecifier, + Script, +} from '../syntax/trees/ParseTrees.js'; +import {StringSet} from '../util/StringSet.js'; +import { + createBindingIdentifier, + createIdentifierToken, + createIdentifierExpression, + createMemberExpression, + createStringLiteral, + createStringLiteralToken, + createVariableStatement, +} from './ParseTreeFactory.js'; +import {parseExpression} from './PlaceholderParser.js'; +import {prependStatements} from './PrependStatements.js'; + +function toTempName(name) { + return `$__${name}`; +} + +function getDeclarationType(options) { + return options.parseOptions.blockBinding && + !options.transformOptions.blockBinding ? CONST : VAR; +} + +export default function ImportRuntimeTrait(ParseTreeTransformerClass) { + return class extends ParseTreeTransformerClass { + constructor(...args) { + super(...args); + this.importedNames = new StringSet(); + this._existingImports = new StringSet(); + } + + getRuntimeExpression(name) { + if (this.options.importRuntime) { + this.addImportedName(name); + return createIdentifierExpression(toTempName(name)); + } + return createMemberExpression('$traceurRuntime', name); + } + + get requiredNames() { + return this.importedNames; + } + + addImportedName(name) { + this.importedNames.add(name); + } + + transformScript(tree) { + let transformed = super.transformScript(tree); + if (tree === transformed) { + return tree; + } + + if (!this.options.importRuntime) { + return transformed; + } + + let scriptItemList = this.addRuntimeImports(transformed.scriptItemList); + return new Script(tree.location, scriptItemList, tree.moduleName); + } + + transformModule(tree) { + let transformed = super.transformModule(tree); + if (tree === transformed) { + return tree; + } + + if (!this.options.importRuntime) { + return transformed; + } + + let scriptItemList = this.addRuntimeImports(transformed.scriptItemList); + return new Module(tree.location, scriptItemList, tree.moduleName); + } + + transformImportedBinding(tree) { + // Add seen imports so that we do not add them twice. + this._existingImports.add(tree.binding.getStringValue()); + return super.transformImportedBinding(tree); + } + + _getModuleSpecifier(name) { + let base = 'traceur/dist/commonjs'; + if (this.options.modules === 'parse') { + base = 'traceur/src'; + } + const moduleId = createStringLiteralToken( + `${base}/runtime/modules/${name}.js`); + return new ModuleSpecifier(null, moduleId); + } + + getRuntimeImports() { + return this.importedNames.valuesAsArray().filter( + name => !this._existingImports.has(toTempName(name))).map(name => { + // import {default as $__name} from '.../name.js' + const def = createIdentifierToken('default'); + const binding = new ImportedBinding(null, + createBindingIdentifier(toTempName(name))); + const specifier = new ImportSpecifier(null, binding, def); + return new ImportDeclaration(null, + new ImportSpecifierSet(null, [specifier]), + this._getModuleSpecifier(name)); + }); + } + + addRuntimeImports(scriptItemList) { + let runtimeImports = this.getRuntimeImports(); + return prependStatements(scriptItemList, ...runtimeImports); + } + } +} diff --git a/src/codegeneration/InnerForOnTransformer.js b/src/codegeneration/InnerForOnTransformer.js index d22af5ff6..14835a5d2 100644 --- a/src/codegeneration/InnerForOnTransformer.js +++ b/src/codegeneration/InnerForOnTransformer.js @@ -53,7 +53,7 @@ export class InnerForOnTransformer extends // FnExtractAbruptCompletions. The common code should really be refactored // into an abstract base class. - constructor(tempIdGenerator, labelSet) { + constructor(tempIdGenerator, labelSet, options) { super(); this.idGenerator_ = tempIdGenerator; this.inLoop_ = 0; @@ -68,6 +68,7 @@ export class InnerForOnTransformer extends this.labelSet_.forEach((tree) => { this.parentLabels_.add(tree.name.value); }); + this.options = options; } transform(tree) { @@ -107,11 +108,12 @@ export class InnerForOnTransformer extends return ${this.result_}.v;`)); let switchStatement = createSwitchStatement(this.result_, caseClauses); + let observeForEach = this.idGenerator_.getRuntimeExpression('observeForEach'); let statement = parseStatement ` do { ${createVariableStatement( createVariableDeclarationList(VAR, this.variableDeclarations_))} - await $traceurRuntime.observeForEach( + await ${observeForEach}( ${tree.observable}[Symbol.observer].bind(${tree.observable}), async function (${value}) { var ${this.observer_} = this; diff --git a/src/codegeneration/JsxTransformer.js b/src/codegeneration/JsxTransformer.js index 776bcf02e..f32e75730 100644 --- a/src/codegeneration/JsxTransformer.js +++ b/src/codegeneration/JsxTransformer.js @@ -42,6 +42,7 @@ import { } from './ParseTreeFactory.js'; import {parseExpression} from './PlaceholderParser.js'; import {spreadProperties} from './SpreadPropertiesTransformer.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; /** * Desugars JSX expressions. @@ -63,10 +64,10 @@ import {spreadProperties} from './SpreadPropertiesTransformer.js'; * * myFunc('p', null) */ -export class JsxTransformer extends ParseTreeTransformer { +export class JsxTransformer extends ImportRuntimeTrait(ParseTreeTransformer) { constructor(idGen, reporter, options) { super(); - this.options_ = options; + this.options = options; this.jsxFunction_ = null; } @@ -75,7 +76,7 @@ export class JsxTransformer extends ParseTreeTransformer { // --jsx -> React.createElement(tagName, opts, ...children) // --jsx=a.b.c -> a.b.c(tagName, opts, ...children) if (!this.jsxFunction_) { - let jsx = this.options_.jsx; + let jsx = this.options.jsx; if (typeof jsx === 'string') { this.jsxFunction_ = parseExpression([jsx]); } else { @@ -99,19 +100,15 @@ export class JsxTransformer extends ParseTreeTransformer { return createNullLiteral(); } if (tree.attributes.some(a => a.type === JSX_SPREAD_ATTRIBUTE)) { - return this.createSpreadAttributeExpression_(attrs); + // + // => + // React.createElement('a', + // $traceurRuntime.spreadProperties({b: 'b', c: 'c'}, d, g)) + return spreadProperties(attrs, this); } return createObjectLiteral(attrs); } - createSpreadAttributeExpression_(attrs) { - // - // => - // React.createElement('a', - // $traceurRuntime.spreadProperties({b: 'b', c: 'c'}, d, g)) - return spreadProperties(attrs); - } - transformJsxElementName(tree) { if (tree.names.length === 1) { let {value} = tree.names[0]; diff --git a/src/codegeneration/ModuleTransformer.js b/src/codegeneration/ModuleTransformer.js index 58d2f2d9b..a4723c795 100644 --- a/src/codegeneration/ModuleTransformer.js +++ b/src/codegeneration/ModuleTransformer.js @@ -24,6 +24,7 @@ import { } from '../syntax/trees/ParseTrees.js'; import {DestructuringTransformer} from './DestructuringTransformer.js'; import {DirectExportVisitor} from './module/DirectExportVisitor.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import {ImportSimplifyingTransformer} from './ImportSimplifyingTransformer.js'; import {TempVarTransformer} from './TempVarTransformer.js'; import { @@ -59,6 +60,7 @@ import { parseStatement, parseStatements } from './PlaceholderParser.js'; +import {prependStatements} from './PrependStatements.js'; function removeUseStrictDirectives(tree) { let result = tree.scriptItemList.filter(tree => !tree.isUseStrictDirective()); @@ -71,7 +73,7 @@ class DestructImportVarStatement extends DestructuringTransformer { } } -export class ModuleTransformer extends TempVarTransformer { +export class ModuleTransformer extends ImportRuntimeTrait(TempVarTransformer) { /** * @param {UniqueIdentifierGenerator} identifierGenerator */ @@ -116,8 +118,9 @@ export class ModuleTransformer extends TempVarTransformer { this.pushTempScope(); let statements = this.transformList(tree.scriptItemList); - statements = this.appendExportStatement(statements); + const runtimeImports = this.transformList(this.getRuntimeImports()); + statements = prependStatements(statements, ...runtimeImports); this.popTempScope(); @@ -224,7 +227,8 @@ export class ModuleTransformer extends TempVarTransformer { this.getTempVarNameForModuleSpecifier(moduleSpecifier)); }); let args = createArgumentList([exportObject, ...starIdents]); - return parseExpression `$traceurRuntime.exportStar(${args})`; + const runtime = this.getRuntimeExpression('exportStar'); + return parseExpression `${runtime}(${args})`; } return exportObject; } diff --git a/src/codegeneration/ProperTailCallTransformer.js b/src/codegeneration/ProperTailCallTransformer.js index f2524bf1f..44f0fb5cf 100644 --- a/src/codegeneration/ProperTailCallTransformer.js +++ b/src/codegeneration/ProperTailCallTransformer.js @@ -29,6 +29,7 @@ import { FunctionDeclaration, FunctionExpression, } from '../syntax/trees/ParseTrees.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; // // Example: @@ -49,13 +50,15 @@ import { // }, this, arguments); // }) -export class ProperTailCallTransformer extends TempVarTransformer { +export class ProperTailCallTransformer extends + ImportRuntimeTrait(TempVarTransformer) { // TODO(mnieper): This transformer currently expects that classes and template // literals have already been desugared. Otherwise they are not guaranteed // to have proper tail calls. constructor(identifierGenerator, reporter, options) { super(identifierGenerator, reporter, options); this.inBlock_ = false; + this.options = options; } transformFunctionDeclaration(tree) { @@ -67,8 +70,11 @@ export class ProperTailCallTransformer extends TempVarTransformer { let nameIdExpression = id(tree.name.identifierToken); + const initTailRecursiveFunction = + this.getRuntimeExpression('initTailRecursiveFunction'); + let setupFlagExpression = parseExpression - `$traceurRuntime.initTailRecursiveFunction(${nameIdExpression})`; + `${initTailRecursiveFunction}(${nameIdExpression})`; let funcDecl = this.transformFunction_(tree, FunctionDeclaration); if (funcDecl === tree) { @@ -105,8 +111,10 @@ export class ProperTailCallTransformer extends TempVarTransformer { return tree; } - return parseExpression ` - $traceurRuntime.initTailRecursiveFunction(${functionExpression})`; + const initTailRecursiveFunction = + this.getRuntimeExpression('initTailRecursiveFunction'); + return parseExpression `${ + initTailRecursiveFunction}(${functionExpression})`; } transformFunction_(tree, constructor) { @@ -116,8 +124,9 @@ export class ProperTailCallTransformer extends TempVarTransformer { } let func = id(this.getTempIdentifier()); let innerFunction = createFunctionExpression(tree.parameterList, body); + const call = this.getRuntimeExpression('call'); let outerBody = createFunctionBody(parseStatements ` - return $traceurRuntime.call(${innerFunction}, this, arguments);`); + return ${call}(${innerFunction}, this, arguments);`); return new constructor(tree.location, tree.name, tree.functionKind, tree.parameterList, tree.typeAnnotation, tree.annotations, outerBody); } diff --git a/src/codegeneration/RewriteTailExpressionsTransformer.js b/src/codegeneration/RewriteTailExpressionsTransformer.js index a159cd114..fc7fb7ea5 100644 --- a/src/codegeneration/RewriteTailExpressionsTransformer.js +++ b/src/codegeneration/RewriteTailExpressionsTransformer.js @@ -43,7 +43,7 @@ import { OR } from '../syntax/TokenType.js'; -function createCall(tree, operand, thisArg) { +function createCall(tree, operand, thisArg, importRuntimeTransformer) { let argList = tree.args; // can be null let argArray = argList ? argList.args : []; argArray = argArray.map(arg => { @@ -52,8 +52,10 @@ function createCall(tree, operand, thisArg) { } return arg; }); + const continuation = + importRuntimeTransformer.getRuntimeExpression('continuation'); return new CallExpression(tree.location, - createMemberExpression('$traceurRuntime', 'continuation'), + continuation, new ArgumentList(argList ? argList.location : null, [operand, thisArg, createArrayLiteral(argArray)])); } @@ -86,7 +88,8 @@ export class RewriteTailExpressionsTransformer extends ParseTreeTransformer { } switch (operand.type) { case IDENTIFIER_EXPRESSION: - return createCall(tree, operand, createNullLiteral()); + return createCall(tree, operand, createNullLiteral(), + this.bodyTransformer_); case MEMBER_EXPRESSION: case MEMBER_LOOKUP_EXPRESSION: return this.transformMemberExpressionCall_(tree, operand) @@ -115,9 +118,9 @@ export class RewriteTailExpressionsTransformer extends ParseTreeTransformer { } if (assignment) { return createParenExpression(createCommaExpression([assignment, - createCall(tree, operand, thisArg)])); + createCall(tree, operand, thisArg, this.bodyTransformer_)])); } else { - return createCall(tree, operand, thisArg); + return createCall(tree, operand, thisArg, this.bodyTransformer_); } } @@ -144,8 +147,8 @@ export class RewriteTailExpressionsTransformer extends ParseTreeTransformer { } transformNewExpression(tree) { - return createCall(tree, createMemberExpression('$traceurRuntime', 'construct'), - tree.operand); + const construct = this.bodyTransformer_.getRuntimeExpression('construct'); + return createCall(tree, construct, tree.operand, this.bodyTransformer_); } transformArrayLiteral(tree) {return tree;} diff --git a/src/codegeneration/SpreadPropertiesTransformer.js b/src/codegeneration/SpreadPropertiesTransformer.js index 02a599f8f..d6acbb7f5 100644 --- a/src/codegeneration/SpreadPropertiesTransformer.js +++ b/src/codegeneration/SpreadPropertiesTransformer.js @@ -15,6 +15,7 @@ import {SPREAD_EXPRESSION} from '../syntax/trees/ParseTreeType.js'; import {createObjectLiteral, createArgumentList} from './ParseTreeFactory.js'; import {parseExpression} from './PlaceholderParser.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import {ParseTreeTransformer} from './ParseTreeTransformer.js'; function hasSpread(trees) { @@ -30,18 +31,24 @@ function hasSpread(trees) { * => * $spreadProperties({a}, b, {c}, d) */ -export class SpreadPropertiesTransformer extends ParseTreeTransformer { +export class SpreadPropertiesTransformer extends + ImportRuntimeTrait(ParseTreeTransformer) { + constructor(identifierGenerator, reporter, options) { + super(identifierGenerator, reporter, options); + this.options = options; + } + transformObjectLiteral(tree) { if (!hasSpread(tree.propertyNameAndValues)) { return super.transformObjectLiteral(tree); } const properties = this.transformList(tree.propertyNameAndValues); - return spreadProperties(properties); + return spreadProperties(properties, this); } } -export function spreadProperties(properties) { +export function spreadProperties(properties, self) { // Accummulate consecutive properties into a single js property. let args = []; let accummulatedProps = null; @@ -63,6 +70,6 @@ export function spreadProperties(properties) { if (accummulatedProps) { args.push(createObjectLiteral(accummulatedProps)); } - return parseExpression `$traceurRuntime.spreadProperties(${ - createArgumentList(args)})`; + const runtime = self.getRuntimeExpression('spreadProperties'); + return parseExpression `${runtime}(${createArgumentList(args)})`; } diff --git a/src/codegeneration/SpreadTransformer.js b/src/codegeneration/SpreadTransformer.js index 6b5d3cb2e..3f07abbde 100644 --- a/src/codegeneration/SpreadTransformer.js +++ b/src/codegeneration/SpreadTransformer.js @@ -23,7 +23,9 @@ import { MEMBER_LOOKUP_EXPRESSION, SPREAD_EXPRESSION } from '../syntax/trees/ParseTreeType.js'; +import {Script} from '../syntax/trees/ParseTrees.js'; import {TempVarTransformer} from './TempVarTransformer.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import { createArgumentList, createArrayLiteral, @@ -39,8 +41,10 @@ import { createVoid0 } from './ParseTreeFactory.js'; import { - parseExpression + parseExpression, + parseStatement, } from './PlaceholderParser.js'; +import {prependStatements} from './PrependStatements.js'; function hasSpreadMember(trees) { return trees.some((tree) => tree && tree.type === SPREAD_EXPRESSION); @@ -52,7 +56,7 @@ function hasSpreadMember(trees) { * * @see harmony:spread */ -export class SpreadTransformer extends TempVarTransformer { +export class SpreadTransformer extends ImportRuntimeTrait(TempVarTransformer) { /** * Creates an expression that results in an array where all the elements are * spread. @@ -91,8 +95,8 @@ export class SpreadTransformer extends TempVarTransformer { if (lastArray) args.push(createArrayLiteral(lastArray)); - return parseExpression - `$traceurRuntime.spread(${createArgumentList(args)})`; + const spread = this.getRuntimeExpression('spread'); + return parseExpression `${spread}(${createArgumentList(args)})`; } desugarCallSpread_(tree) { diff --git a/src/codegeneration/SuperTransformer.js b/src/codegeneration/SuperTransformer.js index 51a1269e9..04fb8cd18 100644 --- a/src/codegeneration/SuperTransformer.js +++ b/src/codegeneration/SuperTransformer.js @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import {TempVarTransformer} from './TempVarTransformer.js'; import { ArgumentList, @@ -138,7 +139,7 @@ class PrototypeState extends State { * } * } */ -export class SuperTransformer extends TempVarTransformer { +export class SuperTransformer extends ImportRuntimeTrait(TempVarTransformer) { constructor(identifierGenerator, reporter, options) { super(identifierGenerator, reporter, options); // Pushing onto this stack is done in pairs. For classes we push one state @@ -308,7 +309,8 @@ export class SuperTransformer extends TempVarTransformer { transformMemberShared_(name) { let {home} = this.peekState(); - return parseExpression `$traceurRuntime.superGet(this, ${home}, ${name})`; + let superGet = this.getRuntimeExpression('superGet'); + return parseExpression `${superGet}(this, ${home}, ${name})`; } /** @@ -337,7 +339,8 @@ export class SuperTransformer extends TempVarTransformer { createThisExpression(), ...args.args ]); let {home} = this.stateStack_[this.stateStack_.length - 2]; - operand = parseExpression `$traceurRuntime.superConstructor(${home})`; + let superConstructor = this.getRuntimeExpression('superConstructor'); + operand = parseExpression `${superConstructor}(${home})`; } else if (hasSuperMemberExpression(tree.operand)) { // super.x(args) operand = this.transformAny(tree.operand); @@ -367,8 +370,9 @@ export class SuperTransformer extends TempVarTransformer { let right = this.transformAny(tree.right); let {home} = this.peekState(); + let superSet = this.getRuntimeExpression('superSet'); return parseExpression - `$traceurRuntime.superSet(this, ${home}, ${name}, ${right})`; + `${superSet}(this, ${home}, ${name}, ${right})`; } return super.transformBinaryExpression(tree); diff --git a/src/codegeneration/SymbolTransformer.js b/src/codegeneration/SymbolTransformer.js index f9e3971f3..4a5fdd2ff 100644 --- a/src/codegeneration/SymbolTransformer.js +++ b/src/codegeneration/SymbolTransformer.js @@ -22,6 +22,7 @@ import { UNARY_EXPRESSION, } from '../syntax/trees/ParseTreeType.js'; import {ParseTreeTransformer} from './ParseTreeTransformer.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import { EQUAL_EQUAL, EQUAL_EQUAL_EQUAL, @@ -63,7 +64,20 @@ function isSafeTypeofString(tree) { * This transformer transforms typeof expressions to return 'symbol' when * symbols have been shimmed. */ -export class SymbolTransformer extends ParseTreeTransformer { +export class SymbolTransformer extends + ImportRuntimeTrait(ParseTreeTransformer) { + /** + * @param {UniqueIdentifierGenerator} identifierGenerator + * @param {ErrorReporter} reporter + * @param {Options} options + */ + constructor(identifierGenerator, reporter, options) { + super(); + this.identifierGenerator = identifierGenerator; + this.reporter = reporter; + this.options = options; + } + /** * Helper for the case where we only want to transform the operand of * the typeof expression. @@ -114,6 +128,7 @@ export class SymbolTransformer extends ParseTreeTransformer { } getRuntimeTypeof(operand) { - return parseExpression `$traceurRuntime.typeof(${operand})`; + let typeOf = this.getRuntimeExpression('typeof'); + return parseExpression `${typeOf}(${operand})`; } } diff --git a/src/codegeneration/TemplateLiteralTransformer.js b/src/codegeneration/TemplateLiteralTransformer.js index ecdac1218..52c9568d8 100644 --- a/src/codegeneration/TemplateLiteralTransformer.js +++ b/src/codegeneration/TemplateLiteralTransformer.js @@ -26,6 +26,7 @@ import { import {LiteralToken} from '../syntax/LiteralToken.js'; import {ParenTrait} from './ParenTrait.js'; import {ParseTreeTransformer} from './ParseTreeTransformer.js'; +import ImportRuntimeTrait from './ImportRuntimeTrait.js'; import { PERCENT, PLUS, @@ -63,7 +64,7 @@ function createStringLiteralExpression(loc, str) { * @param {Array} elements * @return {ParseTree} */ -function createGetTemplateObject(elements) { +function createGetTemplateObject(elements, getTemplateObject) { let cooked = []; let raw = []; let same = true; @@ -93,7 +94,7 @@ function createGetTemplateObject(elements) { } return createCallExpression( - createMemberExpression('$traceurRuntime', 'getTemplateObject'), + getTemplateObject, createArgumentList(args)); } @@ -203,7 +204,13 @@ function toCookedString(s) { } export class TemplateLiteralTransformer extends - ParenTrait(ParseTreeTransformer) { + ImportRuntimeTrait(ParenTrait(ParseTreeTransformer)) { + + constructor(identifierGenerator, reporter, options) { + super(); + this.options = options; + } + transformTemplateLiteralExpression(tree) { if (!tree.operand) { return this.createDefaultTemplateLiteral(tree); @@ -211,8 +218,8 @@ export class TemplateLiteralTransformer extends let operand = this.transformAny(tree.operand); let elements = tree.elements; - - let args = [createGetTemplateObject(tree.elements)]; + let getTemplateObject = this.getRuntimeExpression('getTemplateObject'); + let args = [createGetTemplateObject(tree.elements, getTemplateObject)]; for (let i = 1; i < elements.length; i += 2) { args.push(this.transformAny(elements[i])); } diff --git a/src/codegeneration/generator/AsyncTransformer.js b/src/codegeneration/generator/AsyncTransformer.js index 2cffab1c7..ee26b9055 100644 --- a/src/codegeneration/generator/AsyncTransformer.js +++ b/src/codegeneration/generator/AsyncTransformer.js @@ -20,6 +20,7 @@ import { import {CPSTransformer} from './CPSTransformer.js'; import {EndState} from './EndState.js'; import {FallThroughState} from './FallThroughState.js'; +import ImportRuntimeTrait from '../ImportRuntimeTrait.js'; import { AWAIT_EXPRESSION, BINARY_EXPRESSION, @@ -67,7 +68,7 @@ function scopeContainsAwait(tree) { * return $traceurRuntime.asyncWrap(machineFunction); * } */ -export class AsyncTransformer extends CPSTransformer { +export class AsyncTransformer extends ImportRuntimeTrait(CPSTransformer) { expressionNeedsStateMachine(tree) { if (tree === null) @@ -213,8 +214,8 @@ export class AsyncTransformer extends CPSTransformer { * @return {FunctionBody} */ transformAsyncBody(tree) { - let runtimeFunction = parseExpression `$traceurRuntime.asyncWrap`; - return this.transformCpsFunctionBody(tree, runtimeFunction); + let asyncWrap = this.getRuntimeExpression('asyncWrap'); + return this.transformCpsFunctionBody(tree, asyncWrap); } /** diff --git a/src/codegeneration/generator/GeneratorTransformer.js b/src/codegeneration/generator/GeneratorTransformer.js index 1e3b4b777..905559d7b 100644 --- a/src/codegeneration/generator/GeneratorTransformer.js +++ b/src/codegeneration/generator/GeneratorTransformer.js @@ -23,6 +23,7 @@ import { } from '../../syntax/trees/ParseTrees.js' import {FindInFunctionScope} from '../FindInFunctionScope.js' import {ReturnState} from './ReturnState.js'; +import ImportRuntimeTrait from '../ImportRuntimeTrait.js'; import {YieldState} from './YieldState.js'; import { createIdentifierExpression as id, @@ -69,7 +70,7 @@ function scopeContainsYield(tree) { * return $traceurRuntime.createGeneratorInstance(machineFunction); * } */ -export class GeneratorTransformer extends CPSTransformer { +export class GeneratorTransformer extends ImportRuntimeTrait(CPSTransformer) { constructor(identifierGenerator, reporter, options) { super(identifierGenerator, reporter, options); @@ -295,9 +296,9 @@ export class GeneratorTransformer extends CPSTransformer { * @return {FunctionBody} */ transformGeneratorBody(tree, name) { - let runtimeFunction = - parseExpression `$traceurRuntime.createGeneratorInstance`; - return this.transformCpsFunctionBody(tree, runtimeFunction, name); + let createGeneratorInstance = + this.getRuntimeExpression('createGeneratorInstance'); + return this.transformCpsFunctionBody(tree, createGeneratorInstance, name); } /**