Example #1
0
            // Generates a class declaration for the current alt and its subtypes
            public void GenerateOutput(ref VList <LNode> list)
            {
                bool isAbstract = _classAttrs.Any(a => a.IsIdNamed(S.Abstract));

                var baseParts = new List <AdtParam>();

                for (var type = ParentType; type != null; type = type.ParentType)
                {
                    baseParts.InsertRange(0, type.Parts);
                }
                var allParts = baseParts.Concat(Parts);

                var initialization = Parts.Select(p => LNode.Call(CodeSymbols.Assign, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Id(CodeSymbols.This), p.NameId)).SetStyle(NodeStyle.Operator), p.NameId)).SetStyle(NodeStyle.Operator)).ToList();

                if (baseParts.Count > 0)
                {
                    initialization.Insert(0, F.Call(S.Base, baseParts.Select(p => p.NameId)));
                }

                var args = new VList <LNode>(allParts.Select(p => p.OriginalDecl));

                if (!_constructorAttrs.Any(a => a.IsIdNamed(S.Public)))
                {
                    _constructorAttrs.Add(F.Id(S.Public));
                }
                LNode constructor = LNode.Call(LNode.List(_constructorAttrs), CodeSymbols.Constructor, LNode.List(LNode.Missing, _typeNameStem, LNode.Call(CodeSymbols.AltList, LNode.List(args)), LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(initialization).AddRange(_extraConstrLogic)).SetStyle(NodeStyle.Statement)));

                var outBody = new VList <LNode>();

                outBody.Add(constructor);
                outBody.AddRange(Parts.Select(p => p.GetFieldDecl()));
                outBody.AddRange(baseParts.Select(p => GetWithFn(p, isAbstract, S.Override, allParts)));
                outBody.AddRange(Parts.Select(p => GetWithFn(p, isAbstract, _children.Count > 0 ? S.Virtual : null, allParts)));
                outBody.AddRange(Parts.WithIndexes()
                                 .Where(kvp => kvp.Value.NameId.Name.Name != "Item" + (baseParts.Count + kvp.Key + 1))
                                 .Select(kvp => kvp.Value.GetItemDecl(baseParts.Count + kvp.Key + 1)));
                outBody.AddRange(_classBody);

                list.Add(LNode.Call(LNode.List(_classAttrs), CodeSymbols.Class, LNode.List(TypeName, LNode.Call(CodeSymbols.AltList, LNode.List(BaseTypes)), LNode.Call(CodeSymbols.Braces, LNode.List(outBody)).SetStyle(NodeStyle.Statement))));
                if (_genericArgs.Count > 0 && Parts.Count > 0)
                {
                    var argNames = allParts.Select(p => p.NameId);
                    list.Add(LNode.Call(LNode.List().AddRange(_classAttrs).Add(LNode.Id(CodeSymbols.Static)).Add(LNode.Id(CodeSymbols.Partial)), CodeSymbols.Class, LNode.List(_typeNameStem, LNode.Call(CodeSymbols.AltList), LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(LNode.List(LNode.Id(CodeSymbols.Public), LNode.Id(CodeSymbols.Static)), CodeSymbols.Fn, LNode.List(TypeNameWithoutAttrs, LNode.Call(CodeSymbols.Of, LNode.List().Add(LNode.Id((Symbol)"New")).AddRange(_genericArgs)).SetStyle(NodeStyle.Operator), LNode.Call(CodeSymbols.AltList, LNode.List(args)), LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(LNode.Call(CodeSymbols.New, LNode.List(LNode.Call(TypeNameWithoutAttrs, LNode.List(argNames)))))))).SetStyle(NodeStyle.Statement))))).SetStyle(NodeStyle.Statement))));
                }
                foreach (var child in _children)
                {
                    child.GenerateOutput(ref list);
                }
            }
