Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ ES6_SOURCES= \
passes/desugar-templates.js \
passes/desugar-update-assignments.js \
passes/eq-idioms.js \
passes/flatten-decls.js \
passes/func-decls-to-vars.js \
passes/gather-imports.js \
passes/hoist-func-decls.js \
Expand Down
2 changes: 2 additions & 0 deletions lib/ast-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const ArrayExpression = 'ArrayExpression';
export const ArrayPattern = 'ArrayPattern';
export const ArrowFunctionExpression = 'ArrowFunctionExpression';
export const AssignmentExpression = 'AssignmentExpression';
export const AssignmentPattern = 'AssignmentPattern';
export const BinaryExpression = 'BinaryExpression';
export const BlockStatement = 'BlockStatement';
export const BreakStatement = 'BreakStatement';
Expand Down Expand Up @@ -105,6 +106,7 @@ export function arrayExpression (els = []) { return { type: ArrayExpressio
export function arrowFunctionExpression (params, body, defaults=[], expression=false) { return { type: ArrowFunctionExpression, params: params.map(isast), defaults: defaults.map(isast), body: isast(body), expression: expression }; };

export function assignmentExpression (l, op, r) { return { type: AssignmentExpression, operator: op, left: isast(l), right: isast(r) }; };
export function assignmentPattern (l, op, r) { return { type: AssignmentPattern, left: isast(l), right: isast(r) }; };
export function binaryExpression (l, op, r) { return { type: BinaryExpression, operator: op, left: isast(l), right: isast(r) }; };
export function blockStatement (stmts = [], loc=null) { return { type: BlockStatement, body: stmts.map(isast), loc: loc }; };
export function breakStatement (label) { return { type: BreakStatement, label: isast(label) }; };
Expand Down
4 changes: 3 additions & 1 deletion lib/closure-conversion.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { DesugarForOf } from './passes/desugar-for-of';
import { DesugarSpread } from './passes/desugar-spread';
import { DesugarMetaProperties } from './passes/desugar-metaproperties';
import { HoistFuncDecls } from './passes/hoist-func-decls';
import { FlattenDecls } from './passes/flatten-decls';
import { FuncDeclsToVars } from './passes/func-decls-to-vars';
import { DesugarLetLoopVars } from './passes/desugar-let-loopvars';
import { HoistVars } from './passes/hoist-vars';
Expand All @@ -40,16 +41,17 @@ const enable_cfa2 = false;
const enable_hoist_func_decls_pass = true;

const passes = [
FlattenDecls,
DesugarImportExport,
DesugarClasses,
DesugarRestParameters,
DesugarDestructuring,
DesugarUpdateAssignments,
DesugarTemplates,
DesugarGeneratorFunctions,
DesugarArrowFunctions,
DesugarDefaults,
DesugarForOf,
DesugarDestructuring,
DesugarSpread,
DesugarMetaProperties,
enable_hoist_func_decls_pass ? HoistFuncDecls : null,
Expand Down
1 change: 1 addition & 0 deletions lib/common-ids.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,6 @@ export const getNewTarget_id = identifier("%getNewTarget");
export const setConstructorKindDerived_id = identifier("%setConstructorKindDerived");
export const setConstructorKindBase_id = identifier("%setConstructorKindBase");
export const createIteratorWrapper_id = identifier("%createIteratorWrapper");
export const closeIteratorWrapper_id = identifier("%closeIteratorWrapper");
export const getNextValue_id = identifier("getNextValue");
export const getRest_id = identifier("getRest");
21 changes: 7 additions & 14 deletions lib/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ class LLVMIRVisitor extends TreeVisitor {
arrayFromSpread: { value: this.handleArrayFromSpread },
argPresent: { value: this.handleArgPresent },
createIterResult: { value: this.handleCreateIterResult },
createIteratorWrapper: { value: this.handleCreateIteratorWrapper }
createIteratorWrapper: { value: this.handleCreateIteratorWrapper },
closeIteratorWrapper: { value: this.handleCloseIteratorWrapper }
});

this.opencode_intrinsics = {
Expand Down Expand Up @@ -987,11 +988,6 @@ class LLVMIRVisitor extends TreeVisitor {
// save off the insert point so we can get back to it after generating this function
let insertBlock = ir.getInsertBlock();

for (let param of n.formal_params) {
if (param.type !== b.Identifier)
throw new Error("formal parameters should only be identifiers by this point");
}

// XXX this methods needs to be augmented so that we can pass actual types (or the builtin args need
// to be reflected in jsllvm.cpp too). maybe we can pass the names to this method and it can do it all
// there?
Expand Down Expand Up @@ -1031,14 +1027,6 @@ class LLVMIRVisitor extends TreeVisitor {
new_scope.set(param.name, alloca);
allocas.push(alloca);
}

// now create allocas for the formal parameters
let first_formal_index = allocas.length;
for (let param of n.formal_params) {
let alloca = this.createAlloca(this.currentFunction, types.EjsValue, `local_${param.name}`);
new_scope.set(param.name, alloca);
allocas.push(alloca);
}

debug.log ( () => {
allocas.map( (alloca) => `alloca ${alloca}`).join('\n');
Expand Down Expand Up @@ -2574,6 +2562,11 @@ class LLVMIRVisitor extends TreeVisitor {
let iter = this.visit(exp.arguments[0]);
return this.createCall(this.ejs_runtime.iterator_wrapper_new, [iter], "iter_wrapper");
}

handleCloseIteratorWrapper (exp) {
let iter = this.visit(exp.arguments[0]);
return this.createCall(this.ejs_runtime.iterator_wrapper_close, [iter], "");
}
}

class AddFunctionsVisitor extends TreeVisitor {
Expand Down
19 changes: 19 additions & 0 deletions lib/node-visitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class TreeVisitor {
case b.ArrayPattern: rv = this.visitArrayPattern(n, ...args); break;
case b.ArrowFunctionExpression: rv = this.visitArrowFunctionExpression(n, ...args); break;
case b.AssignmentExpression: rv = this.visitAssignmentExpression(n, ...args); break;
case b.AssignmentPattern: rv = this.visitAssignmentPattern(n, ...args); break;
case b.BinaryExpression: rv = this.visitBinaryExpression(n, ...args); break;
case b.BlockStatement: rv = this.visitBlock(n, ...args); break;
case b.BreakStatement: rv = this.visitBreak(n, ...args); break;
Expand Down Expand Up @@ -81,6 +82,8 @@ export class TreeVisitor {
case b.IfStatement: rv = this.visitIf(n, ...args); break;
case b.ImportDeclaration: rv = this.visitImportDeclaration(n, ...args); break;
case b.ImportSpecifier: rv = this.visitImportSpecifier(n, ...args); break;
case b.ImportDefaultSpecifier: rv = this.visitImportDefaultSpecifier(n, ...args); break;
case b.ImportNamespaceSpecifier: rv = this.visitImportNamespaceSpecifier(n, ...args); break;
case b.LabeledStatement: rv = this.visitLabeledStatement(n, ...args); break;
case b.Literal: rv = this.visitLiteral(n, ...args); break;
case b.LogicalExpression: rv = this.visitLogicalExpression(n, ...args); break;
Expand Down Expand Up @@ -279,6 +282,12 @@ export class TreeVisitor {
n.right = this.visit(n.right, ...args);
return n;
}

visitAssignmentPattern (n, ...args) {
n.left = this.visit(n.left, ...args);
n.right = this.visit(n.right, ...args);
return n;
}

visitConditionalExpression (n, ...args) {
n.test = this.visit(n.test, ...args);
Expand Down Expand Up @@ -419,6 +428,16 @@ export class TreeVisitor {
return n;
}

visitImportDefaultSpecifier (n, ...args) {
n.local = this.visit(n.local, ...args);
return n;
}

visitImportNamespaceSpecifier (n, ...args) {
n.local = this.visit(n.local, ...args);
return n;
}

visitArrayPattern (n, ...args) {
n.elements = this.visitArrayKeep(n.elements, ...args);
return n;
Expand Down
4 changes: 2 additions & 2 deletions lib/passes/desugar-defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
// to:
//
// function (a, b) {
// a = %argPresent(0) ? %getArg(0) : undefined;
// b = %argPresent(1) ? %getArg(1) : a;
// let a = %argPresent(0) ? %getArg(0) : undefined;
// let b = %argPresent(1) ? %getArg(1) : a;
// }
//

Expand Down
132 changes: 47 additions & 85 deletions lib/passes/desugar-destructuring.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import { startGenerator, intrinsic } from '../echo-util';
import { TransformPass } from '../node-visitor';
import * as b from '../ast-builder';
import * as escodegen from '../../external-deps/escodegen/escodegen-es6';
import { reportError, reportWarning } from '../errors';
import { Symbol_id, iterator_id, value_id, next_id, done_id,
createIteratorWrapper_id,
closeIteratorWrapper_id,
getNextValue_id,
getRest_id } from '../common-ids';

Expand All @@ -30,6 +32,11 @@ function createObjectPatternBindings (id, pattern, bindings) {
else
bindings.push({ key: prop.value, value: memberexp });
}
else if (prop.value.type === b.AssignmentPattern) {
let default_id = fresh();
bindings.push({ key: default_id, value: memberexp });
bindings.push({ key: prop.value.left, value: b.conditionalExpression(default_id, default_id, prop.value.right) });
}
else if (prop.value.type === b.ObjectPattern) {
bindings.push({ key: prop.key, value: memberexp });

Expand All @@ -47,7 +54,7 @@ function createObjectPatternBindings (id, pattern, bindings) {
}

function createArrayPatternBindingsUsingIterator(id, pattern, bindings) {
let seen_spread = false;
let seen_rest = false;

// first off we create an iterator and wrapper for the rhs
let iter_id = fresh();
Expand All @@ -56,16 +63,21 @@ function createArrayPatternBindingsUsingIterator(id, pattern, bindings) {
bindings.push({ key: wrapper_id, value: intrinsic(createIteratorWrapper_id, [iter_id]), need_decl: true });

for (let el of pattern.elements) {
if (seen_spread)
reportError(SyntaxError, "elements after spread element in array pattern", el.loc);
if (seen_rest)
reportError(SyntaxError, "elements after rest element in array pattern", el.loc);

if (el == null) {
bindings.push({ key: fresh() /*unused*/, value: b.callExpression(b.memberExpression(wrapper_id, getNextValue_id), []) });
}
else if (el.type == b.Identifier) {
else if (el.type === b.Identifier) {
bindings.push({ key: el, value: b.callExpression(b.memberExpression(wrapper_id, getNextValue_id), []) });
}
else if (el.type == b.ObjectPattern) {
else if (el.type === b.AssignmentPattern) {
let default_id = fresh();
bindings.push({ key: default_id, value: b.callExpression(b.memberExpression(wrapper_id, getNextValue_id), []) });
bindings.push({ key: el.left, value: b.conditionalExpression(default_id, default_id, el.right) });
}
else if (el.type === b.ObjectPattern) {
let p_id = fresh();

bindings.push({ key: p_id, value: b.callExpression(b.memberExpression(wrapper_id, getNextValue_id), []) });
Expand All @@ -79,61 +91,19 @@ function createArrayPatternBindingsUsingIterator(id, pattern, bindings) {

createArrayPatternBindingsUsingIterator(p_id, el, bindings);
}
else if (el.type === b.SpreadElement) {
else if (el.type === b.RestElement) {
bindings.push({ key: el.argument, value: b.callExpression(b.memberExpression(wrapper_id, getRest_id), []) });
seen_spread = true;
seen_rest = true;
}
else
throw new Error(`createArrayPatternBindingsUsingIterator ${el.type}`);
}
}

function createArrayPatternBindings (id, pattern, bindings) {
let el_num = 0;
let seen_spread = false;
for (let el of pattern.elements) {
let memberexp = b.memberExpression(id, b.literal(el_num), true);

if (seen_spread)
reportError(SyntaxError, "elements after spread element in array pattern", el.loc);

if (el === null) {
// happens when there's a hole in the array pattern, like var [a,,b] = [1,2,3];
// we just skip that index and continue
el_num += 1;
continue;
}

if (el.type === b.Identifier) {
bindings.push({ key: el, value: memberexp });
}
else if (el.type === b.ObjectPattern) {
let p_id = fresh();

bindings.push({ key: p_id, value: memberexp });

createObjectPatternBindings(p_id, el, bindings);
}
else if (el.type === b.ArrayPattern) {
let p_id = fresh();

bindings.push({ key: p_id, value: memberexp });

createArrayPatternBindings(p_id, el, bindings);
}
else if (el.type === b.SpreadElement) {
bindings.push({ key: el.argument, value: b.callExpression(b.memberExpression(id, b.identifier("slice"))) });
seen_spread = true;
}
else
throw new Error(`createArrayPatternBindings ${el.type}`);
el_num += 1;
}
}


export class DesugarDestructuring extends TransformPass {
visitFunction (n) {
return super.visitFunction(n);

// we visit the formal parameters directly, rewriting
// them as tmp arg names and adding 'let' decls for the
// pattern identifiers at the top of the function's
Expand All @@ -145,24 +115,20 @@ export class DesugarDestructuring extends TransformPass {
if (ptype === b.ObjectPattern) {
let p_id = fresh();
new_params.push(p_id);
let new_decl = b.letDeclaration();
let bindings = [];
createObjectPatternBindings(p_id, p, bindings);
for (let binding of bindings) {
new_decl.declarations.push(b.variableDeclarator(binding.key, binding.value));
new_decls.push(b.letDeclaration(binding.key, binding.value));
}
new_decls.push(new_decl);
}
else if (ptype === b.ArrayPattern) {
let p_id = fresh();
new_params.push(p_id);
let new_decl = b.letDeclaration();
let bindings = [];
createArrayPatternBindingsUsingIterator(p_id, p, bindings);
for (let binding of bindings) {
new_decl.declarations.push(b.variableDeclarator(binding.key, binding.value));
new_decls.push(b.letDeclaration(binding.key, binding.value));
}
new_decls.push(new_decl);
}
else if (ptype === b.Identifier) {
// we just pass this along
Expand All @@ -180,38 +146,34 @@ export class DesugarDestructuring extends TransformPass {
}

visitVariableDeclaration (n) {
let decls = [];
let decl = n.declarations[0];

for (let decl of n.declarations) {
if (decl.id.type === b.ObjectPattern) {
let obj_tmp_id = fresh();
let bindings = [];
decls.push(b.variableDeclarator(obj_tmp_id, this.visit(decl.init)));
createObjectPatternBindings(obj_tmp_id, decl.id, bindings);
for (let binding of bindings) {
decls.push(b.variableDeclarator(binding.key, binding.value));
}
}
else if (decl.id.type === b.ArrayPattern) {
// create a fresh tmp and declare it
let array_tmp_id = fresh();
let bindings = [];
decls.push(b.variableDeclarator(array_tmp_id, this.visit(decl.init)));
createArrayPatternBindingsUsingIterator(array_tmp_id, decl.id, bindings);
for (let binding of bindings) {
decls.push(b.variableDeclarator(binding.key, binding.value));
}
}
else if (decl.id.type === b.Identifier) {
decl.init = this.visit(decl.init);
decls.push(decl);
if (decl.id.type === b.ObjectPattern) {
let decls = [];
let obj_tmp_id = fresh();
let bindings = [];
decls.push(b.variableDeclaration('let', obj_tmp_id, this.visit(decl.init)));
createObjectPatternBindings(obj_tmp_id, decl.id, bindings);
for (let binding of bindings) {
decls.push(b.variableDeclaration(n.kind, binding.key, binding.value));
}
else {
reportError(Error, `unhandled type of variable declaration in DesugarDestructuring ${decl.id.type}`, this.filename, n.loc);
return decls;
}
else if (decl.id.type === b.ArrayPattern) {
let decls = [];
// create a fresh tmp and declare it
let array_tmp_id = fresh();
let bindings = [];
decls.push(b.variableDeclaration('let', array_tmp_id, this.visit(decl.init)));
createArrayPatternBindingsUsingIterator(array_tmp_id, decl.id, bindings);
for (let binding of bindings) {
decls.push(b.variableDeclaration(n.kind, binding.key, binding.value));
}
return decls;
}
else {
return super.visitVariableDeclaration(n);
}
n.declarations = decls;
return n;
}

visitAssignmentExpression (n) {
Expand Down
Loading