Page MenuHomec4science

BlockStatement.js
No OneTemporary

File Metadata

Created
Sat, Feb 1, 12:50

BlockStatement.js

import Node from './Node.js';
import Scope from './Scope.js';
import destructure from '../utils/destructure.js';
function isUseStrict(node) {
if (!node) return false;
if (node.type !== 'ExpressionStatement') return false;
if (node.expression.type !== 'Literal') return false;
return node.expression.value === 'use strict';
}
export default class BlockStatement extends Node {
createScope() {
this.parentIsFunction = /Function/.test(this.parent.type);
this.isFunctionBlock = this.parentIsFunction || this.parent.type === 'Root';
this.scope = new Scope({
block: !this.isFunctionBlock,
parent: this.parent.findScope(false),
declare: id => this.createdDeclarations.push(id)
});
if (this.parentIsFunction) {
this.parent.params.forEach(node => {
this.scope.addDeclaration(node, 'param');
});
}
}
initialise(transforms) {
this.thisAlias = null;
this.argumentsAlias = null;
this.defaultParameters = [];
this.createdDeclarations = [];
// normally the scope gets created here, during initialisation,
// but in some cases (e.g. `for` statements), we need to create
// the scope early, as it pertains to both the init block and
// the body of the statement
if (!this.scope) this.createScope();
this.body.forEach(node => node.initialise(transforms));
this.scope.consolidate();
}
findLexicalBoundary() {
if (this.type === 'Program') return this;
if (/^Function/.test(this.parent.type)) return this;
return this.parent.findLexicalBoundary();
}
findScope(functionScope) {
if (functionScope && !this.isFunctionBlock)
return this.parent.findScope(functionScope);
return this.scope;
}
getArgumentsAlias() {
if (!this.argumentsAlias) {
this.argumentsAlias = this.scope.createIdentifier('arguments');
}
return this.argumentsAlias;
}
getArgumentsArrayAlias() {
if (!this.argumentsArrayAlias) {
this.argumentsArrayAlias = this.scope.createIdentifier('argsArray');
}
return this.argumentsArrayAlias;
}
getThisAlias() {
if (!this.thisAlias) {
this.thisAlias = this.scope.createIdentifier('this');
}
return this.thisAlias;
}
getIndentation() {
if (this.indentation === undefined) {
const source = this.program.magicString.original;
const useOuter = this.synthetic || !this.body.length;
let c = useOuter ? this.start : this.body[0].start;
while (c && source[c] !== '\n') c -= 1;
this.indentation = '';
// eslint-disable-next-line no-constant-condition
while (true) {
c += 1;
const char = source[c];
if (char !== ' ' && char !== '\t') break;
this.indentation += char;
}
const indentString = this.program.magicString.getIndentString();
// account for dedented class constructors
let parent = this.parent;
while (parent) {
if (parent.kind === 'constructor' && !parent.parent.parent.superClass) {
this.indentation = this.indentation.replace(indentString, '');
}
parent = parent.parent;
}
if (useOuter) this.indentation += indentString;
}
return this.indentation;
}
transpile(code, transforms) {
const indentation = this.getIndentation();
const introStatementGenerators = [];
if (this.argumentsAlias) {
introStatementGenerators.push((start, prefix, suffix) => {
const assignment = `${prefix}var ${this.argumentsAlias} = arguments${
suffix
}`;
code.appendLeft(start, assignment);
});
}
if (this.thisAlias) {
introStatementGenerators.push((start, prefix, suffix) => {
const assignment = `${prefix}var ${this.thisAlias} = this${suffix}`;
code.appendLeft(start, assignment);
});
}
if (this.argumentsArrayAlias) {
introStatementGenerators.push((start, prefix, suffix) => {
const i = this.scope.createIdentifier('i');
const assignment = `${prefix}var ${i} = arguments.length, ${
this.argumentsArrayAlias
} = Array(${i});\n${indentation}while ( ${i}-- ) ${
this.argumentsArrayAlias
}[${i}] = arguments[${i}]${suffix}`;
code.appendLeft(start, assignment);
});
}
if (/Function/.test(this.parent.type)) {
this.transpileParameters(
this.parent.params,
code,
transforms,
indentation,
introStatementGenerators
);
} else if ('CatchClause' === this.parent.type) {
this.transpileParameters(
[this.parent.param],
code,
transforms,
indentation,
introStatementGenerators
);
}
if (transforms.letConst && this.isFunctionBlock) {
this.transpileBlockScopedIdentifiers(code);
}
super.transpile(code, transforms);
if (this.createdDeclarations.length) {
introStatementGenerators.push((start, prefix, suffix) => {
const assignment = `${prefix}var ${this.createdDeclarations.join(', ')}${suffix}`;
code.appendLeft(start, assignment);
});
}
if (this.synthetic) {
if (this.parent.type === 'ArrowFunctionExpression') {
const expr = this.body[0];
if (introStatementGenerators.length) {
code
.appendLeft(this.start, `{`)
.prependRight(this.end, `${this.parent.getIndentation()}}`);
code.prependRight(expr.start, `\n${indentation}return `);
code.appendLeft(expr.end, `;\n`);
} else if (transforms.arrow) {
code.prependRight(expr.start, `{ return `);
code.appendLeft(expr.end, `; }`);
}
} else if (introStatementGenerators.length) {
code.prependRight(this.start, `{`).appendLeft(this.end, `}`);
}
}
let start;
if (isUseStrict(this.body[0])) {
start = this.body[0].end;
} else if (this.synthetic || this.parent.type === 'Root') {
start = this.start;
} else {
start = this.start + 1;
}
const prefix = `\n${indentation}`;
let suffix = ';';
introStatementGenerators.forEach((fn, i) => {
if (i === introStatementGenerators.length - 1) suffix = `;\n`;
fn(start, prefix, suffix);
});
}
transpileParameters(params, code, transforms, indentation, introStatementGenerators) {
params.forEach(param => {
if (
param.type === 'AssignmentPattern' &&
param.left.type === 'Identifier'
) {
if (transforms.defaultParameter) {
introStatementGenerators.push((start, prefix, suffix) => {
const lhs = `${prefix}if ( ${param.left.name} === void 0 ) ${
param.left.name
}`;
code
.prependRight(param.left.end, lhs)
.move(param.left.end, param.right.end, start)
.appendLeft(param.right.end, suffix);
});
}
} else if (param.type === 'RestElement') {
if (transforms.spreadRest) {
introStatementGenerators.push((start, prefix, suffix) => {
const penultimateParam = params[params.length - 2];
if (penultimateParam) {
code.remove(
penultimateParam ? penultimateParam.end : param.start,
param.end
);
} else {
let start = param.start,
end = param.end; // TODO https://gitlab.com/Rich-Harris/buble/issues/8
while (/\s/.test(code.original[start - 1])) start -= 1;
while (/\s/.test(code.original[end])) end += 1;
code.remove(start, end);
}
const name = param.argument.name;
const len = this.scope.createIdentifier('len');
const count = params.length - 1;
if (count) {
code.prependRight(
start,
`${prefix}var ${name} = [], ${len} = arguments.length - ${
count
};\n${indentation}while ( ${len}-- > 0 ) ${name}[ ${
len
} ] = arguments[ ${len} + ${count} ]${suffix}`
);
} else {
code.prependRight(
start,
`${prefix}var ${name} = [], ${len} = arguments.length;\n${
indentation
}while ( ${len}-- ) ${name}[ ${len} ] = arguments[ ${len} ]${
suffix
}`
);
}
});
}
} else if (param.type !== 'Identifier') {
if (transforms.parameterDestructuring) {
const ref = this.scope.createIdentifier('ref');
destructure(
code,
id => this.scope.createIdentifier(id),
({ name }) => this.scope.resolveName(name),
param,
ref,
false,
introStatementGenerators
);
code.prependRight(param.start, ref);
}
}
});
}
transpileBlockScopedIdentifiers(code) {
Object.keys(this.scope.blockScopedDeclarations).forEach(name => {
const declarations = this.scope.blockScopedDeclarations[name];
for (const declaration of declarations) {
let cont = false; // TODO implement proper continue...
if (declaration.kind === 'for.let') {
// special case
const forStatement = declaration.node.findNearest('ForStatement');
if (forStatement.shouldRewriteAsFunction) {
const outerAlias = this.scope.createIdentifier(name);
const innerAlias = forStatement.reassigned[name]
? this.scope.createIdentifier(name)
: name;
declaration.name = outerAlias;
code.overwrite(
declaration.node.start,
declaration.node.end,
outerAlias,
{ storeName: true }
);
forStatement.aliases[name] = {
outer: outerAlias,
inner: innerAlias
};
for (const identifier of declaration.instances) {
const alias = forStatement.body.contains(identifier)
? innerAlias
: outerAlias;
if (name !== alias) {
code.overwrite(identifier.start, identifier.end, alias, {
storeName: true
});
}
}
cont = true;
}
}
if (!cont) {
const alias = this.scope.createIdentifier(name);
if (name !== alias) {
const declarationParent = declaration.node.parent;
declaration.name = alias;
code.overwrite(
declaration.node.start,
declaration.node.end,
alias,
{ storeName: true }
);
if (declarationParent.type === 'Property' && declarationParent.shorthand) {
declarationParent.shorthand = false;
code.prependLeft(declaration.node.start, `${name}: `);
}
for (const identifier of declaration.instances) {
identifier.rewritten = true;
const identifierParent = identifier.parent;
code.overwrite(identifier.start, identifier.end, alias, {
storeName: true
});
if (identifierParent.type === 'Property' && identifierParent.shorthand) {
identifierParent.shorthand = false;
code.prependLeft(identifier.start, `${name}: `);
}
}
}
}
}
});
}
}

Event Timeline