Example #2
0
 public static IListSource <ILNode> GetTrailingTrivia(this ILNode node)
 {
     if (node is LNode)
     {
         LNodeList list = GetTrailingTrivia((LNode)node);
         if (list.IsEmpty)
         {
             return(EmptyList <ILNode> .Value);                  // avoid boxing in the common case
         }
         return(list);
     }
     else
     {
         VList <ILNode> list = VList <ILNode> .Empty;
         foreach (ILNode a in node.Attrs())
         {
             if (a.Calls(S.TriviaTrailing))
             {
                 list.AddRange(a.Args());
             }
         }
         if (list.IsEmpty)
         {
             return(EmptyList <ILNode> .Value);                  // avoid boxing in the common case
         }
         return(list);
     }
 }
Example #3
0
 public static VList <LNode> WithSpliced(this VList <LNode> list, LNode node, Symbol listName = null)
 {
     if (node.Calls(listName ?? CodeSymbols.Splice))
     {
         return(list.AddRange(node.Args));
     }
     else
     {
         return(list.Add(node));
     }
 }
Example #4
0
 private void Add(ref VList <LNode> results, LNode result)
 {
     if (result.Calls(S.Splice))
     {
         results.AddRange(result.Args);
     }
     else
     {
         results.Add(result);
     }
 }
    private void LoadGroup(TGroup group, string groupName, TGrid grid)
    {
        VList <T> returnList = FetchInformation(group);

        if (Session[groupName] != null)
        {
            List <T> tempList = (List <T>)Session[groupName];
            returnList.AddRange(tempList);
        }
        grid.DataSource = returnList;
        grid.DataBind();
    }
Example #6
0
        public static LNode concatId(LNode node, IMessageSink sink)
        {
            var args = node.Args;

            if (args.Count == 0)
            {
                return(null);
            }
            if (args.Slice(0, args.Count - 1).Any(n => n.IsCall))
            {
                return(Reject(sink, node, "All arguments to ##() or concat() must be identifiers or literals (except the last one)"));
            }

            VList <LNode> attrs = node.Attrs;
            LNode         arg   = null;
            StringBuilder sb    = new StringBuilder();

            for (int i = 0; i < args.Count; i++)
            {
                arg = args[i];
                attrs.AddRange(arg.Attrs);

                if (arg.IsLiteral)
                {
                    sb.Append(arg.Value ?? "null");
                }
                else if (arg.IsId)
                {
                    sb.Append(arg.Name);
                }
                else                   // call
                {
                    if (i + 1 != args.Count || !arg.HasSimpleHead())
                    {
                        return(Reject(sink, arg, "Expected simple identifier or literal"));
                    }
                    sb.Append(arg.Name);
                }
            }
            Symbol combined = GSymbol.Get(sb.ToString());
            LNode  result;

            if (arg.IsCall)
            {
                result = arg.WithTarget(combined);
            }
            else
            {
                result = LNode.Id(combined, node);
            }
            return(result.WithAttrs(attrs));
        }
Example #7
0
        static bool MatchesPatternNested(LNode candidate, LNode pattern, ref MMap <Symbol, LNode> captures, ref VList <LNode> trivia)
        {
            VList <LNode> unmatchedAttrs;

            if (!MatchesPattern(candidate, pattern, ref captures, out unmatchedAttrs))
            {
                return(false);
            }
            if (unmatchedAttrs.Any(a => !a.IsTrivia))
            {
                return(false);
            }
            trivia.AddRange(unmatchedAttrs);
            return(true);
        }
