/// <summary>
        /// function (o) {
        ///   var a = []
        ///   for (var i in o) if (o.hasOwnProperty(i)) a.push(i)
        ///   return a
        /// }        
        /// </summary>
        private static PatternSpecialization ObjectKeysSpecialization() {
            var oParam = new ExpectedParameter(0);
            var aVar = new ExpectedVariableDeclaration(ExpectedArrayLiteral.Empty);

            var iVar = new ExpectedVariableDeclaration(null);


            return new PatternSpecialization(
                ObjectKeysSpecializationImpl,
                false,
                aVar,                
                new ExpectedNode(
                    typeof(ForIn),
                    iVar,
                    oParam,
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(IfNode),
                            new ExpectedCall(
                                new ExpectedMember(
                                    oParam,
                                    "hasOwnProperty"
                                ),
                                iVar.Variable
                            ),
                            new ExpectedNode(
                                typeof(Block),
                                new ExpectedNode(
                                    typeof(ExpressionStatement),
                                    new ExpectedCall(
                                        new ExpectedMember(
                                            aVar.Variable,
                                            "push"
                                        ),
                                        iVar.Variable
                                    )
                                )
                            )
                        )
                    )
                ),
                new ExpectedNode(
                    typeof(ReturnNode),
                    aVar.Variable
                )
            );
        }
        /// <summary>
        /// function(obj) {
        ///    if (!_.isObject(obj)) return obj;
        ///    var source, prop;
        ///    for (var i = 1, length = arguments.length; i < length; i++) {
        ///      source = arguments[i];
        ///      for (prop in source) {
        ///        if (hasOwnProperty.call(source, prop)) {
        ///            obj[prop] = source[prop];
        ///        }
        ///      }
        ///    }
        ///    return obj;
        ///  };
        ///
        /// </summary>
        /// <returns></returns>
        private static PatternSpecialization UnderscoreExtendSpecialization() {
            var objParam = new ExpectedParameter(0);
            var sourceVar = new ExpectedVariableDeclaration();
            var propVar = new ExpectedVariableDeclaration();
            var iVar = new ExpectedVariableDeclaration(new ExpectedConstant(1.0));
            var arguments = new ExpectedLookup("arguments");
            var lengthVar = new ExpectedVariableDeclaration(new ExpectedMember(arguments, "length"));
            var retNode = new ExpectedNode(typeof(ReturnNode), objParam);

            return new PatternSpecialization(
                ExtendSpecializationImpl,
                false,
                new ExpectedNode(
                    typeof(IfNode),
                    new ExpectedUnary(
                        JSToken.LogicalNot,
                        new ExpectedCall(
                            new ExpectedMember(
                                new ExpectedLookup("_"),
                                "isObject"
                            ),
                            objParam
                        )
                    ),
                    new ExpectedNode(
                        typeof(Block),
                        retNode
                    )
                ),
                new ExpectedVar(
                    sourceVar,
                    propVar
                ),
                new ExpectedNode(
                    typeof(ForNode),
                    new ExpectedVar(
                        iVar,
                        lengthVar
                    ),
                    new ExpectedBinary(
                        JSToken.LessThan,
                        iVar.Variable,
                        lengthVar.Variable
                    ),
                    new ExpectedUnary(
                        JSToken.Increment,
                        iVar.Variable
                    ),
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(ExpressionStatement),
                            new ExpectedBinary(
                                JSToken.Assign,
                                sourceVar.Variable,
                                new ExpectedIndex(
                                    arguments,
                                    iVar.Variable
                                )
                            )
                        ),
                        new ExpectedNode(
                            typeof(ForIn),
                            new ExpectedNode(
                                typeof(ExpressionStatement),
                                propVar.Variable
                            ),
                            sourceVar.Variable,
                            new ExpectedNode(
                                typeof(Block),
                                new ExpectedNode(
                                    typeof(IfNode),
                                    new ExpectedCall(
                                        new ExpectedMember(
                                            new ExpectedLookup("hasOwnProperty"),
                                            "call"
                                        ),
                                        sourceVar.Variable,
                                        propVar.Variable
                                    ),
                                    new ExpectedNode(
                                        typeof(Block),
                                        new ExpectedNode(
                                            typeof(ExpressionStatement),
                                            new ExpectedBinary(
                                                JSToken.Assign,
                                                new ExpectedIndex(
                                                    objParam,
                                                    propVar.Variable
                                                ),
                                                new ExpectedIndex(
                                                    sourceVar.Variable,
                                                    propVar.Variable
                                                )
                                            )
                                        )
                                    )
                                )
                            )
                        )
                    )
                ),
                retNode
            );
        }
        /// <summary>
        /// var assign = function(object, source, guard) {
        ///      var index, iterable = object, result = iterable;
        ///      if (!iterable) return result;
        ///      var args = arguments,
        ///          argsIndex = 0,
        ///          argsLength = typeof guard == 'number' ? 2 : args.length;
        ///      if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {
        ///        var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);
        ///      } else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {
        ///        callback = args[--argsLength];
        ///      }
        ///      while (++argsIndex < argsLength) {
        ///        iterable = args[argsIndex];
        ///        if (iterable && objectTypes[typeof iterable]) {
        ///          var ownIndex = -1,
        ///              ownProps = objectTypes[typeof iterable] && keys(iterable),
        ///              length = ownProps ? ownProps.length : 0;
        ///          
        ///          while (++ownIndex < length) {
        ///            index = ownProps[ownIndex];
        ///            result[index] = callback ? callback(result[index], iterable[index]) : iterable[index];
        ///          }
        ///        }
        ///      }
        ///      return result
        ///    };
        ///
        /// </summary>
        private static PatternSpecialization AssignSpecialization() {
            var object_ = new ExpectedParameter(0);
            var source = new ExpectedParameter(1);
            var guard = new ExpectedParameter(2);
            var index = new ExpectedVariableDeclaration();
            var iterable = new ExpectedVariableDeclaration(object_);
            var result = new ExpectedVariableDeclaration(iterable.Variable);
            var args = new ExpectedVariableDeclaration(new ExpectedLookup("arguments"));
            var argsIndex = new ExpectedVariableDeclaration(new ExpectedConstant(0.0));
            var argsLength = new ExpectedVariableDeclaration(AlwaysMatch.Instance);
            var callback = new ExpectedVariableDeclaration(AlwaysMatch.Instance);
            var objectTypes = new ExpectedLookup("objectTypes");
            var ownIndex = new ExpectedVariableDeclaration(new ExpectedUnary(JSToken.Minus, new ExpectedConstant(1.0)));
            var ownProps = new ExpectedVariableDeclaration(
                new ExpectedBinary(
                    JSToken.LogicalAnd,
                    new ExpectedIndex(
                        objectTypes,
                        new ExpectedUnary(JSToken.TypeOf, iterable.Variable)
                    ),
                    new ExpectedCall(
                        new ExpectedLookup("keys"),
                        iterable.Variable
                    )
                )
            );
            var length = new ExpectedVariableDeclaration(
                new ExpectedNode(
                    typeof(Conditional),
                    ownProps.Variable,
                    new ExpectedMember(ownProps.Variable, "length"),
                    new ExpectedConstant(0.0)
                )
            );

            return new PatternSpecialization(
                ExtendSpecializationImpl,
                new ExpectedVar(index, iterable, result),
                new ExpectedNode(
                    typeof(IfNode),
                    new ExpectedUnary(
                        JSToken.LogicalNot,
                        iterable.Variable
                    ),
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(ReturnNode),
                            result.Variable
                        )
                    )
                ),
                new ExpectedVar(args, argsIndex, argsLength),
                new ExpectedNode(
                    typeof(IfNode),
                    new ExpectedBinary(
                        JSToken.LogicalAnd,
                        new ExpectedBinary(
                            JSToken.GreaterThan,
                            argsLength.Variable,
                            new ExpectedConstant(3.0)
                        ),
                        new ExpectedBinary(
                            JSToken.Equal,
                            AlwaysMatch.Instance,
                            new ExpectedConstant("function")
                        )
                    ),
                    ExpectedBlock(callback),
                    ExpectedBlock(
                        new ExpectedNode(
                            typeof(IfNode),
                            new ExpectedBinary(
                                JSToken.LogicalAnd,
                                new ExpectedBinary(
                                    JSToken.GreaterThan,
                                    argsLength.Variable,
                                    new ExpectedConstant(2.0)
                                ),
                                new ExpectedBinary(
                                    JSToken.Equal,
                                    AlwaysMatch.Instance,
                                    new ExpectedConstant("function")
                                )
                            ),
                            ExpectedBlock(
                                new ExpectedNode(
                                    typeof(ExpressionStatement),
                                    new ExpectedBinary(
                                        JSToken.Assign,
                                        callback.Variable,
                                        AlwaysMatch.Instance
                                    )
                                )
                            )
                        )
                    )
                ),
                new ExpectedNode(
                    typeof(WhileNode),
                    new ExpectedBinary(
                        JSToken.LessThan,
                        new ExpectedUnary(
                            JSToken.Increment,
                            argsIndex.Variable
                        ),
                        argsLength.Variable
                    ),
                    ExpectedBlock(
                        ExpectedExprStmt(
                            ExpectedAssign(
                                iterable.Variable, 
                                new ExpectedIndex(args.Variable, argsIndex.Variable)
                            )
                        ),
                        new ExpectedNode(
                            typeof(IfNode),
                            new ExpectedBinary(
                                JSToken.LogicalAnd,
                                iterable.Variable,
                                new ExpectedIndex(
                                    objectTypes, 
                                    new ExpectedUnary(JSToken.TypeOf, iterable.Variable)
                                )
                            ),
                            ExpectedBlock(
                                new ExpectedVar(ownIndex, ownProps, length),
                                new ExpectedNode(
                                    typeof(WhileNode),
                                    new ExpectedBinary(
                                        JSToken.LessThan,
                                        new ExpectedUnary(
                                            JSToken.Increment,
                                            ownIndex.Variable
                                        ),
                                        length.Variable
                                    ),
                                    ExpectedBlock(
                                        ExpectedExprStmt(
                                            ExpectedAssign(
                                                index.Variable,
                                                new ExpectedIndex(
                                                    ownProps.Variable,
                                                    ownIndex.Variable
                                                )
                                            )
                                        ),
                                        ExpectedExprStmt(
                                            ExpectedAssign(
                                                new ExpectedIndex(result.Variable, index.Variable),
                                                new ExpectedNode(
                                                    typeof(Conditional),
                                                    callback.Variable,
                                                    AlwaysMatch.Instance,
                                                    new ExpectedIndex(iterable.Variable, index.Variable)
                                                )
                                            )
                                        )
                                    )
                                )
                            )
                        )
                    )
                ),
                new ExpectedNode(
                    typeof(ReturnNode),
                    result.Variable
                )
            );
        }
        /// <summary>
        /// function wrapfunction(fn, message) {
        ///  if (typeof fn !== 'function') {
        ///    throw new TypeError('argument fn must be a function')
        ///  }
        ///
        ///  var args = createArgumentsString(fn.length)
        ///  var deprecate = this
        ///  var stack = getStack()
        ///  var site = callSiteLocation(stack[1])
        ///
        ///  site.name = fn.name
        ///
        ///  var deprecatedfn = eval('(function (' + args + ') {\n'
        ///    + '"use strict"\n'
        ///    + 'log.call(deprecate, message, site)\n'
        ///    + 'return fn.apply(this, arguments)\n'
        ///    + '})')
        ///
        ///  return deprecatedfn
        ///}
        /// </summary>
        /// <returns></returns>
        private static PatternSpecialization WrapFunctionSpecialization() {
            var fn = new ExpectedParameter(0);
            var message = new ExpectedParameter(1);
            var args = new ExpectedVariableDeclaration(
                new ExpectedCall(
                    new ExpectedLookup("createArgumentsString"),
                    new ExpectedMember(fn, "length")
                )
            );
            var deprecate = new ExpectedVariableDeclaration(
                new ExpectedNode(typeof(ThisLiteral))
            );
            var stack = new ExpectedVariableDeclaration(new ExpectedCall(new ExpectedLookup("getStack")));
            var site = new ExpectedVariableDeclaration(
                new ExpectedCall(
                    new ExpectedLookup("callSiteLocation"),
                    new ExpectedIndex(
                        stack.Variable,
                        new ExpectedConstant(1.0)
                    )
                )
            );
            var deprecatedfn = new ExpectedVariableDeclaration(
                new ExpectedCall(
                    new ExpectedLookup("eval"),
                    AlwaysMatch.Instance
                )
            );

            return new PatternSpecialization(
                WrapFunctionSpecializationImpl,
                new ExpectedNode(
                    typeof(IfNode),
                    new ExpectedBinary(
                        JSToken.StrictNotEqual,
                        new ExpectedUnary(
                            JSToken.TypeOf,
                            fn
                        ),
                        new ExpectedConstant("function")
                    ),
                    AlwaysMatch.Instance
                ),
                args,
                deprecate,
                stack,
                site,
                new ExpectedNode(
                    typeof(ExpressionStatement),
                    new ExpectedBinary(
                        JSToken.Assign,
                        new ExpectedMember(site.Variable, "name"),
                        new ExpectedMember(fn, "name")
                    )
                ),
                deprecatedfn,
                new ExpectedNode(
                    typeof(ReturnNode),
                    deprecatedfn.Variable
                )
            );
        }
        /// <summary>
        /// function extend(protoProps, staticProps) {
        ///   var parent = this;
        ///   var child;
        ///   
        ///   if (protoProps && _.has(protoProps, 'constructor')) {
        ///     child = protoProps.constructor;
        ///   } else {
        ///     child = function(){ return parent.apply(this, arguments); };
        ///   }
        ///
        ///   _.extend(child, parent, staticProps);
        ///   
        ///   var Surrogate = function(){ this.constructor = child; };
        ///   Surrogate.prototype = parent.prototype;
        ///   child.prototype = new Surrogate;
        ///
        ///   if (protoProps) _.extend(child.prototype, protoProps);
        ///
        ///   child.__super__ = parent.prototype;
        ///
        ///   return child;
        /// };
        /// </summary>
        /// <returns></returns>
        private static PatternSpecialization BackboneExtendSpecialization() {
            var protoProps = new ExpectedParameter(0);
            var staticProps = new ExpectedParameter(1);
            var parentVar = new ExpectedVariableDeclaration(new ExpectedNode(typeof(ThisLiteral)));
            var childVar = new ExpectedVariableDeclaration();
            var underscore = new ExpectedLookup("_");
            var thisNode = new ExpectedNode(typeof(ThisLiteral));
            var surrogate = new ExpectedVariableDeclaration(
                new ExpectedFunctionExpr(
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(ExpressionStatement),
                            new ExpectedBinary(
                                JSToken.Assign,
                                new ExpectedMember(
                                    thisNode,
                                    "constructor"
                                ),
                                childVar.Variable
                            )
                        )
                    )
                )
            );

            return new PatternSpecialization(
                BackboneExtendSpecializationImpl,
                false,
                parentVar,
                childVar,
                new ExpectedNode(
                    typeof(IfNode),
                    new ExpectedBinary(
                        JSToken.LogicalAnd,
                        protoProps,
                        new ExpectedCall(
                            new ExpectedMember(underscore, "has"),
                            protoProps,
                            new ExpectedConstant("constructor")
                        )
                    ),
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(ExpressionStatement),
                            new ExpectedBinary(
                                JSToken.Assign,
                                childVar.Variable,
                                new ExpectedMember(
                                    protoProps,
                                    "constructor"
                                )
                            )
                        )
                    ),
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(ExpressionStatement),
                            new ExpectedBinary(
                                JSToken.Assign,
                                childVar.Variable,
                                new ExpectedFunctionExpr(
                                    new ExpectedNode(
                                        typeof(Block),
                                        new ExpectedNode(
                                            typeof(ReturnNode),
                                            new ExpectedCall(
                                                new ExpectedMember(parentVar.Variable, "apply"),
                                                thisNode,
                                                new ExpectedLookup("arguments")
                                            )
                                        )
                                    )
                                )
                            )
                        )
                    )
                ),
                new ExpectedNode(
                    typeof(ExpressionStatement),
                    new ExpectedCall(
                        new ExpectedMember(underscore, "extend"),
                        childVar.Variable,
                        parentVar.Variable,
                        staticProps
                    )
                ),
                surrogate,
                new ExpectedNode(
                    typeof(ExpressionStatement),
                    new ExpectedBinary(
                        JSToken.Assign,
                        new ExpectedMember(surrogate.Variable, "prototype"),
                        new ExpectedMember(parentVar.Variable, "prototype")
                    )
                ),
                new ExpectedNode(
                    typeof(ExpressionStatement),
                    new ExpectedBinary(
                        JSToken.Assign,
                        new ExpectedMember(childVar.Variable, "prototype"),
                        new ExpectedNew(surrogate.Variable)
                    )
                ),
                new ExpectedNode(
                    typeof(IfNode),
                    protoProps,
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(ExpressionStatement),
                            new ExpectedCall(
                                new ExpectedMember(underscore, "extend"),
                                new ExpectedMember(childVar.Variable, "prototype"),
                                protoProps
                            )
                        )
                    )
                ),
                new ExpectedNode(
                    typeof(ExpressionStatement),
                    new ExpectedBinary(
                        JSToken.Assign,
                        new ExpectedMember(childVar.Variable, "__super__"),
                        new ExpectedMember(parentVar.Variable, "prototype")
                    )
                ),
                new ExpectedNode(
                    typeof(ReturnNode),
                    childVar.Variable
                )
            );
        }
        /// <summary>
        /// Matches:
        /// 
        /// module.exports = function (dest, src) {
        ///     Object.getOwnPropertyNames(src).forEach(function (name) {
        ///         var descriptor = Object.getOwnPropertyDescriptor(src, name)
        ///         Object.defineProperty(dest, name, descriptor)
        ///     })
        ///     return dest
        /// }
        /// </summary>
        private static PatternSpecialization MergeDescriptorsSpecialization() {
            var destParam = new ExpectedParameter(0);
            var srcParam = new ExpectedParameter(1);
            var descriptorVar = new ExpectedVariableDeclaration(
                new ExpectedCall(
                    new ExpectedMember(new ExpectedLookup("Object"), "getOwnPropertyDescriptor"),
                    new ExpectedParameter(1, 1), // src in outer function
                    new ExpectedParameter(0)    // name in inner function
                )
            );

            return new PatternSpecialization(
                MergeSpecializationImpl,
                new ExpectedNode(
                    typeof(ExpressionStatement),
                    new ExpectedCall(
                        new ExpectedMember(
                            new ExpectedCall(
                                new ExpectedMember(new ExpectedLookup("Object"), "getOwnPropertyNames"),
                                srcParam
                            ),
                            "forEach"
                        ),
                        new ExpectedFunctionExpr(
                            new ExpectedNode(
                                typeof(Block),
                                descriptorVar,
                                new ExpectedNode(
                                    typeof(ExpressionStatement),
                                    new ExpectedCall(
                                        new ExpectedMember(new ExpectedLookup("Object"), "defineProperty"),
                                        new ExpectedParameter(0, 1),    // dest in outer function
                                        new ExpectedParameter(0),   // name in inner function
                                        descriptorVar.Variable
                                    )
                                )
                            )
                        )
                    )
                ),
                new ExpectedNode(typeof(ReturnNode), destParam)
            );
        }
 public ExpectedVariable(ExpectedVariableDeclaration decl) {
     _decl = decl;
 }
        /// <summary>
        /// Matches:
        /// 
        /// function merge (to, from) {
        ///  var keys = Object.keys(from)
        ///    , i = keys.length
        ///    , key
        ///
        ///  while (i--) {
        ///    key = keys[i];
        ///    if ('undefined' === typeof to[key]) {
        ///      to[key] = clone(from[key], {retainKeyOrder:1});
        ///    } else {
        ///      if (exports.isObject(from[key])) {
        ///        mergeClone(to[key], from[key]);
        ///      } else {
        ///        to[key] = clone(from[key], {retainKeyOrder: 1});
        ///      }
        ///    }
        ///  }
        ///}
        /// </summary>
        private static PatternSpecialization MergeCloneSpecialization() {
            var toParam = new ExpectedParameter(0);
            var fromParam = new ExpectedParameter(1);
            var keysVar = new ExpectedVariableDeclaration(
                new ExpectedCall(
                    new ExpectedMember(new ExpectedLookup("Object"), "keys"),
                    fromParam
                )
            );
            var iVar = new ExpectedVariableDeclaration(
                new ExpectedMember(keysVar.Variable, "length")
            );
            var keyVar = new ExpectedVariableDeclaration();

            var copyProp = ExpectedExprStmt(
                    ExpectedAssign(
                        new ExpectedIndex(
                            toParam,
                            keyVar.Variable
                        ),
                        new ExpectedCall(
                            new ExpectedLookup("clone"),
                            new ExpectedIndex(
                                fromParam,
                                keyVar.Variable
                            ),
                            AlwaysMatch.Instance
                        )
                    )
                );

            return new PatternSpecialization(
                MergeSpecializationImpl,
                false,
                new ExpectedVar(keysVar, iVar, keyVar),
                new ExpectedNode(
                    typeof(WhileNode),
                    new ExpectedUnary(JSToken.Decrement, iVar.Variable),
                    ExpectedBlock(
                        ExpectedExprStmt(
                            ExpectedAssign(
                                keyVar.Variable,
                                new ExpectedIndex(keysVar.Variable, iVar.Variable)
                            )
                        ),

                        new ExpectedNode(
                            typeof(IfNode),
                            new ExpectedBinary(
                                JSToken.StrictEqual,
                                new ExpectedConstant("undefined"),
                                new ExpectedUnary(
                                    JSToken.TypeOf,
                                    new ExpectedIndex(toParam, keyVar.Variable)
                                )
                            ),
                            ExpectedBlock(copyProp),
                            ExpectedBlock(
                                new ExpectedNode(
                                    typeof(IfNode),
                                    new ExpectedCall(
                                        new ExpectedMember(
                                            AlwaysMatch.Instance,
                                            "isObject"
                                        ),
                                        new ExpectedIndex(
                                            fromParam,
                                            keyVar.Variable
                                        )
                                    ),
                                    ExpectedBlock(
                                        ExpectedExprStmt(
                                            new ExpectedCall(
                                                new ExpectedLookup("mergeClone"),
                                                new ExpectedIndex(
                                                    toParam,
                                                    keyVar.Variable
                                                ),
                                                new ExpectedIndex(
                                                    fromParam,
                                                    keyVar.Variable
                                                )
                                            )
                                        )
                                    ),
                                    ExpectedBlock(copyProp)
                                )
                            )
                        )
                    )
                )
            );
        }
 /// <summary>
 /// Matches:
 /// 
 /// function merge(a, b) {
 ///     if(a && b) {
 ///         for(var key in b) {
 ///             a[key] = b[key]
 ///         }
 ///     }
 ///     return a;
 /// }
 /// </summary>
 private static PatternSpecialization MergeSpecialization() {
     var targetParam = new ExpectedParameter(0);
     var sourceParam = new ExpectedParameter(1);
     var keyVar = new ExpectedVariableDeclaration();
     return new PatternSpecialization(
         MergeSpecializationImpl,
         false,
         new ExpectedNode(
             typeof(IfNode),
             new ExpectedBinary(
                 JSToken.LogicalAnd,
                 targetParam,
                 sourceParam
             ),
             new ExpectedNode(
                 typeof(Block),
                 new ExpectedNode(
                     typeof(ForIn),
         // variable
                     keyVar,
         // collection
                     sourceParam,
         // body
                     new ExpectedNode(
                         typeof(Block),
                         new ExpectedNode(
                             typeof(ExpressionStatement),
                             new ExpectedBinary(
                                 JSToken.Assign,
                                 new ExpectedIndex(
                                     targetParam,
                                     keyVar.Variable
                                 ),
                                 new ExpectedIndex(
                                     sourceParam,
                                     keyVar.Variable
                                 )
                             )
                         )
                     )
                 )
             )
         ),
         new ExpectedNode(
             typeof(ReturnNode),
             targetParam
         )
     );
 }
        /// <summary>
        /// function copy (obj) {
        ///  var o = {}
        ///  Object.keys(obj).forEach(function (i) {
        ///    o[i] = obj[i]
        ///  })
        ///  return o
        ///}
        private static PatternSpecialization CopySpecialization() {
            var objParam = new ExpectedParameter(0);
            var iParam = new ExpectedParameter(0);
            var oVar = new ExpectedVariableDeclaration(ExpectedObjectLiteral.Empty);

            return new PatternSpecialization(
                CopySpecializationImpl,
                false,
                oVar,
                new ExpectedNode(
                    typeof(ExpressionStatement),
                    new ExpectedCall(
                        new ExpectedMember(
                            new ExpectedCall(
                                new ExpectedMember(
                                    ObjectLookup,
                                    "keys"
                                ),
                                objParam
                            ),
                            "forEach"
                        ),
                        new ExpectedFunctionExpr(
                            new ExpectedNode(
                                typeof(Block),
                                new ExpectedNode(
                                    typeof(ExpressionStatement),
                                    new ExpectedBinary(
                                        JSToken.Assign,
                                        new ExpectedIndex(
                                            oVar.Variable,
                                            iParam
                                        ),
                                        new ExpectedIndex(
                                            new ExpectedParameter(0, 1),
                                            iParam
                                        )
                                    )
                                )
                            )
                        )
                    )
                ),
                new ExpectedNode(
                    typeof(ReturnNode),
                    oVar.Variable
                )
            );
        }