/// <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>
        /// 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
                )
            );
        }