Example #8
0
 public void ScanClassBody(VList <LNode> body)
 {
     foreach (var stmt in body)
     {
         int i;
         {
             LNode         altName;
             VList <LNode> attrs, childBody = default(VList <LNode>), parts, rest;
             if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.Fn, 3) && stmt.Args[0].IsIdNamed((Symbol)"alt") && (altName = stmt.Args[1]) != null && stmt.Args[2].Calls(CodeSymbols.AltList) && (parts = stmt.Args[2].Args).IsEmpty | true || (attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.Fn, 4) && stmt.Args[0].IsIdNamed((Symbol)"alt") && (altName = stmt.Args[1]) != null && stmt.Args[2].Calls(CodeSymbols.AltList) && (parts = stmt.Args[2].Args).IsEmpty | true && stmt.Args[3].Calls(CodeSymbols.Braces) && (childBody = stmt.Args[3].Args).IsEmpty | true)
             {
                 LNode genericAltName = altName;
                 if (altName.CallsMin(CodeSymbols.Of, 1))
                 {
                 }
                 else if (_genericArgs.Count > 0)
                 {
                     genericAltName = LNode.Call(CodeSymbols.Of, LNode.List().Add(altName).AddRange(_genericArgs.ToVList())).SetStyle(NodeStyle.Operator);
                 }
                 var child = new AltType(attrs, genericAltName, LNode.List(), this);
                 child.AddParts(parts);
                 child.ScanClassBody(childBody);
                 _children.Add(child);
             }
             else if ((attrs = stmt.Attrs).IsEmpty | true && (i = attrs.IndexWhere(a => a.IsIdNamed(__alt))) > -1 && stmt.CallsMin(CodeSymbols.Constructor, 3) && stmt.Args[1].IsIdNamed((Symbol)"#this") && stmt.Args[2].Calls(CodeSymbols.AltList) && (rest = new VList <LNode>(stmt.Args.Slice(3))).IsEmpty | true && rest.Count <= 1)
             {
                 parts = stmt.Args[2].Args;
                 attrs.RemoveAt(i);
                 _constructorAttrs.AddRange(attrs);
                 if (rest.Count > 0 && rest[0].Calls(S.Braces))
                 {
                     _extraConstrLogic.AddRange(rest[0].Args);
                 }
                 AddParts(parts);
             }
             else
             {
                 _classBody.Add(stmt);
             }
         }
     }
 }
Example #9
0
		static bool MatchesPatternNested(LNode candidate, LNode pattern, ref MMap<Symbol, LNode> captures, ref VList<LNode> trivia)
		{
			VList<LNode> unmatchedAttrs;
			if (!MatchesPattern(candidate, pattern, ref captures, out unmatchedAttrs))
				return false;
			if (unmatchedAttrs.Any(a => !a.IsTrivia))
				return false;
			trivia.AddRange(unmatchedAttrs);
			return true;
		}
