                         return create_intrinsic "builtinUndefined", []
                 n
 
-class InlineSomeIIFEs extends TreeTransformer
+#
+# special pass to inline some common idioms dealing with IIFEs
+# (immediately invoked function expressions).
+#
+#    (function(x1, x2, ...) { ... body ... }(y1, y2, ...);
+#
+# This is a common way to provide scoping in ES5 and earlier.  It is
+# unnecessary with the addition of 'let' in ES6.  We assume that all
+# bindings in 'body' have been replace by 'let' or 'const' (meaning
+# that all hoisting has been done.)
+#
+# we translate this form into the following equivalent inlined form:
+#
+#    {
+#        let x1 = y1;
+#        let x2 = y2;
+#
+#        ...
+# 
+#        { ... body ... }
+#    }
+#
+# we limit the inlining to those where count(y) <= count(x).
+# otherwise we'd need to ensure that the evaluation of the extra y's
+# takes place before the body is executed, even if they aren't used.
+#
+# Another form we can optimize is the following:
+#
+#    (function() { body }).call(this)
+#
+# this form can be inlined directly as:
+#
+#    { body }
+# 
+class IIFEIdioms extends TreeTransformer
 
         constructor: ->
                 @function_stack = new Stack
                 @iife_generator = startGenerator()
+                super
 
         visitFunction: (n) ->
                 @function_stack.push n
                 rv = super
                 @function_stack.pop
                 rv
-                
-        visitExpressionStatement: (n) ->
-                # bail out early if we know we aren't in the right place
-                if is_intrinsic "invokeClosure", n.expression
-                        candidate = n.expression
-                else if is_intrinsic "setSlot", n.expression
-                        candidate = n.expression.arguments[2]
-                else if (is_intrinsic "setGlobal", n.expression) or (is_intrinsic "setLocal", n.expression)
-                        candidate = n.expression.arguments[1]
-                else
-                        return n
-
-                return n if not is_intrinsic "invokeClosure", candidate
-                return n if not (is_intrinsic "makeClosure", candidate.arguments[0]) and not (is_intrinsic "makeAnonClosure", candidate.arguments[0])
-
+
+        maybeInlineIIFE: (candidate, n) ->
                 arity = candidate.arguments[0].arguments[1].params.length
                 arg_count = candidate.arguments.length - 1 # %invokeClosure's first arg is the callee
 
-                console.warn "arity = #{arity}.  arg_count = #{arg_count}"
-
                 return n if arg_count > arity
 
-                # at this point we know we have a zero arg IIFE in an expression statement, ala:
+                # at this point we know we have an IIFE in an expression statement, ala:
                 #
                 # (function(x, ...) { ...body...})(y, ...);
                 #

                                                value: null
                                }],
                                kind: "let"
                        @visitFunctionBody n
                else
                        # okay, we know we need a fresh environment in this function
                        

                
        visitFunction: (n) ->
            try
                new_mapping = deep_copy_object @currentMapping()
                if n.ejs_env.closed.empty() and not n.ejs_env.nested_requires_env
                        n.body.body.unshift
                                type: syntax.VariableDeclaration,
