diff --git a/lib/Makefile b/lib/Makefile index 8f142ed7..0eb2609d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 \ diff --git a/lib/ast-builder.js b/lib/ast-builder.js index 34bca354..3566667a 100644 --- a/lib/ast-builder.js +++ b/lib/ast-builder.js @@ -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'; @@ -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) }; }; diff --git a/lib/closure-conversion.js b/lib/closure-conversion.js index 46fb5142..731ec57a 100644 --- a/lib/closure-conversion.js +++ b/lib/closure-conversion.js @@ -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'; @@ -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, diff --git a/lib/common-ids.js b/lib/common-ids.js index 4ed8a245..b806b944 100644 --- a/lib/common-ids.js +++ b/lib/common-ids.js @@ -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"); diff --git a/lib/compiler.js b/lib/compiler.js index 705977c1..5bedc197 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -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 = { @@ -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? @@ -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'); @@ -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 { diff --git a/lib/node-visitor.js b/lib/node-visitor.js index acc007a5..23dced77 100644 --- a/lib/node-visitor.js +++ b/lib/node-visitor.js @@ -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; @@ -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; @@ -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); @@ -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; diff --git a/lib/passes/desugar-defaults.js b/lib/passes/desugar-defaults.js index 619c599e..69db8a74 100644 --- a/lib/passes/desugar-defaults.js +++ b/lib/passes/desugar-defaults.js @@ -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; // } // diff --git a/lib/passes/desugar-destructuring.js b/lib/passes/desugar-destructuring.js index f3324df0..a99b55d3 100644 --- a/lib/passes/desugar-destructuring.js +++ b/lib/passes/desugar-destructuring.js @@ -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'; @@ -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 }); @@ -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(); @@ -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), []) }); @@ -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 @@ -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 @@ -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) { diff --git a/lib/passes/flatten-decls.js b/lib/passes/flatten-decls.js new file mode 100644 index 00000000..299cf3ba --- /dev/null +++ b/lib/passes/flatten-decls.js @@ -0,0 +1,65 @@ +/* -*- Mode: js2; tab-width: 4; indent-tabs-mode: nil; -*- + * vim: set ts=4 sw=4 et tw=99 ft=js: + */ + +// this pass flattens all declaration AST nodes such that they're 1 +// declarator per declaration. +// +// i.e. from: +// { +// .... +// var x = 5, y = 10, z = x; +// .... +// } +// to: +// { +// .... +// var x = 5; +// var y = 10; +// var z = x; +// .... +// } +// +// with one exception. we don't flatten the decls inside of a for +// loop init, since this violates the rules of the AST, and blows up +// our traversal routines. + +import * as b from '../ast-builder'; +import { TransformPass } from '../node-visitor'; +import { Stack } from '../stack-es6'; +import Set from '../set-es6'; +import { reportWarning } from '../errors'; + +export class FlattenDecls extends TransformPass { + constructor (options, filename) { + super(options); + this.filename = filename; + } + + visitFor(n, ...args) { + this.in_for = true; + n.init = super.visit(n.init, ...args); + this.in_for = false; + n.test = super.visit(n.test, ...args); + n.update = super.visit(n.update, ...args); + n.body = super.visit(n.body, ...args); + return n; + } + + visitVariableDeclaration (n) { + if (this.in_for) return super.visitVariableDeclaration(n); + + if (n.declarations.length === 1) + return super.visitVariableDeclaration(n); + + let rv = []; + n.declarations.forEach((declarator) => { + let new_declaration = b.variableDeclaration(n.kind, [declarator]); + + new_declaration.loc = declarator.loc; + + rv.push(new_declaration); + }); + return rv; + } +} diff --git a/lib/runtime.js b/lib/runtime.js index d94dbe23..9788bd42 100644 --- a/lib/runtime.js +++ b/lib/runtime.js @@ -80,7 +80,8 @@ const runtime_interface = { create_iter_result: function() { return this.abi.createExternalFunction(this.module, "_ejs_create_iter_result", ty.EjsValue, [ty.EjsValue, ty.EjsValue]); }, - iterator_wrapper_new: function() { return this.abi.createExternalFunction(this.module, "_ejs_iterator_wrapper_new", ty.EjsValue, [ty.EjsValue]); }, + iterator_wrapper_new: function() { return this.abi.createExternalFunction(this.module, "_ejs_iterator_wrapper_new", ty.EjsValue, [ty.EjsValue]); }, + iterator_wrapper_close: function() { return this.abi.createExternalFunction(this.module, "_ejs_iterator_wrapper_close", ty.Void, [ty.EjsValue]); }, undefined: function() { return this.module.getOrInsertGlobal ("_ejs_undefined", ty.EjsValue); }, "true": function() { return this.module.getOrInsertGlobal ("_ejs_true", ty.EjsValue); }, diff --git a/pirouette b/pirouette index b9b6fb07..414f8e93 160000 --- a/pirouette +++ b/pirouette @@ -1 +1 @@ -Subproject commit b9b6fb0765ee1f13d9351d5e12bf210b3e96cc5d +Subproject commit 414f8e9366746e0377d866abdbb7491fa6e43bcd diff --git a/runtime/ejs-generator.c b/runtime/ejs-generator.c index 5fe6d8b2..60122ffb 100644 --- a/runtime/ejs-generator.c +++ b/runtime/ejs-generator.c @@ -114,6 +114,13 @@ _ejs_iterator_wrapper_new (ejsval iterator) return OBJECT_TO_EJSVAL(rv); } +void +_ejs_iterator_wrapper_close (ejsval iterator) +{ + EJSIteratorWrapper* wrapper = (EJSIteratorWrapper*)EJSVAL_TO_OBJECT(iterator); + IteratorClose(wrapper->iterator, _ejs_undefined, EJS_FALSE); +} + #define GENERATOR_STACK_SIZE 64 * 1024 static void diff --git a/runtime/ejs-generator.h b/runtime/ejs-generator.h index a9d84a47..8a65b4df 100644 --- a/runtime/ejs-generator.h +++ b/runtime/ejs-generator.h @@ -56,6 +56,10 @@ extern void _ejs_iterator_init_proto (); extern void _ejs_gc_push_generator(EJSGenerator *gen); extern void _ejs_gc_pop_generator(); + +extern ejsval _ejs_iterator_wrapper_new (ejsval iterator); +extern void _ejs_iterator_wrapper_close (ejsval iterator); + EJS_END_DECLS #endif