Example #10
0
            void ProcessEnsuresAttribute(VList <LNode> conditions, Symbol mode, LNode exceptionType, LNode variableName)
            {
                // Create a "Contract.Whatever()" check for each provided condition.
                bool haveCCRewriter = _haveCCRewriter && mode != sy_ensuresAssert && mode != sy_ensuresFinally;
                var  checks         = LNode.List();

                foreach (var condition_ in conditions)
                {
                    LNode condition = condition_;                       // make it writable so we can replace `_`
                    LNode conditionStr;

                    LNode  contractResult  = null;
                    string underscoreError = null;
                    if (mode == sy_ensuresOnThrow)
                    {
                        contractResult = Id__exception__;
                        if (haveCCRewriter)
                        {
                            underscoreError = "`ensuresOnThrow` does not support `_` in MS Code Contracts mode.";
                        }
                    }
                    else                                // @@ensures or @@ensuresAssert or @@ensuresFinally
                    {
                        contractResult = haveCCRewriter ? LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Id((Symbol)"Contract"), LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol)"Result"), ReturnType)).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.Operator)) : Id_return_value;
                        if (mode == sy_ensuresFinally)
                        {
                            underscoreError = "The macro for `{0}` does not support `_` because the return value is not available in `finally`";
                        }
                        else if (haveCCRewriter && ReturnType.IsIdNamed(S.Missing))
                        {
                            underscoreError = "The macro for `{0}` does not support `_` in this context when MS Code Contracts are enabled, because the return type is unknown.";
                        }
                        bool changed = ReplaceContractUnderscore(ref condition, contractResult);
                    }
                    if (ReplaceContractUnderscore(ref condition, contractResult) && underscoreError != null)
                    {
                        Context.Sink.Error(condition, underscoreError, mode);
                    }

                    if (haveCCRewriter)
                    {
                        if (mode == sy_ensuresOnThrow)
                        {
                            checks.Add(exceptionType != null
                                                        ? LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Id((Symbol)"Contract"), LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol)"EnsuresOnThrow"), exceptionType)).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.Operator), LNode.List(condition)) : LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Id((Symbol)"Contract"), LNode.Id((Symbol)"EnsuresOnThrow"))).SetStyle(NodeStyle.Operator), LNode.List(condition)));
                        }
                        else
                        {
                            checks.Add(LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Id((Symbol)"Contract"), LNode.Id((Symbol)"Ensures"))).SetStyle(NodeStyle.Operator), LNode.List(condition)));
                        }
                    }
                    else
                    {
                        conditionStr = ConditionToStringLit(condition,
                                                            mode == sy_ensuresOnThrow
                                                ? "Postcondition failed after throwing an exception: {1}" :
                                                            "Postcondition failed: {1}");


                        if (mode == sy_ensuresOnThrow)
                        {
                            var excType = GetExceptionTypeForEnsuresOnThrow();
                            checks.Add(LNode.Call(CodeSymbols.If, LNode.List(LNode.Call(CodeSymbols.Not, LNode.List(condition)).SetStyle(NodeStyle.Operator), LNode.Call(CodeSymbols.Throw, LNode.List(LNode.Call(CodeSymbols.New, LNode.List(LNode.Call(excType, LNode.List(conditionStr, Id__exception__)))))))));
                        }
                        else
                        {
                            LNode assertMethod;
                            if (mode == sy_ensuresAssert)
                            {
                                assertMethod = GetAssertMethod(Context);
                            }
                            else if (mode == sy_ensuresFinally)
                            {
                                assertMethod = GetAssertMethodForEnsuresFinally();
                            }
                            else
                            {
                                assertMethod = GetAssertMethodForEnsures();
                            }

                            checks.Add(LNode.Call(assertMethod, LNode.List(condition, conditionStr)));
                        }
                    }
                }

                // Request that the checks be added to the beginning of the method
                if (checks.Count > 0)
                {
                    if (_haveCCRewriter)
                    {
                        PrependStmts.AddRange(checks);
                    }
                    else if (mode == sy_ensuresOnThrow)
                    {
                        LNode excSpec = exceptionType == null ? Id__exception__ : LNode.Call(CodeSymbols.Var, LNode.List(exceptionType, Id__exception__));
                        PrependStmts.Add(LNode.Call((Symbol)"on_throw", LNode.List(excSpec, LNode.Call(CodeSymbols.Braces, LNode.List(checks)).SetStyle(NodeStyle.Statement))).SetStyle(NodeStyle.Special));
                    }
                    else if (mode == sy_ensuresFinally)
                    {
                        PrependStmts.Add(LNode.Call((Symbol)"on_finally", LNode.List(LNode.Call(CodeSymbols.Braces, LNode.List(checks)).SetStyle(NodeStyle.Statement))).SetStyle(NodeStyle.Special));
                    }
                    else                                // mode == @@ensures || mode == @@ensuresAssert
                    {
                        PrependStmts.Add(LNode.Call((Symbol)"on_return", LNode.List(Id_return_value, LNode.Call(CodeSymbols.Braces, LNode.List(checks)).SetStyle(NodeStyle.Statement))).SetStyle(NodeStyle.Special));
                    }
                }
            }
