From adcba81974979be3763c8d9d46eb2de9a88ff67c Mon Sep 17 00:00:00 2001
From: Peter Melnichenko <mpeterval@gmail.com>
Date: Thu, 6 Oct 2016 16:24:55 +0300
Subject: Add tests for util.sortedpairs

---
 spec/util_spec.lua | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/spec/util_spec.lua b/spec/util_spec.lua
index e6776e4b..2779b1ce 100644
--- a/spec/util_spec.lua
+++ b/spec/util_spec.lua
@@ -116,3 +116,59 @@ describe("Basic tests #blackbox #b_util", function()
       end)
    end)
 end)
+
+test_env.unload_luarocks()
+local util = require("luarocks.util")
+
+describe("Luarocks util test #whitebox #w_util", function()
+   describe("util.sortedpairs", function()
+      local function collect(iter, state, var)
+         local collected = {}
+
+         while true do
+            local returns = {iter(state, var)}
+
+            if returns[1] == nil then
+               return collected
+            else
+               table.insert(collected, returns)
+               var = returns[1]
+            end
+         end
+      end
+
+      it("default sort", function()
+         assert.are.same({}, collect(util.sortedpairs({})))
+         assert.are.same({
+            {1, "v1"},
+            {2, "v2"},
+            {3, "v3"},
+            {"bar", "v5"},
+            {"foo", "v4"}
+         }, collect(util.sortedpairs({"v1", "v2", "v3", foo = "v4", bar = "v5"})))
+      end)
+
+      it("sort by function", function()
+         local function compare(a, b) return a > b end
+         assert.are.same({}, collect(util.sortedpairs({}, compare)))
+         assert.are.same({
+            {3, "v3"},
+            {2, "v2"},
+            {1, "v1"}
+         }, collect(util.sortedpairs({"v1", "v2", "v3"}, compare)))
+      end)
+
+      it("sort by priority table", function()
+         assert.are.same({}, collect(util.sortedpairs({}, {"k1", "k2"})))
+         assert.are.same({
+            {"k3", "v3"},
+            {"k2", "v2", {"sub order"}},
+            {"k1", "v1"},
+            {"k4", "v4"},
+            {"k5", "v5"},
+         }, collect(util.sortedpairs({
+            k1 = "v1", k2 = "v2", k3 = "v3", k4 = "v4", k5 = "v5"
+         }, {"k3", {"k2", {"sub order"}}, "k1"})))
+      end)
+   end)
+end)
-- 
cgit v1.2.3-55-g6feb


From eb7427676a2f09556f9d5f19a1a6392fd945e8bc Mon Sep 17 00:00:00 2001
From: Peter Melnichenko <mpeterval@gmail.com>
Date: Thu, 6 Oct 2016 16:51:04 +0300
Subject: Rewrite util.sortedpairs to avoid using coroutines

util.sortedpairs is used in luarocks loader since @6dc745a.
Openresty does not like coroutines being used from inside `require`,
resulting in "attempt to yield across C-call boundary" error.
New version of util.sortedpairs uses a prepared array of ordered keys
instead of coroutines.

Ref #620.
---
 src/luarocks/util.lua | 79 ++++++++++++++++++++++++++++-----------------------
 1 file changed, 43 insertions(+), 36 deletions(-)

diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua
index 532bea8b..c9fb7d63 100644
--- a/src/luarocks/util.lua
+++ b/src/luarocks/util.lua
@@ -357,52 +357,59 @@ local function default_sort(a, b)
    end
 end
 
--- The iterator function used internally by util.sortedpairs.
+--- A table iterator generator that returns elements sorted by key,
+-- to be used in "for" loops.
 -- @param tbl table: The table to be iterated.
--- @param sort_function function or nil: An optional comparison function
--- to be used by table.sort when sorting keys.
--- @see sortedpairs
-local function sortedpairs_iterator(tbl, sort_function)
-   local ks = util.keys(tbl)
-   if not sort_function or type(sort_function) == "function" then
-      table.sort(ks, sort_function or default_sort)
-      for _, k in ipairs(ks) do
-         coroutine.yield(k, tbl[k])
-      end
+-- @param sort_function function or table or nil: An optional comparison function
+-- to be used by table.sort when sorting keys, or an array listing an explicit order
+-- for keys. If a value itself is an array, it is taken so that the first element
+-- is a string representing the field name, and the second element is a priority table
+-- for that key, which is returned by the iterator as the third value after the key
+-- and the value.
+-- @return function: the iterator function.
+function util.sortedpairs(tbl, sort_function)
+   sort_function = sort_function or default_sort
+   local keys = util.keys(tbl)
+   local sub_orders = {}
+
+   if type(sort_function) == "function" then
+      table.sort(keys, sort_function)
    else
       local order = sort_function
-      local done = {}
-      for _, k in ipairs(order) do
-         local sub_order
-         if type(k) == "table" then
-            sub_order = k[2]
-            k = k[1]
+      local ordered_keys = {}
+      local all_keys = keys
+      keys = {}
+
+      for _, order_entry in ipairs(order) do
+         local key, sub_order
+         if type(order_entry) == "table" then
+            key = order_entry[1]
+            sub_order = order_entry[2]
+         else
+            key = order_entry
          end
-         if tbl[k] then
-            done[k] = true
-            coroutine.yield(k, tbl[k], sub_order)
+
+         if tbl[key] then
+            ordered_keys[key] = true
+            sub_orders[key] = sub_order
+            table.insert(keys, key)
          end
       end
-      table.sort(ks, default_sort)
-      for _, k in ipairs(ks) do
-         if not done[k] then
-            coroutine.yield(k, tbl[k])
+
+      table.sort(all_keys, default_sort)
+      for _, key in ipairs(all_keys) do
+         if not ordered_keys[key] then
+            table.insert(keys, key)
          end
       end
    end
-end
 
---- A table iterator generator that returns elements sorted by key,
--- to be used in "for" loops.
--- @param tbl table: The table to be iterated.
--- @param sort_function function or table or nil: An optional comparison function
--- to be used by table.sort when sorting keys, or an array listing an explicit order
--- for keys. If a value itself is an array, it is taken so that the first element
--- is a string representing the field name, and the second element is a priority table
--- for that key.
--- @return function: the iterator function.
-function util.sortedpairs(tbl, sort_function)
-   return coroutine.wrap(function() sortedpairs_iterator(tbl, sort_function) end)
+   local i = 1
+   return function()
+      local key = keys[i]
+      i = i + 1
+      return key, tbl[key], sub_orders[key]
+   end
 end
 
 function util.lua_versions()
-- 
cgit v1.2.3-55-g6feb