Example #11
0
			// Generates a class declaration for the current alt and its subtypes
			public void GenerateOutput(ref VList<LNode> list)
			{
				bool isAbstract = _classAttrs.Any(a => a.IsIdNamed(S.Abstract));
			
				var baseParts = new List<AdtParam>();
				for (var type = ParentType; type != null; type = type.ParentType)
					baseParts.InsertRange(0, type.Parts);
				var allParts = baseParts.Concat(Parts);
			
				var initialization = Parts.Select(p => LNode.Call(CodeSymbols.Assign, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Id(CodeSymbols.This), p.NameId)).SetStyle(NodeStyle.Operator), p.NameId)).SetStyle(NodeStyle.Operator)).ToList();
				if (baseParts.Count > 0)
					initialization.Insert(0, F.Call(S.Base, baseParts.Select(p => p.NameId)));
			
				var args = new VList<LNode>(allParts.Select(p => p.OriginalDecl));
				if (!_constructorAttrs.Any(a => a.IsIdNamed(S.Public)))
					_constructorAttrs.Add(F.Id(S.Public));
				LNode constructor = LNode.Call(LNode.List(_constructorAttrs), CodeSymbols.Constructor, LNode.List(LNode.Missing, _typeNameStem, LNode.Call(CodeSymbols.AltList, LNode.List(args)), LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(initialization).AddRange(_extraConstrLogic)).SetStyle(NodeStyle.Statement)));
			
				var outBody = new VList<LNode>();
				outBody.Add(constructor);
				outBody.AddRange(Parts.Select(p => p.GetFieldDecl()));
				outBody.AddRange(baseParts.Select(p => GetWithFn(p, isAbstract, S.Override, allParts)));
				outBody.AddRange(Parts.Select(p => GetWithFn(p, isAbstract, _children.Count > 0 ? S.Virtual : null, allParts)));
				outBody.AddRange(Parts.WithIndexes()
				.Where(kvp => kvp.Value.NameId.Name.Name != "Item" + (baseParts.Count + kvp.Key + 1))
				.Select(kvp => kvp.Value.GetItemDecl(baseParts.Count + kvp.Key + 1)));
				outBody.AddRange(_classBody);
			
				list.Add(LNode.Call(LNode.List(_classAttrs), CodeSymbols.Class, LNode.List(TypeName, LNode.Call(CodeSymbols.AltList, LNode.List(BaseTypes)), LNode.Call(CodeSymbols.Braces, LNode.List(outBody)).SetStyle(NodeStyle.Statement))));
				if (_genericArgs.Count > 0 && Parts.Count > 0) {
					var argNames = allParts.Select(p => p.NameId);
					list.Add(LNode.Call(LNode.List().AddRange(_classAttrs).Add(LNode.Id(CodeSymbols.Static)).Add(LNode.Id(CodeSymbols.Partial)), CodeSymbols.Class, LNode.List(_typeNameStem, LNode.Call(CodeSymbols.AltList), LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(LNode.List(LNode.Id(CodeSymbols.Public), LNode.Id(CodeSymbols.Static)), CodeSymbols.Fn, LNode.List(TypeNameWithoutAttrs, LNode.Call(CodeSymbols.Of, LNode.List().Add(LNode.Id((Symbol) "New")).AddRange(_genericArgs)).SetStyle(NodeStyle.Operator), LNode.Call(CodeSymbols.AltList, LNode.List(args)), LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(LNode.Call(CodeSymbols.New, LNode.List(LNode.Call(TypeNameWithoutAttrs, LNode.List(argNames)))))))).SetStyle(NodeStyle.Statement))))).SetStyle(NodeStyle.Statement))));
				}
				foreach (var child in _children)
					child.GenerateOutput(ref list);
			}
Example #12
0
		public void TestSublistProblem()
		{
			// This problem affects FVList.PreviousIn(), VList.NextIn(),
			// AddRange(list, excludeSubList), VList.Enumerator when used with a
			// range.

			// Normally this works fine:
			VList<int> subList = new VList<int>(), list;
			subList.AddRange(new int[] { 1, 2, 3, 4, 5, 6, 7 });
			list = subList;
			list.Add(8);
			Assert.That(subList.NextIn(list).Last == 8);

			// But try it a second time and the problem arises, without some special
			// code in VListBlock<T>.FindNextBlock() that has been added to
			// compensate. I call the problem copy-causing-sharing-failure. You see,
			// right now subList is formed from three blocks: a size-8 block that
			// contains {7}, a size-4 block {3, 4, 5, 6} and a size-2 block {1, 2}.
			// But the size-8 block actually has two items {7, 8} and when we
			// attempt to add 9, a new array must be created. It might waste a lot
			// of memory to make a new block {9} that links to the size-8 block that
			// contains {7}, so instead a new size-8 block {7, 9} is created that
			// links directly to {3, 4, 5, 6}. That way, the block {7, 8} can be
			// garbage-collected if it is no longer in use. But a side effect is
			// that subList no longer appears to be a part of list. The fix is to
			// notice that list (block {7, 9}) and subList (block that contains {7})
			// have the same prior list, {3, 4, 5, 6}, and that the remaining 
			// item(s) in subList (just one item, {7}, in this case) are also
			// present in list.
			list = subList;
			list.Add(9);
			Assert.AreEqual(9, subList.NextIn(list).Last);
		}
Example #13
0
		void TestAddRangePair()
		{
			VList<int> list = new VList<int>();
			VList<int> list2 = new VList<int>();
			list2.AddRange(new int[] { 1, 2, 3, 4 });
			list.AddRange(list2, list2.WithoutLast(1));
			list.AddRange(list2, list2.WithoutLast(2));
			list.AddRange(list2, list2.WithoutLast(3));
			list.AddRange(list2, list2.WithoutLast(4));
			ExpectList(list, 1, 2, 3, 1, 2, 1);

			AssertThrows<InvalidOperationException>(delegate() { list2.AddRange(list2.WithoutLast(1), list2); });
			AssertThrows<InvalidOperationException>(delegate() { list2.AddRange(VList<int>.Empty, list2); });
		}
Example #14
0
		public void TestToArray()
		{
			VList<int> list = new VList<int>();
			int[] array = list.ToArray();
			Assert.AreEqual(array.Length, 0);

			array = list.Add(1).ToArray();
			ExpectList(array.AsListSource(), 1);

			array = list.Add(2).ToArray();
			ExpectList(array.AsListSource(), 1, 2);

			array = list.Add(3).ToArray();
			ExpectList(array.AsListSource(), 1, 2, 3);

			array = list.AddRange(new int[] { 4, 5, 6, 7, 8 }).ToArray();
			ExpectList(array.AsListSource(), 1, 2, 3, 4, 5, 6, 7, 8);
		}
Example #15
0
		public void TestEmptyListOperations()
		{
			VList<int> a = new VList<int>();
			VList<int> b = new VList<int>();
			a.AddRange(b);
			a.InsertRange(0, b);
			a.RemoveRange(0, 0);
			Assert.That(!a.Remove(0));
			Assert.That(a.IsEmpty);

			a.Add(1);
			b.AddRange(a);
			ExpectList(b, 1);
			b.RemoveAt(0);
			Assert.That(b.IsEmpty);
			b.InsertRange(0, a);
			ExpectList(b, 1);
			b.RemoveRange(0, 1);
			Assert.That(b.IsEmpty);
			b.Insert(0, a[0]);
			ExpectList(b, 1);
			b.Remove(a.Last);
			Assert.That(b.IsEmpty);
			
			AssertThrows<InvalidOperationException>(delegate() { a.NextIn(b); });
		}
Example #16
0
		public void SimpleTests()
		{
			// In this simple test, I only add and remove items from the back
			// of a VList, but forking is also tested.

			VList<int> list = new VList<int>();
			Assert.That(list.IsEmpty);

			// Adding to VListBlockOfTwo
			list = new VList<int>(10, 20);
			ExpectList(list, 10, 20);

			list = new VList<int>();
			list.Add(1);
			Assert.That(!list.IsEmpty);
			list.Add(2);
			ExpectList(list, 1, 2);

			// A fork in VListBlockOfTwo. Note that list2 will use two VListBlocks
			// here but list will only use one.
			VList<int> list2 = list.WithoutLast(1);
			list2.Add(3);
			ExpectList(list, 1, 2);
			ExpectList(list2, 1, 3);

			// Try doubling list2
			list2.AddRange(list2);
			ExpectList(list2, 1, 3, 1, 3);

			// list now uses two arrays
			list.Add(4);
			ExpectList(list, 1, 2, 4);

			// Try doubling list using a different overload of AddRange()
			list.AddRange((IList<int>)list);
			ExpectList(list, 1, 2, 4, 1, 2, 4);
			list = list.WithoutLast(3);
			ExpectList(list, 1, 2, 4);

			// Remove(), Pop()
			Assert.AreEqual(3, list2.Pop());
			ExpectList(list2, 1, 3, 1);
			Assert.That(!list2.Remove(0));
			Assert.AreEqual(1, list2.Pop());
			Assert.That(list2.Remove(3));
			ExpectList(list2, 1);
			Assert.That(list2.Remove(1));
			ExpectList(list2);
			AssertThrows<Exception>(delegate() { list2.Pop(); });

			// Add many, SubList(). This will fill 3 arrays (sizes 8, 4, 2) and use
			// 1 element of a size-16 array. Oh, and test the enumerator.
			for (int i = 5; i <= 16; i++)
				list.Add(i);
			ExpectList(list, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
			list2 = list.WithoutLast(6);
			ExpectListByEnumerator(list2, 1, 2, 4, 5, 6, 7, 8, 9, 10);
			AssertThrows<IndexOutOfRangeException>(delegate() { int i = list[-1]; });
			AssertThrows<IndexOutOfRangeException>(delegate() { int i = list[15]; });

			// IndexOf, contains
			Assert.That(list.Contains(11));
			Assert.That(!list2.Contains(11));
			Assert.That(list[list.IndexOf(2)] == 2);
			Assert.That(list[list.IndexOf(1)] == 1);
			Assert.That(list[list.IndexOf(15)] == 15);
			Assert.That(list.IndexOf(3) == -1);

			// PreviousIn(), Last
			VList<int> list3 = list2;
			Assert.AreEqual(11, (list3 = list3.NextIn(list)).Last);
			Assert.AreEqual(12, (list3 = list3.NextIn(list)).Last);
			Assert.AreEqual(13, (list3 = list3.NextIn(list)).Last);
			Assert.AreEqual(14, (list3 = list3.NextIn(list)).Last);
			Assert.AreEqual(15, (list3 = list3.NextIn(list)).Last);
			Assert.AreEqual(16, (list3 = list3.NextIn(list)).Last);
			AssertThrows<Exception>(delegate() { list3.NextIn(list); });

			// Next
			Assert.AreEqual(10, (list3 = list3.WithoutLast(6)).Last);
			Assert.AreEqual(9, (list3 = list3.Tail).Last);
			Assert.AreEqual(8, (list3 = list3.Tail).Last);
			Assert.AreEqual(7, (list3 = list3.Tail).Last);
			Assert.AreEqual(6, (list3 = list3.Tail).Last);
			Assert.AreEqual(5, (list3 = list3.Tail).Last);
			Assert.AreEqual(4, (list3 = list3.Tail).Last);
			Assert.AreEqual(2, (list3 = list3.Tail).Last);
			Assert.AreEqual(1, (list3 = list3.Tail).Last);
			Assert.That((list3 = list3.Tail).IsEmpty);

			// list2 is still the same
			ExpectList(list2, 1, 2, 4, 5, 6, 7, 8, 9, 10);

			// ==, !=, Equals(), AddRange(a, b)
			Assert.That(!list2.Equals("hello"));
			list3 = list2;
			Assert.That(list3.Equals(list2));
			Assert.That(list3 == list2);
			// This AddRange forks the list. List2 ends up with block sizes 8 (3
			// used), 8 (3 used), 4, 2.
			list2.AddRange(list2, list2.WithoutLast(3));
			ExpectList(list2, 1, 2, 4, 5, 6, 7, 8, 9, 10, 8, 9, 10);
			Assert.That(list3 != list2);

			// List3 is a sublist of list, but list2 no longer is
			Assert.That(list3.NextIn(list).Last == 11);
			AssertThrows<InvalidOperationException>(delegate() { list2.NextIn(list); });

			list2 = list2.WithoutLast(3);
			Assert.That(list3 == list2);
		}