From 7d3e2bd2c4cfd1296d1d6f996d7548de26540d41 Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Fri, 15 Feb 2013 16:27:18 -0800 Subject: Sync with 8 months of Google-internal development. Many things have changed and been simplified. The memory-management story for upb_def and upb_handlers is much more robust; upb_def and upb_handlers should be fairly stable interfaces now. There is still much work to do for the runtime component (upb_sink). --- bindings/lua/LICENSE | 32 + bindings/lua/lunitx/atexit.lua | 32 + bindings/lua/lunitx/lunit.lua | 725 +++++++++++++++++ bindings/lua/lunitx/lunit/console.lua | 156 ++++ bindings/lua/lunitx/lunitx.lua | 21 + bindings/lua/table.c | 167 ++++ bindings/lua/test.lua | 345 +++++--- bindings/lua/upb.c | 1412 +++++++++++++++++---------------- bindings/lua/upb.h | 45 ++ 9 files changed, 2165 insertions(+), 770 deletions(-) create mode 100644 bindings/lua/LICENSE create mode 100644 bindings/lua/lunitx/atexit.lua create mode 100644 bindings/lua/lunitx/lunit.lua create mode 100644 bindings/lua/lunitx/lunit/console.lua create mode 100644 bindings/lua/lunitx/lunitx.lua create mode 100644 bindings/lua/table.c create mode 100644 bindings/lua/upb.h (limited to 'bindings/lua') diff --git a/bindings/lua/LICENSE b/bindings/lua/LICENSE new file mode 100644 index 0000000..fb720fe --- /dev/null +++ b/bindings/lua/LICENSE @@ -0,0 +1,32 @@ + +Lunit License +------------- + +Lunit is written by Michael Roth and is licensed +under the terms of the MIT license reproduced below. + +======================================================================== + +Copyright (c) 2004-2010 Michael Roth + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +======================================================================== + diff --git a/bindings/lua/lunitx/atexit.lua b/bindings/lua/lunitx/atexit.lua new file mode 100644 index 0000000..c3cdddc --- /dev/null +++ b/bindings/lua/lunitx/atexit.lua @@ -0,0 +1,32 @@ + +local actions = {} + +local atexit + +if _VERSION >= 'Lua 5.2' then + + atexit = function (fn) + actions[#actions+1] = setmetatable({}, { __gc = fn }) + end + +else + + local newproxy = newproxy + local debug = debug + local assert = assert + local setmetatable = setmetatable + + local function gc(fn) + local p = assert(newproxy()) + assert(debug.setmetatable(p, { __gc = fn })) + return p + end + + atexit = function (fn) + actions[#actions+1] = gc(fn) + end + +end + +return atexit + diff --git a/bindings/lua/lunitx/lunit.lua b/bindings/lua/lunitx/lunit.lua new file mode 100644 index 0000000..8fa87de --- /dev/null +++ b/bindings/lua/lunitx/lunit.lua @@ -0,0 +1,725 @@ +--[[-------------------------------------------------------------------------- + + This file is part of lunit 0.5. + + For Details about lunit look at: http://www.mroth.net/lunit/ + + Author: Michael Roth + + Copyright (c) 2004, 2006-2010 Michael Roth + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--]]-------------------------------------------------------------------------- + + +local orig_assert = assert + +local pairs = pairs +local ipairs = ipairs +local next = next +local type = type +local error = error +local tostring = tostring +local setmetatable = setmetatable +local pcall = pcall +local xpcall = xpcall +local require = require +local loadfile = loadfile + +local string_sub = string.sub +local string_gsub = string.gsub +local string_format = string.format +local string_lower = string.lower +local string_find = string.find + +local table_concat = table.concat + +local debug_getinfo = debug.getinfo + +local _G = _G + +local lunit + +if _VERSION >= 'Lua 5.2' then + + lunit = {} + _ENV = lunit + +else + + module("lunit") + lunit = _M + +end + + +local __failure__ = {} -- Type tag for failed assertions + +local typenames = { "nil", "boolean", "number", "string", "table", "function", "thread", "userdata" } + + +local traceback_hide -- Traceback function which hides lunit internals +local mypcall -- Protected call to a function with own traceback +do + local _tb_hide = setmetatable( {}, {__mode="k"} ) + + function traceback_hide(func) + _tb_hide[func] = true + end + + local function my_traceback(errobj) + if is_table(errobj) and errobj.type == __failure__ then + local info = debug_getinfo(5, "Sl") -- FIXME: Hardcoded integers are bad... + errobj.where = string_format( "%s:%d", info.short_src, info.currentline) + else + errobj = { msg = tostring(errobj) } + errobj.tb = {} + local i = 2 + while true do + local info = debug_getinfo(i, "Snlf") + if not is_table(info) then + break + end + if not _tb_hide[info.func] then + local line = {} -- Ripped from ldblib.c... + line[#line+1] = string_format("%s:", info.short_src) + if info.currentline > 0 then + line[#line+1] = string_format("%d:", info.currentline) + end + if info.namewhat ~= "" then + line[#line+1] = string_format(" in function '%s'", info.name) + else + if info.what == "main" then + line[#line+1] = " in main chunk" + elseif info.what == "C" or info.what == "tail" then + line[#line+1] = " ?" + else + line[#line+1] = string_format(" in function <%s:%d>", info.short_src, info.linedefined) + end + end + errobj.tb[#errobj.tb+1] = table_concat(line) + end + i = i + 1 + end + end + return errobj + end + + function mypcall(func) + orig_assert( is_function(func) ) + local ok, errobj = xpcall(func, my_traceback) + if not ok then + return errobj + end + end + traceback_hide(mypcall) +end + + +-- Type check functions + +for _, typename in ipairs(typenames) do + lunit["is_"..typename] = function(x) + return type(x) == typename + end +end + +local is_nil = is_nil +local is_boolean = is_boolean +local is_number = is_number +local is_string = is_string +local is_table = is_table +local is_function = is_function +local is_thread = is_thread +local is_userdata = is_userdata + + +local function failure(name, usermsg, defaultmsg, ...) + local errobj = { + type = __failure__, + name = name, + msg = string_format(defaultmsg,...), + usermsg = usermsg + } + error(errobj, 0) +end +traceback_hide( failure ) + + +local function format_arg(arg) + local argtype = type(arg) + if argtype == "string" then + return "'"..arg.."'" + elseif argtype == "number" or argtype == "boolean" or argtype == "nil" then + return tostring(arg) + else + return "["..tostring(arg).."]" + end +end + + +local function selected(map, name) + if not map then + return true + end + + local m = {} + for k,v in pairs(map) do + m[k] = lunitpat2luapat(v) + end + return in_patternmap(m, name) +end + + +function fail(msg) + stats.assertions = stats.assertions + 1 + failure( "fail", msg, "failure" ) +end +traceback_hide( fail ) + + +function assert(assertion, msg) + stats.assertions = stats.assertions + 1 + if not assertion then + failure( "assert", msg, "assertion failed" ) + end + return assertion +end +traceback_hide( assert ) + + +function assert_true(actual, msg) + stats.assertions = stats.assertions + 1 + if actual ~= true then + failure( "assert_true", msg, "true expected but was %s", format_arg(actual) ) + end + return actual +end +traceback_hide( assert_true ) + + +function assert_false(actual, msg) + stats.assertions = stats.assertions + 1 + if actual ~= false then + failure( "assert_false", msg, "false expected but was %s", format_arg(actual) ) + end + return actual +end +traceback_hide( assert_false ) + + +function assert_equal(expected, actual, msg) + stats.assertions = stats.assertions + 1 + if expected ~= actual then + failure( "assert_equal", msg, "expected %s but was %s", format_arg(expected), format_arg(actual) ) + end + return actual +end +traceback_hide( assert_equal ) + + +function assert_not_equal(unexpected, actual, msg) + stats.assertions = stats.assertions + 1 + if unexpected == actual then + failure( "assert_not_equal", msg, "%s not expected but was one", format_arg(unexpected) ) + end + return actual +end +traceback_hide( assert_not_equal ) + + +function assert_match(pattern, actual, msg) + stats.assertions = stats.assertions + 1 + if type(pattern) ~= "string" then + failure( "assert_match", msg, "expected a string as pattern but was %s", format_arg(pattern) ) + end + if type(actual) ~= "string" then + failure( "assert_match", msg, "expected a string to match pattern '%s' but was a %s", pattern, format_arg(actual) ) + end + if not string_find(actual, pattern) then + failure( "assert_match", msg, "expected '%s' to match pattern '%s' but doesn't", actual, pattern ) + end + return actual +end +traceback_hide( assert_match ) + + +function assert_not_match(pattern, actual, msg) + stats.assertions = stats.assertions + 1 + if type(pattern) ~= "string" then + failure( "assert_not_match", msg, "expected a string as pattern but was %s", format_arg(pattern) ) + end + if type(actual) ~= "string" then + failure( "assert_not_match", msg, "expected a string to not match pattern '%s' but was %s", pattern, format_arg(actual) ) + end + if string_find(actual, pattern) then + failure( "assert_not_match", msg, "expected '%s' to not match pattern '%s' but it does", actual, pattern ) + end + return actual +end +traceback_hide( assert_not_match ) + + +function assert_error(msg, func) + stats.assertions = stats.assertions + 1 + if func == nil then + func, msg = msg, nil + end + if type(func) ~= "function" then + failure( "assert_error", msg, "expected a function as last argument but was %s", format_arg(func) ) + end + local ok, errmsg = pcall(func) + if ok then + failure( "assert_error", msg, "error expected but no error occurred" ) + end +end +traceback_hide( assert_error ) + + +function assert_error_match(msg, pattern, func) + stats.assertions = stats.assertions + 1 + if func == nil then + msg, pattern, func = nil, msg, pattern + end + if type(pattern) ~= "string" then + failure( "assert_error_match", msg, "expected the pattern as a string but was %s", format_arg(pattern) ) + end + if type(func) ~= "function" then + failure( "assert_error_match", msg, "expected a function as last argument but was %s", format_arg(func) ) + end + local ok, errmsg = pcall(func) + if ok then + failure( "assert_error_match", msg, "error expected but no error occurred" ) + end + if type(errmsg) ~= "string" then + failure( "assert_error_match", msg, "error as string expected but was %s", format_arg(errmsg) ) + end + if not string_find(errmsg, pattern) then + failure( "assert_error_match", msg, "expected error '%s' to match pattern '%s' but doesn't", errmsg, pattern ) + end +end +traceback_hide( assert_error_match ) + + +function assert_pass(msg, func) + stats.assertions = stats.assertions + 1 + if func == nil then + func, msg = msg, nil + end + if type(func) ~= "function" then + failure( "assert_pass", msg, "expected a function as last argument but was %s", format_arg(func) ) + end + local ok, errmsg = pcall(func) + if not ok then + failure( "assert_pass", msg, "no error expected but error was: '%s'", errmsg ) + end +end +traceback_hide( assert_pass ) + + +-- lunit.assert_typename functions + +for _, typename in ipairs(typenames) do + local assert_typename = "assert_"..typename + lunit[assert_typename] = function(actual, msg) + stats.assertions = stats.assertions + 1 + if type(actual) ~= typename then + failure( assert_typename, msg, "%s expected but was %s", typename, format_arg(actual) ) + end + return actual + end + traceback_hide( lunit[assert_typename] ) +end + + +-- lunit.assert_not_typename functions + +for _, typename in ipairs(typenames) do + local assert_not_typename = "assert_not_"..typename + lunit[assert_not_typename] = function(actual, msg) + stats.assertions = stats.assertions + 1 + if type(actual) == typename then + failure( assert_not_typename, msg, typename.." not expected but was one" ) + end + end + traceback_hide( lunit[assert_not_typename] ) +end + + +function lunit.clearstats() + stats = { + assertions = 0; + passed = 0; + failed = 0; + errors = 0; + } +end + + +local report, reporterrobj +do + local testrunner + + function lunit.setrunner(newrunner) + if not ( is_table(newrunner) or is_nil(newrunner) ) then + return error("lunit.setrunner: Invalid argument", 0) + end + local oldrunner = testrunner + testrunner = newrunner + return oldrunner + end + + function lunit.loadrunner(name) + if not is_string(name) then + return error("lunit.loadrunner: Invalid argument", 0) + end + local ok, runner = pcall( require, name ) + if not ok then + return error("lunit.loadrunner: Can't load test runner: "..runner, 0) + end + return setrunner(runner) + end + + function lunit.getrunner() + return testrunner + end + + function report(event, ...) + local f = testrunner and testrunner[event] + if is_function(f) then + pcall(f, ...) + end + end + + function reporterrobj(context, tcname, testname, errobj) + local fullname = tcname .. "." .. testname + if context == "setup" then + fullname = fullname .. ":" .. setupname(tcname, testname) + elseif context == "teardown" then + fullname = fullname .. ":" .. teardownname(tcname, testname) + end + if errobj.type == __failure__ then + stats.failed = stats.failed + 1 + report("fail", fullname, errobj.where, errobj.msg, errobj.usermsg) + else + stats.errors = stats.errors + 1 + report("err", fullname, errobj.msg, errobj.tb) + end + end +end + + + +local function key_iter(t, k) + return (next(t,k)) +end + + +local testcase +do + -- Array with all registered testcases + local _testcases = {} + + -- Marks a module as a testcase. + -- Applied over a module from module("xyz", lunit.testcase). + function lunit.testcase(m) + orig_assert( is_table(m) ) + --orig_assert( m._M == m ) + orig_assert( is_string(m._NAME) ) + --orig_assert( is_string(m._PACKAGE) ) + + -- Register the module as a testcase + _testcases[m._NAME] = m + + -- Import lunit, fail, assert* and is_* function to the module/testcase + m.lunit = lunit + m.fail = lunit.fail + for funcname, func in pairs(lunit) do + if "assert" == string_sub(funcname, 1, 6) or "is_" == string_sub(funcname, 1, 3) then + m[funcname] = func + end + end + end + + function lunit.module(name,seeall) + local m = {} + if seeall == "seeall" then + setmetatable(m, { __index = _G }) + end + m._NAME = name + lunit.testcase(m) + return m + end + + -- Iterator (testcasename) over all Testcases + function lunit.testcases() + -- Make a copy of testcases to prevent confusing the iterator when + -- new testcase are defined + local _testcases2 = {} + for k,v in pairs(_testcases) do + _testcases2[k] = true + end + return key_iter, _testcases2, nil + end + + function testcase(tcname) + return _testcases[tcname] + end +end + + +do + -- Finds a function in a testcase case insensitive + local function findfuncname(tcname, name) + for key, value in pairs(testcase(tcname)) do + if is_string(key) and is_function(value) and string_lower(key) == name then + return key + end + end + end + + function lunit.setupname(tcname) + return findfuncname(tcname, "setup") + end + + function lunit.teardownname(tcname) + return findfuncname(tcname, "teardown") + end + + -- Iterator over all test names in a testcase. + -- Have to collect the names first in case one of the test + -- functions creates a new global and throws off the iteration. + function lunit.tests(tcname) + local testnames = {} + for key, value in pairs(testcase(tcname)) do + if is_string(key) and is_function(value) then + local lfn = string_lower(key) + if string_sub(lfn, 1, 4) == "test" or string_sub(lfn, -4) == "test" then + testnames[key] = true + end + end + end + return key_iter, testnames, nil + end +end + + + + +function lunit.runtest(tcname, testname) + orig_assert( is_string(tcname) ) + orig_assert( is_string(testname) ) + + if (not getrunner()) then + loadrunner("lunit.console") + end + + local function callit(context, func) + if func then + local err = mypcall(func) + if err then + reporterrobj(context, tcname, testname, err) + return false + end + end + return true + end + traceback_hide(callit) + + report("run", tcname, testname) + + local tc = testcase(tcname) + local setup = tc[setupname(tcname)] + local test = tc[testname] + local teardown = tc[teardownname(tcname)] + + local setup_ok = callit( "setup", setup ) + local test_ok = setup_ok and callit( "test", test ) + local teardown_ok = setup_ok and callit( "teardown", teardown ) + + if setup_ok and test_ok and teardown_ok then + stats.passed = stats.passed + 1 + report("pass", tcname, testname) + end +end +traceback_hide(runtest) + + + +function lunit.run(testpatterns) + clearstats() + report("begin") + for testcasename in lunit.testcases() do + -- Run tests in the testcases + for testname in lunit.tests(testcasename) do + if selected(testpatterns, testname) then + runtest(testcasename, testname) + end + end + end + report("done") + return stats +end +traceback_hide(run) + + +function lunit.loadonly() + clearstats() + report("begin") + report("done") + return stats +end + + + + + + + + + +local lunitpat2luapat +do + local conv = { + ["^"] = "%^", + ["$"] = "%$", + ["("] = "%(", + [")"] = "%)", + ["%"] = "%%", + ["."] = "%.", + ["["] = "%[", + ["]"] = "%]", + ["+"] = "%+", + ["-"] = "%-", + ["?"] = ".", + ["*"] = ".*" + } + function lunitpat2luapat(str) + --return "^" .. string.gsub(str, "%W", conv) .. "$" + -- Above was very annoying, if I want to run all the tests having to do with + -- RSS, I want to be able to do "-t rss" not "-t \*rss\*". + return string_gsub(str, "%W", conv) + end +end + + + +local function in_patternmap(map, name) + if map[name] == true then + return true + else + for _, pat in ipairs(map) do + if string_find(name, pat) then + return true + end + end + end + return false +end + + + + + + + + +-- Called from 'lunit' shell script. + +function main(argv) + argv = argv or {} + + -- FIXME: Error handling and error messages aren't nice. + + local function checkarg(optname, arg) + if not is_string(arg) then + return error("lunit.main: option "..optname..": argument missing.", 0) + end + end + + local function loadtestcase(filename) + if not is_string(filename) then + return error("lunit.main: invalid argument") + end + local chunk, err = loadfile(filename) + if err then + return error(err) + else + chunk() + end + end + + local testpatterns = nil + local doloadonly = false + + local i = 0 + while i < #argv do + i = i + 1 + local arg = argv[i] + if arg == "--loadonly" then + doloadonly = true + elseif arg == "--runner" or arg == "-r" then + local optname = arg; i = i + 1; arg = argv[i] + checkarg(optname, arg) + loadrunner(arg) + elseif arg == "--test" or arg == "-t" then + local optname = arg; i = i + 1; arg = argv[i] + checkarg(optname, arg) + testpatterns = testpatterns or {} + testpatterns[#testpatterns+1] = arg + elseif arg == "--help" or arg == "-h" then + print[[ +lunit 0.5 +Copyright (c) 2004-2009 Michael Roth +This program comes WITHOUT WARRANTY OF ANY KIND. + +Usage: lua test [OPTIONS] [--] scripts + +Options: + + -r, --runner RUNNER Testrunner to use, defaults to 'lunit-console'. + -t, --test PATTERN Which tests to run, may contain * or ? wildcards. + --loadonly Only load the tests. + -h, --help Print this help screen. + +Please report bugs to . +]] + return + elseif arg == "--" then + while i < #argv do + i = i + 1; arg = argv[i] + loadtestcase(arg) + end + else + loadtestcase(arg) + end + end + + if doloadonly then + return loadonly() + else + return run(testpatterns) + end +end + +clearstats() + +return lunit diff --git a/bindings/lua/lunitx/lunit/console.lua b/bindings/lua/lunitx/lunit/console.lua new file mode 100644 index 0000000..0ff22a4 --- /dev/null +++ b/bindings/lua/lunitx/lunit/console.lua @@ -0,0 +1,156 @@ + +--[[-------------------------------------------------------------------------- + + This file is part of lunit 0.5. + + For Details about lunit look at: http://www.mroth.net/lunit/ + + Author: Michael Roth + + Copyright (c) 2006-2008 Michael Roth + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--]]-------------------------------------------------------------------------- + + + +--[[ + + begin() + run(testcasename, testname) + err(fullname, message, traceback) + fail(fullname, where, message, usermessage) + pass(testcasename, testname) + done() + + Fullname: + testcase.testname + testcase.testname:setupname + testcase.testname:teardownname + +--]] + + +lunit = require "lunit" + +local lunit_console + +if _VERSION >= 'Lua 5.2' then + + lunit_console = setmetatable({},{__index = _ENV}) + _ENV = lunit_console + +else + + module( "lunit-console", package.seeall ) + lunit_console = _M + +end + + + +local function printformat(format, ...) + io.write( string.format(format, ...) ) +end + + +local columns_printed = 0 + +local function writestatus(char) + if columns_printed == 0 then + io.write(" ") + end + if columns_printed == 60 then + io.write("\n ") + columns_printed = 0 + end + io.write(char) + io.flush() + columns_printed = columns_printed + 1 +end + + +local msgs = {} + + +function begin() + local total_tc = 0 + local total_tests = 0 + + msgs = {} -- e + + for tcname in lunit.testcases() do + total_tc = total_tc + 1 + for testname, test in lunit.tests(tcname) do + total_tests = total_tests + 1 + end + end + + printformat("Loaded testsuite with %d tests in %d testcases.\n\n", total_tests, total_tc) +end + + +function run(testcasename, testname) + -- NOP +end + + +function err(fullname, message, traceback) + writestatus("E") + msgs[#msgs+1] = "Error! ("..fullname.."):\n"..message.."\n\t"..table.concat(traceback, "\n\t") .. "\n" +end + + +function fail(fullname, where, message, usermessage) + writestatus("F") + local text = "Failure ("..fullname.."):\n".. + where..": "..message.."\n" + + if usermessage then + text = text .. where..": "..usermessage.."\n" + end + + msgs[#msgs+1] = text +end + + +function pass(testcasename, testname) + writestatus(".") +end + + + +function done() + printformat("\n\n%d Assertions checked.\n", lunit.stats.assertions ) + print() + + for i, msg in ipairs(msgs) do + printformat( "%3d) %s\n", i, msg ) + end + + printformat("Testsuite finished (%d passed, %d failed, %d errors).\n", + lunit.stats.passed, lunit.stats.failed, lunit.stats.errors ) +end + + +return lunit_console + + diff --git a/bindings/lua/lunitx/lunitx.lua b/bindings/lua/lunitx/lunitx.lua new file mode 100644 index 0000000..7656e6a --- /dev/null +++ b/bindings/lua/lunitx/lunitx.lua @@ -0,0 +1,21 @@ +local atexit = require "atexit" +local lunit = require "lunit" + +--for k,v in pairs(debug.getinfo(1,"S")) do print(k,v) end +-- autonameing +-- module("bcrc-test", lunit.testcase, package.seeall) + +atexit(function() + local _, emsg = xpcall(function() + lunit.main(arg) + end, debug.traceback) + if emsg then + print(emsg) + os.exit(1) + end + if lunit.stats.failed > 0 or lunit.stats.errors > 0 then + os.exit(1) + end +end) + +return lunit diff --git a/bindings/lua/table.c b/bindings/lua/table.c new file mode 100644 index 0000000..31b92d2 --- /dev/null +++ b/bindings/lua/table.c @@ -0,0 +1,167 @@ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2012 Google Inc. See LICENSE for details. + * Author: Josh Haberman + * + * Lua extension that provides access to upb_table. This is an internal-only + * interface and exists for the sole purpose of writing a C code generator in + * Lua that can dump a upb_table as static C initializers. This lets us use + * Lua for convenient string manipulation while saving us from re-implementing + * the upb_table hash function and hash table layout / collision strategy in + * Lua. + * + * Since this is used only as part of the toolchain (and not part of the + * runtime) we do not hold this module to the same stringent requirements as + * the main Lua modules (for example that misbehaving Lua programs cannot + * crash the interpreter). + */ + +#include +#include +#include +#include +#include "lauxlib.h" +#include "bindings/lua/upb.h" +#include "upb/def.h" + +static void lupbtable_setnum(lua_State *L, int tab, const char *key, + lua_Number val) { + lua_pushnumber(L, val); + lua_setfield(L, tab - 1, key); +} + +static void lupbtable_pushval(lua_State *L, upb_value val, upb_ctype_t type) { + switch (type) { + case UPB_CTYPE_INT32: + lua_pushnumber(L, upb_value_getint32(val)); + break; + case UPB_CTYPE_PTR: + lupb_def_pushwrapper(L, upb_value_getptr(val), NULL); + break; + case UPB_CTYPE_CSTR: + lua_pushstring(L, upb_value_getcstr(val)); + break; + default: + luaL_error(L, "Unexpected type: %d", type); + } +} + +// Sets a few fields common to both hash table entries and arrays. +static void lupbtable_setmetafields(lua_State *L, int type, const void *ptr) { + // We tack this onto every entry so we know it even if the entries + // don't stay with the table. + lua_pushnumber(L, type); + lua_setfield(L, -2, "valtype"); + + // Set this to facilitate linking. + lua_pushlightuserdata(L, (void*)ptr); + lua_setfield(L, -2, "ptr"); +} + +static void lupbtable_pushent(lua_State *L, const upb_tabent *e, + bool inttab, int type) { + lua_newtable(L); + if (!upb_tabent_isempty(e)) { + if (inttab) { + lua_pushnumber(L, e->key.num); + } else { + lua_pushstring(L, e->key.str); + } + lua_setfield(L, -2, "key"); + lupbtable_pushval(L, e->val, type); + lua_setfield(L, -2, "value"); + } + lua_pushlightuserdata(L, (void*)e->next); + lua_setfield(L, -2, "next"); + lupbtable_setmetafields(L, type, e); +} + +// Dumps the shared part of upb_table into a Lua table. +static void lupbtable_pushtable(lua_State *L, const upb_table *t, bool inttab) { + lua_newtable(L); + lupbtable_setnum(L, -1, "count", t->count); + lupbtable_setnum(L, -1, "mask", t->mask); + lupbtable_setnum(L, -1, "type", t->type); + lupbtable_setnum(L, -1, "size_lg2", t->size_lg2); + + lua_newtable(L); + for (int i = 0; i < upb_table_size(t); i++) { + lupbtable_pushent(L, &t->entries[i], inttab, t->type); + lua_rawseti(L, -2, i + 1); + } + lua_setfield(L, -2, "entries"); +} + +// Dumps a upb_inttable to a Lua table. +static void lupbtable_pushinttable(lua_State *L, const upb_inttable *t) { + lupbtable_pushtable(L, &t->t, true); + lupbtable_setnum(L, -1, "array_size", t->array_size); + lupbtable_setnum(L, -1, "array_count", t->array_count); + + lua_newtable(L); + for (int i = 0; i < t->array_size; i++) { + lua_newtable(L); + if (upb_arrhas(t->array[i])) { + lupbtable_pushval(L, t->array[i], t->t.type); + lua_setfield(L, -2, "val"); + } + lupbtable_setmetafields(L, t->t.type, &t->array[i]); + lua_rawseti(L, -2, i + 1); + } + lua_setfield(L, -2, "array"); +} + +static void lupbtable_pushstrtable(lua_State *L, const upb_strtable *t) { + lupbtable_pushtable(L, &t->t, false); +} + +static int lupbtable_msgdef_itof(lua_State *L) { + const upb_msgdef *m = lupb_msgdef_check(L, 1); + lupbtable_pushinttable(L, &m->itof); + return 1; +} + +static int lupbtable_msgdef_ntof(lua_State *L) { + const upb_msgdef *m = lupb_msgdef_check(L, 1); + lupbtable_pushstrtable(L, &m->ntof); + return 1; +} + +static int lupbtable_enumdef_iton(lua_State *L) { + const upb_enumdef *e = lupb_enumdef_check(L, 1); + lupbtable_pushinttable(L, &e->iton); + return 1; +} + +static int lupbtable_enumdef_ntoi(lua_State *L) { + const upb_enumdef *e = lupb_enumdef_check(L, 1); + lupbtable_pushstrtable(L, &e->ntoi); + return 1; +} + +static void lupbtable_setfieldi(lua_State *L, const char *field, int i) { + lua_pushnumber(L, i); + lua_setfield(L, -2, field); +} + +static const struct luaL_Reg lupbtable_toplevel_m[] = { + {"msgdef_itof", lupbtable_msgdef_itof}, + {"msgdef_ntof", lupbtable_msgdef_ntof}, + {"enumdef_iton", lupbtable_enumdef_iton}, + {"enumdef_ntoi", lupbtable_enumdef_ntoi}, + {NULL, NULL} +}; + +int luaopen_upbtable(lua_State *L) { + lupb_newlib(L, "upb.table", lupbtable_toplevel_m); + + // We define these here because they are not public (at least at the moment). + lupbtable_setfieldi(L, "CTYPE_PTR", UPB_CTYPE_PTR); + lupbtable_setfieldi(L, "CTYPE_INT32", UPB_CTYPE_INT32); + + lua_pushlightuserdata(L, NULL); + lua_setfield(L, -2, "NULL"); + + return 1; // Return a single Lua value, the package table created above. +} diff --git a/bindings/lua/test.lua b/bindings/lua/test.lua index 42bce25..6b162a9 100644 --- a/bindings/lua/test.lua +++ b/bindings/lua/test.lua @@ -1,109 +1,262 @@ -require "upb" +local upb = require "upb" +local lunit = require "lunitx" -symtab = upb.SymbolTable{ - upb.MessageDef{fqname="A", fields={ - upb.FieldDef{name="a", type=upb.TYPE_INT32, number=1}, - upb.FieldDef{name="b", type=upb.TYPE_DOUBLE, number=2}} +if _VERSION >= 'Lua 5.2' then + _ENV = lunit.module("testupb", "seeall") +else + module("testupb", lunit.testcase, package.seeall) +end + +function test_fielddef() + local f = upb.FieldDef() + assert_false(f:is_frozen()) + assert_nil(f:number()) + assert_nil(f:name()) + assert_equal(upb.LABEL_OPTIONAL, f:label()) + + f:set_name("foo_field") + f:set_number(3) + f:set_label(upb.LABEL_REPEATED) + f:set_type(upb.TYPE_FLOAT) + + assert_equal("foo_field", f:name()) + assert_equal(3, f:number()) + assert_equal(upb.LABEL_REPEATED, f:label()) + assert_equal(upb.TYPE_FLOAT, f:type()) + + local f2 = upb.FieldDef{ + name = "foo", number = 5, type = upb.TYPE_DOUBLE, label = upb.LABEL_REQUIRED } -} - -symtab = upb.SymbolTable{ - upb.MessageDef{fqname="A", fields={ - upb.FieldDef{name="a", type=upb.TYPE_INT32, number=1}, - upb.FieldDef{name="b", type=upb.TYPE_DOUBLE, number=2}} - }, - upb.MessageDef{fqname="B"} -} -A, B, C = symtab:lookup("A", "B") -print(A) -print(B) -print(C) - -a = A() -a2 = upb.Message(A) -print("YO! a.a=" .. tostring(a.a) .. ", a2.a=" .. tostring(a2.a)) -a.a = 2 -a2.a = 3 -print("YO! a.a=" .. tostring(a.a) .. ", a2.a=" .. tostring(a2.a)) - -A = symtab:lookup("A") -if not A then - error("Could not find A") + + assert_equal("foo", f2:name()) + assert_equal(5, f2:number()) + assert_equal(upb.TYPE_DOUBLE, f2:type()) + assert_equal(upb.LABEL_REQUIRED, f2:label()) end -f = io.open("../../upb/descriptor.pb") -if not f then - error("Couldn't open descriptor.pb, try running 'make descriptorgen'") +function test_enumdef() + local e = upb.EnumDef() + assert_equal(0, #e) + assert_nil(e:value(5)) + assert_nil(e:value("NONEXISTENT_NAME")) + + for name, value in e:values() do + fail() + end + + e:add("VAL1", 1) + e:add("VAL2", 2) + + local values = {} + for name, value in e:values() do + values[name] = value + end + + assert_equal(1, values["VAL1"]) + assert_equal(2, values["VAL2"]) + + local e2 = upb.EnumDef{ + values = { + {"FOO", 1}, + {"BAR", 77}, + } + } + + assert_equal(1, e2:value("FOO")) + assert_equal(77, e2:value("BAR")) + assert_equal("FOO", e2:value(1)) + assert_equal("BAR", e2:value(77)) end -symtab:parsedesc(f:read("*all")) -symtab:load_descriptor() -symtab:load_descriptor_file() -upb.pb.load_descriptor(f:read("*all")) +function test_empty_msgdef() + local md = upb.MessageDef() + assert_nil(md:full_name()) -- Def without name is anonymous. + assert_false(md:is_frozen()) + assert_equal(0, #md) + assert_nil(md:field("nonexistent_field")) + assert_nil(md:field(3)) + for field in md:fields() do + fail() + end + + upb.freeze(md) + assert_true(md:is_frozen()) + assert_equal(0, #md) + assert_nil(md:field("nonexistent_field")) + assert_nil(md:field(3)) + for field in md:fields() do + fail() + end +end + +function test_msgdef_constructor() + local f1 = upb.FieldDef{name = "field1", number = 7, type = upb.TYPE_INT32} + local f2 = upb.FieldDef{name = "field2", number = 8, type = upb.TYPE_INT32} + local md = upb.MessageDef{ + full_name = "TestMessage", + fields = {f1, f2} + } + assert_equal("TestMessage", md:full_name()) + assert_false(md:is_frozen()) + assert_equal(2, #md) + assert_equal(f1, md:field("field1")) + assert_equal(f2, md:field("field2")) + assert_equal(f1, md:field(7)) + assert_equal(f2, md:field(8)) + local count = 0 + local found = {} + for field in md:fields() do + count = count + 1 + found[field] = true + end + assert_equal(2, count) + assert_true(found[f1]) + assert_true(found[f2]) -upb.pb.load_descriptor_file("../../src/descriptor.pb", symtab) + upb.freeze(md) +end + +function test_msgdef_setters() + local md = upb.MessageDef() + md:set_full_name("Message1") + assert_equal("Message1", md:full_name()) + local f = upb.FieldDef{name = "field1", number = 3, type = upb.TYPE_DOUBLE} + md:add{f} + assert_equal(1, #md) + assert_equal(f, md:field("field1")) +end + +function test_msgdef_errors() + assert_error(function() upb.MessageDef{bad_initializer_key = 5} end) + local md = upb.MessageDef() + assert_error(function() + -- Duplicate field number. + upb.MessageDef{ + fields = { + upb.FieldDef{name = "field1", number = 1, type = upb.TYPE_INT32}, + upb.FieldDef{name = "field2", number = 1, type = upb.TYPE_INT32} + } + } + end) + assert_error(function() + -- Duplicate field name. + upb.MessageDef{ + fields = { + upb.FieldDef{name = "field1", number = 1, type = upb.TYPE_INT32}, + upb.FieldDef{name = "field1", number = 2, type = upb.TYPE_INT32} + } + } + end) + + -- attempt to set a name with embedded NULLs. + assert_error_match("names cannot have embedded NULLs", function() + md:set_full_name("abc\0def") + end) + + upb.freeze(md) + -- Attempt to mutate frozen MessageDef. + -- TODO(haberman): better error message and test for message. + assert_error(function() + md:add{upb.FieldDef{name = "field1", number = 1, type = upb.TYPE_INT32}} + end) + assert_error(function() + md:set_full_name("abc") + end) + + -- Attempt to freeze a msgdef without freezing its subdef. + assert_error_match("is not frozen or being frozen", function() + m1 = upb.MessageDef() + upb.freeze( + upb.MessageDef{ + fields = { + upb.FieldDef{name = "f1", number = 1, type = upb.TYPE_MESSAGE, + subdef = m1} + } + } + ) + end) +end + +function test_symtab() + local empty = upb.SymbolTable() + assert_equal(0, #empty:getdefs(upb.DEF_ANY)) + + local symtab = upb.SymbolTable{ + upb.MessageDef{full_name = "TestMessage"}, + upb.MessageDef{full_name = "ContainingMessage", fields = { + upb.FieldDef{name = "field1", number = 1, type = upb.TYPE_INT32}, + upb.FieldDef{name = "field2", number = 2, type = upb.TYPE_MESSAGE, + subdef_name = ".TestMessage"} + } + } + } + + local msgdef1 = symtab:lookup("TestMessage") + local msgdef2 = symtab:lookup("ContainingMessage") + assert_not_nil(msgdef1) + assert_not_nil(msgdef2) + assert_equal(msgdef1, msgdef2:field("field2"):subdef()) + assert_true(msgdef1:is_frozen()) + assert_true(msgdef2:is_frozen()) + + symtab:add{ + upb.MessageDef{full_name = "ContainingMessage2", fields = { + upb.FieldDef{name = "field5", number = 5, type = upb.TYPE_MESSAGE, + subdef = msgdef2} + } + } + } + + local msgdef3 = symtab:lookup("ContainingMessage2") + assert_not_nil(msgdef3) + assert_equal(msgdef3:field("field5"):subdef(), msgdef2) +end -f = io.open("../../benchmarks/google_messages.proto.pb") -if not f then - error("Couldn't open google_messages.proto.pb, try running 'make benchmarks'") +-- Lua 5.1 and 5.2 have slightly different semantics for how a finalizer +-- can be defined in Lua. +if _VERSION >= 'Lua 5.2' then + function defer(fn) + setmetatable({}, { __gc = fn }) + end +else + function defer(fn) + getmetatable(newproxy(true)).__gc = fn + end end -symtab:parsedesc(f:read("*all")) -for _, def in ipairs(symtab:getdefs(-1)) do - print(def:name()) +function test_finalizer() + -- Tests that we correctly handle a call into an already-finalized object. + -- Collectible objects are finalized in the opposite order of creation. + do + local t = {} + defer(function() + assert_error_match("called into dead def", function() + -- Generic def call. + t[1]:full_name() + end) + assert_error_match("called into dead msgdef", function() + -- Specific msgdef call. + t[1]:add() + end) + assert_error_match("called into dead enumdef", function() + t[2]:values() + end) + assert_error_match("called into dead fielddef", function() + t[3]:number() + end) + assert_error_match("called into dead symtab", + function() t[4]:lookup() + end) + end) + t = { + upb.MessageDef(), + upb.EnumDef(), + upb.FieldDef(), + upb.SymbolTable(), + } + end + collectgarbage() end -SpeedMessage1 = symtab:lookup("benchmarks.SpeedMessage1") -SpeedMessage2 = symtab:lookup("benchmarks.SpeedMessage2") -print(SpeedMessage1:name()) - -msg = MyType() -msg:Decode(str) - -msg:DecodeJSON(str) - -msg = upb.pb.decode(str, MyType) -str = upb.pb.encode(msg) - -msg = upb.pb.decode_text(str, MyType) -str = upb.pb.encode_text(msg) - -upb.clear(msg) -upb.msgdef(msg) -upb.has(msg, "foo_bar") - -msg = upb.json.decode(str, MyType) - -msg = upb.pb.DecodeText(str) -msg = upb.pb.EncodeText(msg) -upb. - -upb.pb.decode_into(msg, str) - -str = upb.json.Encode(msg) -upb.json.DecodeInto(msg, str) -f = assert(io.open("../../benchmarks/google_message1.dat")) -msg:Parse(f:read("*all")) -print(msg:ToText()) -print(upb.json.encode(msg)) - -msg = SpeedMessage2() -f = assert(io.open("../../benchmarks/google_message2.dat")) -msg:Parse(f:read("*all")) -print(msg:ToText()) ---msg:Serialize() ---msg:FromText(str) --- print(msg.field129) --- print(msg.field271) ---print(msg.field15.field15) ---msg.field15.field15 = "my override" ---print(msg.field15.field15) --- print(msg.field1) --- print(msg.field1) --- msg.field1 = "YEAH BABY!" --- print(msg.field1) --- print(msg.field129) --- msg.field129 = 5 --- print(msg.field129) ---]] +lunit.main() diff --git a/bindings/lua/upb.c b/bindings/lua/upb.c index 4cce4b6..1a1d7c0 100644 --- a/bindings/lua/upb.c +++ b/bindings/lua/upb.c @@ -4,96 +4,85 @@ * Copyright (c) 2009 Google Inc. See LICENSE for details. * Author: Josh Haberman * - * A Lua extension for upb. + * A Lua extension for upb. Exposes only the core library + * (sub-libraries are exposed in other extensions). */ -#include -#include #include +#include +#include +#include #include "lauxlib.h" -#include "upb/def.h" -#include "upb/msg.h" +#include "bindings/lua/upb.h" +#include "upb/bytestream.h" #include "upb/pb/glue.h" -#if LUA_VERSION_NUM == 501 -#define lua_rawlen lua_objlen -#endif +// Lua metatable types. +#define LUPB_MSGDEF "lupb.msgdef" +#define LUPB_ENUMDEF "lupb.enumdef" +#define LUPB_FIELDDEF "lupb.fielddef" +#define LUPB_SYMTAB "lupb.symtab" -static bool streql(const char *a, const char *b) { return strcmp(a, b) == 0; } +// Other table constants. +#define LUPB_OBJCACHE "lupb.objcache" -static bool lupb_isint(double n) { return (double)(int)n == n; } +#if LUA_VERSION_NUM == 501 -static uint8_t lupb_touint8(lua_State *L, int narg, const char *name) { - lua_Number n = lua_tonumber(L, narg); - if (n > UINT8_MAX || n < 0 || rint(n) != n) - luaL_error(L, "Invalid %s", name); - return n; +// Taken from Lua 5.2's source. +void *luaL_testudata(lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + luaL_getmetatable(L, tname); /* get correct metatable */ + if (!lua_rawequal(L, -1, -2)) /* not the same? */ + p = NULL; /* value is a userdata with wrong metatable */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + return NULL; /* value is not a userdata with a metatable */ } -static uint32_t lupb_touint32(lua_State *L, int narg, const char *name) { - lua_Number n = lua_tonumber(L, narg); - if (n > UINT32_MAX || n < 0 || rint(n) != n) - luaL_error(L, "Invalid %s", name); - return n; +#elif LUA_VERSION_NUM == 502 + +int luaL_typerror(lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", + tname, luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); } -static void lupb_pushstring(lua_State *L, const upb_strref *ref) { - if (ref->ptr) { - lua_pushlstring(L, ref->ptr, ref->len); - } else { - // Lua requires a continguous string; must copy+allocate. - char *str = upb_strref_dup(ref); - lua_pushlstring(L, str, ref->len); - free(str); - } +#else +#error Only Lua 5.1 and 5.2 are supported +#endif + +const char *lupb_checkname(lua_State *L, int narg) { + size_t len; + const char *name = luaL_checklstring(L, narg, &len); + if (strlen(name) != len) + luaL_error(L, "names cannot have embedded NULLs"); + return name; } -static void lupb_pushvalue(lua_State *L, upb_value val, upb_fielddef *f) { - switch (f->type) { - case UPB_TYPE(INT32): - case UPB_TYPE(SINT32): - case UPB_TYPE(SFIXED32): - case UPB_TYPE(ENUM): - lua_pushnumber(L, upb_value_getint32(val)); break; - case UPB_TYPE(INT64): - case UPB_TYPE(SINT64): - case UPB_TYPE(SFIXED64): - lua_pushnumber(L, upb_value_getint64(val)); break; - case UPB_TYPE(UINT32): - case UPB_TYPE(FIXED32): - lua_pushnumber(L, upb_value_getuint32(val)); break; - case UPB_TYPE(UINT64): - case UPB_TYPE(FIXED64): - lua_pushnumber(L, upb_value_getuint64(val)); break; - case UPB_TYPE(DOUBLE): - lua_pushnumber(L, upb_value_getdouble(val)); break; - case UPB_TYPE(FLOAT): - lua_pushnumber(L, upb_value_getfloat(val)); break; - case UPB_TYPE(BOOL): - lua_pushboolean(L, upb_value_getbool(val)); break; - default: luaL_error(L, "internal error"); - } +static bool streql(const char *a, const char *b) { return strcmp(a, b) == 0; } + +static uint32_t lupb_checkint32(lua_State *L, int narg, const char *name) { + lua_Number n = lua_tonumber(L, narg); + if (n > INT32_MAX || n < INT32_MIN || rint(n) != n) + luaL_error(L, "Invalid %s", name); + return n; } -// Returns a scalar value (ie. not a submessage) as a upb_value. -static upb_value lupb_getvalue(lua_State *L, int narg, upb_fielddef *f, - upb_strref *ref) { - assert(!upb_issubmsg(f)); +// Converts a number or bool from Lua -> upb_value. +static upb_value lupb_getvalue(lua_State *L, int narg, upb_fieldtype_t type) { upb_value val; - if (upb_fielddef_type(f) == UPB_TYPE(BOOL)) { + if (type == UPB_TYPE(BOOL)) { if (!lua_isboolean(L, narg)) luaL_error(L, "Must explicitly pass true or false for boolean fields"); upb_value_setbool(&val, lua_toboolean(L, narg)); - } else if (upb_fielddef_type(f) == UPB_TYPE(STRING)) { - size_t len; - ref->ptr = luaL_checklstring(L, narg, &len); - ref->len = len; - upb_value_setstrref(&val, ref); } else { // Numeric type. - lua_Number num = 0; - num = luaL_checknumber(L, narg); - switch (upb_fielddef_type(f)) { + lua_Number num = luaL_checknumber(L, narg); + switch (type) { case UPB_TYPE(INT32): case UPB_TYPE(SINT32): case UPB_TYPE(SFIXED32): @@ -133,374 +122,743 @@ static upb_value lupb_getvalue(lua_State *L, int narg, upb_fielddef *f, luaL_error(L, "Cannot convert %f to float", num); upb_value_setfloat(&val, num); break; + default: luaL_error(L, "invalid type"); } } return val; } -static void lupb_typecheck(lua_State *L, int narg, upb_fielddef *f) { - upb_strref ref; - lupb_getvalue(L, narg, f, &ref); +// Converts a upb_value -> Lua value. +static void lupb_pushvalue(lua_State *L, upb_value val, upb_fieldtype_t type) { + switch (type) { + case UPB_TYPE(INT32): + case UPB_TYPE(SINT32): + case UPB_TYPE(SFIXED32): + case UPB_TYPE(ENUM): + lua_pushnumber(L, upb_value_getint32(val)); break; + case UPB_TYPE(INT64): + case UPB_TYPE(SINT64): + case UPB_TYPE(SFIXED64): + lua_pushnumber(L, upb_value_getint64(val)); break; + case UPB_TYPE(UINT32): + case UPB_TYPE(FIXED32): + lua_pushnumber(L, upb_value_getuint32(val)); break; + case UPB_TYPE(UINT64): + case UPB_TYPE(FIXED64): + lua_pushnumber(L, upb_value_getuint64(val)); break; + case UPB_TYPE(DOUBLE): + lua_pushnumber(L, upb_value_getdouble(val)); break; + case UPB_TYPE(FLOAT): + lua_pushnumber(L, upb_value_getfloat(val)); break; + case UPB_TYPE(BOOL): + lua_pushboolean(L, upb_value_getbool(val)); break; + case UPB_TYPE(STRING): + case UPB_TYPE(BYTES): { + const upb_byteregion *r = upb_value_getbyteregion(val); + size_t len; + const char *str = upb_byteregion_getptr(r, 0, &len); + lua_pushlstring(L, str, len); + } + default: luaL_error(L, "internal error"); + } } -//static void lupb_msg_getorcreate(lua_State *L, upb_msg *msg, upb_msgdef *md); -static void lupb_fielddef_getorcreate(lua_State *L, upb_fielddef *f); -static upb_msgdef *lupb_msgdef_check(lua_State *L, int narg); -static void lupb_msg_pushnew(lua_State *L, const void *md); - void lupb_checkstatus(lua_State *L, upb_status *s) { if (!upb_ok(s)) { - // Need to copy the string to the stack, so we can free it and not leak - // it (since luaL_error() does not return). - const char *str = upb_status_getstr(s); - char buf[strlen(str)+1]; - strcpy(buf, str); + lua_pushstring(L, upb_status_getstr(s)); + upb_status_uninit(s); + lua_error(L); + } else { upb_status_uninit(s); - luaL_error(L, "%s", buf); } - upb_status_uninit(s); } -/* object cache ***************************************************************/ +/* refcounted *****************************************************************/ + +// All upb objects that use upb_refcounted share a common Lua userdata +// representation and a common scheme for caching Lua wrapper object. They do +// however have different metatables. Objects are cached in a weak table +// indexed by the C pointer of the object they are caching. + +typedef union { + const upb_refcounted *refcounted; + const upb_def *def; + upb_symtab *symtab; +} lupb_refcounted; -// We cache all the lua objects (userdata) we vend in a weak table, indexed by -// the C pointer of the object they are caching. +static bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj, + const char *type, const void *owner) { + if (obj == NULL) { + lua_pushnil(L); + return false; + } -static void *lupb_cache_getorcreate_size( - lua_State *L, void *cobj, const char *type, size_t size) { // Lookup our cache in the registry (we don't put our objects in the registry // directly because we need our cache to be a weak table). - void **obj = NULL; - lua_getfield(L, LUA_REGISTRYINDEX, "upb.objcache"); + lupb_refcounted *ud = NULL; + lua_getfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE); assert(!lua_isnil(L, -1)); // Should have been created by luaopen_upb. - lua_pushlightuserdata(L, cobj); + lua_pushlightuserdata(L, (void*)obj); lua_rawget(L, -2); // Stack: objcache, cached value. - if (lua_isnil(L, -1)) { + bool create = lua_isnil(L, -1) || + // A corner case: it is possible for the value to be GC'd + // already, in which case we should evict this entry and create + // a new one. + ((lupb_refcounted*)lua_touserdata(L, -1))->refcounted == NULL; + if (create) { // Remove bad cached value and push new value. lua_pop(L, 1); + // We take advantage of the fact that all of our objects are currently a // single pointer, and thus have the same layout. - obj = lua_newuserdata(L, size); - *obj = cobj; + // TODO: this probably violates aliasing. + ud = lua_newuserdata(L, sizeof(lupb_refcounted)); + ud->refcounted = obj; + upb_refcounted_donateref(obj, owner, ud); + luaL_getmetatable(L, type); assert(!lua_isnil(L, -1)); // Should have been created by luaopen_upb. lua_setmetatable(L, -2); // Set it in the cache. - lua_pushlightuserdata(L, cobj); + lua_pushlightuserdata(L, (void*)obj); lua_pushvalue(L, -2); lua_rawset(L, -4); + } else { + // Existing wrapper obj already has a ref. + ud = lua_touserdata(L, -1); + upb_refcounted_checkref(obj, ud); + if (owner) + upb_refcounted_unref(obj, owner); } lua_insert(L, -2); lua_pop(L, 1); - return obj; -} - -// Most types are just 1 pointer and can use this helper. -static bool lupb_cache_getorcreate(lua_State *L, void *cobj, const char *type) { - return lupb_cache_getorcreate_size(L, cobj, type, sizeof(void*)) != NULL; + return create; } -static void lupb_cache_create(lua_State *L, void *cobj, const char *type) { - bool created = - lupb_cache_getorcreate_size(L, cobj, type, sizeof(void*)) != NULL; - (void)created; // For NDEBUG - assert(created); +static void lupb_refcounted_pushnewrapper(lua_State *L, upb_refcounted *obj, + const char *type, const void *owner) { + bool created = lupb_refcounted_pushwrapper(L, obj, type, owner); + UPB_ASSERT_VAR(created, created == true); } /* lupb_def *******************************************************************/ -// All the def types share the same C layout, even though they are different Lua -// types with different metatables. -typedef struct { - upb_def *def; -} lupb_def; - -static lupb_def *lupb_def_check(lua_State *L, int narg) { - void *ldef = luaL_checkudata(L, narg, "upb.msgdef"); - if (!ldef) ldef = luaL_checkudata(L, narg, "upb.enumdef"); - if (!ldef) luaL_typerror(L, narg, "upb def"); - return ldef; -} - -static void lupb_def_getorcreate(lua_State *L, const upb_def *def, int owned) { - bool created = false; - switch(def->type) { - case UPB_DEF_MSG: - created = lupb_cache_getorcreate(L, (void*)def, "upb.msgdef"); - break; - case UPB_DEF_ENUM: - created = lupb_cache_getorcreate(L, (void*)def, "upb.enumdef"); - break; - default: - luaL_error(L, "unknown deftype %d", def->type); +static const upb_def *lupb_def_check(lua_State *L, int narg) { + lupb_refcounted *r = luaL_testudata(L, narg, LUPB_MSGDEF); + if (!r) r = luaL_testudata(L, narg, LUPB_ENUMDEF); + if (!r) r = luaL_testudata(L, narg, LUPB_FIELDDEF); + if (!r) luaL_typerror(L, narg, "upb def"); + if (!r->refcounted) luaL_error(L, "called into dead def"); + return r->def; +} + +static upb_def *lupb_def_checkmutable(lua_State *L, int narg) { + const upb_def *def = lupb_def_check(L, narg); + if (upb_def_isfrozen(def)) + luaL_typerror(L, narg, "not allowed on frozen value"); + return (upb_def*)def; +} + +bool lupb_def_pushwrapper(lua_State *L, const upb_def *def, const void *owner) { + if (def == NULL) { + lua_pushnil(L); + return false; } - if (!owned && created) { - upb_def_ref(def); - } else if (owned && !created) { - upb_def_unref(def); + + const char *type = NULL; + switch (def->type) { + case UPB_DEF_MSG: type = LUPB_MSGDEF; break; + case UPB_DEF_ENUM: type = LUPB_ENUMDEF; break; + case UPB_DEF_FIELD: type = LUPB_FIELDDEF; break; + default: luaL_error(L, "unknown deftype %d", def->type); } + return lupb_refcounted_pushwrapper(L, upb_upcast(def), type, owner); } +void lupb_def_pushnewrapper(lua_State *L, const upb_def *def, + const void *owner) { + bool created = lupb_def_pushwrapper(L, def, owner); + UPB_ASSERT_VAR(created, created == true); +} -/* lupb_fielddef **************************************************************/ +static int lupb_def_type(lua_State *L) { + const upb_def *def = lupb_def_check(L, 1); + lua_pushnumber(L, upb_def_type(def)); + return 1; +} -typedef struct { - upb_fielddef *field; -} lupb_fielddef; - -static lupb_fielddef *lupb_fielddef_check(lua_State *L, int narg) { - lupb_fielddef *f = luaL_checkudata(L, narg, "upb.fielddef"); - if (!f) luaL_typerror(L, narg, "upb fielddef"); - return f; -} - -static int lupb_fielddef_index(lua_State *L) { - lupb_fielddef *f = lupb_fielddef_check(L, 1); - const char *str = luaL_checkstring(L, 2); - if (streql(str, "name")) { - lua_pushstring(L, upb_fielddef_name(f->field)); - } else if (streql(str, "number")) { - lua_pushinteger(L, upb_fielddef_number(f->field)); - } else if (streql(str, "type")) { - lua_pushinteger(L, upb_fielddef_type(f->field)); - } else if (streql(str, "label")) { - lua_pushinteger(L, upb_fielddef_label(f->field)); - } else if (streql(str, "subdef")) { - lupb_def_getorcreate(L, upb_fielddef_subdef(f->field), false); - } else if (streql(str, "msgdef")) { - lupb_def_getorcreate(L, UPB_UPCAST(upb_fielddef_msgdef(f->field)), false); - } else { - luaL_error(L, "Invalid fielddef member '%s'", str); - } +static int lupb_def_isfrozen(lua_State *L) { + const upb_def *def = lupb_def_check(L, 1); + lua_pushboolean(L, upb_def_isfrozen(def)); return 1; } -static void lupb_fielddef_set(lua_State *L, upb_fielddef *f, - const char *field, int narg) { - if (!upb_fielddef_ismutable(f)) luaL_error(L, "fielddef is not mutable."); - if (streql(field, "name")) { - const char *name = lua_tostring(L, narg); - if (!name || !upb_fielddef_setname(f, name)) - luaL_error(L, "Invalid name"); - } else if (streql(field, "number")) { - if (!upb_fielddef_setnumber(f, lupb_touint32(L, narg, "number"))) - luaL_error(L, "Invalid number"); - } else if (streql(field, "type")) { - if (!upb_fielddef_settype(f, lupb_touint8(L, narg, "type"))) - luaL_error(L, "Invalid type"); - } else if (streql(field, "label")) { - if (!upb_fielddef_setlabel(f, lupb_touint8(L, narg, "label"))) - luaL_error(L, "Invalid label"); - } else if (streql(field, "type_name")) { - const char *name = lua_tostring(L, narg); - if (!name || !upb_fielddef_settypename(f, name)) - luaL_error(L, "Invalid type_name"); - } else if (streql(field, "default_value")) { - if (!upb_fielddef_type(f)) - luaL_error(L, "Must set type before setting default_value"); - upb_strref ref; - upb_fielddef_setdefault(f, lupb_getvalue(L, narg, f, &ref)); +static int lupb_def_fullname(lua_State *L) { + const upb_def *def = lupb_def_check(L, 1); + lua_pushstring(L, upb_def_fullname(def)); + return 1; +} + +static int lupb_def_setfullname(lua_State *L) { + upb_def *def = lupb_def_checkmutable(L, 1); + const char *name = lupb_checkname(L, 2); + upb_def_setfullname(def, name); + return 0; +} + +#define LUPB_COMMON_DEF_METHODS \ + {"def_type", lupb_def_type}, \ + {"full_name", lupb_def_fullname}, \ + {"is_frozen", lupb_def_isfrozen}, \ + {"set_full_name", lupb_def_setfullname}, \ + + +/* lupb_fielddef **************************************************************/ + +static const upb_fielddef *lupb_fielddef_check(lua_State *L, int narg) { + lupb_refcounted *r = luaL_checkudata(L, narg, LUPB_FIELDDEF); + if (!r) luaL_typerror(L, narg, "upb fielddef"); + if (!r->refcounted) luaL_error(L, "called into dead fielddef"); + return upb_downcast_fielddef(r->def); +} + +static upb_fielddef *lupb_fielddef_checkmutable(lua_State *L, int narg) { + const upb_fielddef *f = lupb_fielddef_check(L, narg); + if (upb_fielddef_isfrozen(f)) + luaL_typerror(L, narg, "not allowed on frozen value"); + return (upb_fielddef*)f; +} + +// Setter functions; these are called by both the constructor and the individual +// setter API calls like field:set_type(). + +static void lupb_fielddef_dosetdefault(lua_State *L, upb_fielddef *f, + int narg) { + int type = lua_type(L, narg); + upb_fieldtype_t upbtype = upb_fielddef_type(f); + if (type == LUA_TSTRING) { + if (!upb_fielddef_isstring(f) && upbtype != UPB_TYPE(ENUM)) + luaL_argerror(L, narg, "field does not expect a string default"); + size_t len; + const char *str = lua_tolstring(L, narg, &len); + if (!upb_fielddef_setdefaultstr(f, str, len)) + luaL_argerror(L, narg, "invalid default string for enum"); } else { - luaL_error(L, "Cannot set fielddef member '%s'", field); + upb_fielddef_setdefault(f, lupb_getvalue(L, narg, upbtype)); } } +static void lupb_fielddef_dosetlabel(lua_State *L, upb_fielddef *f, int narg) { + upb_label_t label = luaL_checknumber(L, narg); + if (!upb_fielddef_setlabel(f, label)) + luaL_argerror(L, narg, "invalid field label"); +} + +static void lupb_fielddef_dosetnumber(lua_State *L, upb_fielddef *f, int narg) { + int32_t n = luaL_checknumber(L, narg); + if (!upb_fielddef_setnumber(f, n)) + luaL_argerror(L, narg, "invalid field number"); +} + +static void lupb_fielddef_dosetsubdef(lua_State *L, upb_fielddef *f, int narg) { + const upb_def *def = NULL; + if (!lua_isnil(L, narg)) + def = lupb_def_check(L, narg); + if (!upb_fielddef_setsubdef(f, def)) + luaL_argerror(L, narg, "invalid subdef for this field"); +} + +static void lupb_fielddef_dosetsubdefname(lua_State *L, upb_fielddef *f, + int narg) { + const char *name = NULL; + if (!lua_isnil(L, narg)) + name = lupb_checkname(L, narg); + if (!upb_fielddef_setsubdefname(f, name)) + luaL_argerror(L, narg, "field type does not expect a subdef"); +} + +static void lupb_fielddef_dosettype(lua_State *L, upb_fielddef *f, int narg) { + int32_t type = luaL_checknumber(L, narg); + if (!upb_fielddef_settype(f, type)) + luaL_argerror(L, narg, "invalid field type"); +} + +// Setter API calls. These use the setter functions above. + +static int lupb_fielddef_setdefault(lua_State *L) { + upb_fielddef *f = lupb_fielddef_checkmutable(L, 1); + lupb_fielddef_dosetdefault(L, f, 2); + return 0; +} + +static int lupb_fielddef_setlabel(lua_State *L) { + upb_fielddef *f = lupb_fielddef_checkmutable(L, 1); + lupb_fielddef_dosetlabel(L, f, 2); + return 0; +} + +static int lupb_fielddef_setnumber(lua_State *L) { + upb_fielddef *f = lupb_fielddef_checkmutable(L, 1); + lupb_fielddef_dosetnumber(L, f, 2); + return 0; +} + +static int lupb_fielddef_setsubdef(lua_State *L) { + upb_fielddef *f = lupb_fielddef_checkmutable(L, 1); + lupb_fielddef_dosetsubdef(L, f, 2); + return 0; +} + +static int lupb_fielddef_setsubdefname(lua_State *L) { + upb_fielddef *f = lupb_fielddef_checkmutable(L, 1); + lupb_fielddef_dosetsubdefname(L, f, 2); + return 0; +} + +static int lupb_fielddef_settype(lua_State *L) { + upb_fielddef *f = lupb_fielddef_checkmutable(L, 1); + lupb_fielddef_dosettype(L, f, 2); + return 0; +} + +// Constructor and other methods. + static int lupb_fielddef_new(lua_State *L) { - upb_fielddef *f = upb_fielddef_new(); - lupb_cache_create(L, f, "upb.fielddef"); + upb_fielddef *f = upb_fielddef_new(&f); + int narg = lua_gettop(L); + + lupb_def_pushnewrapper(L, upb_upcast(f), &f); - if (lua_gettop(L) == 0) return 1; + if (narg == 0) return 1; // User can specify initialization values like so: // upb.FieldDef{label=upb.LABEL_REQUIRED, name="my_field", number=5, // type=upb.TYPE_INT32, default_value=12, type_name="Foo"} luaL_checktype(L, 1, LUA_TTABLE); - // Iterate over table. - lua_pushnil(L); // first key - while (lua_next(L, 1)) { + for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { luaL_checktype(L, -2, LUA_TSTRING); const char *key = lua_tostring(L, -2); - lupb_fielddef_set(L, f, key, -1); - lua_pop(L, 1); + int v = -1; + if (streql(key, "name")) upb_fielddef_setname(f, lupb_checkname(L, v)); + else if (streql(key, "number")) lupb_fielddef_dosetnumber(L, f, v); + else if (streql(key, "type")) lupb_fielddef_dosettype(L, f, v); + else if (streql(key, "label")) lupb_fielddef_dosetlabel(L, f, v); + else if (streql(key, "default_value")) ; // Defer to second pass. + else if (streql(key, "subdef")) ; // Defer to second pass. + else if (streql(key, "subdef_name")) ; // Defer to second pass. + else luaL_error(L, "Cannot set fielddef member '%s'", key); } + + // Have to do these in a second pass because these depend on the type, so we + // have to make sure the type is set if the user specified one. + for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { + const char *key = lua_tostring(L, -2); + int v = -1; + if (streql(key, "default_value")) lupb_fielddef_dosetdefault(L, f, v); + else if (streql(key, "subdef")) lupb_fielddef_dosetsubdef(L, f, v); + else if (streql(key, "subdef_name")) lupb_fielddef_dosetsubdefname(L, f, v); + } + return 1; } -static void lupb_fielddef_getorcreate(lua_State *L, upb_fielddef *f) { - bool created = lupb_cache_getorcreate(L, f, "upb.fielddef"); - if (created) upb_fielddef_ref(f); +static int lupb_fielddef_default(lua_State *L) { + const upb_fielddef *f = lupb_fielddef_check(L, 1); + upb_fieldtype_t type = upb_fielddef_type(f); + if (upb_fielddef_default_is_symbolic(f)) + type = UPB_TYPE(STRING); + lupb_pushvalue(L, upb_fielddef_default(f), type); + return 1; } -static int lupb_fielddef_newindex(lua_State *L) { - lupb_fielddef *f = lupb_fielddef_check(L, 1); - lupb_fielddef_set(L, f->field, luaL_checkstring(L, 2), 3); - return 0; +static int lupb_fielddef_label(lua_State *L) { + const upb_fielddef *f = lupb_fielddef_check(L, 1); + lua_pushnumber(L, upb_fielddef_label(f)); + return 1; +} + +static int lupb_fielddef_number(lua_State *L) { + const upb_fielddef *f = lupb_fielddef_check(L, 1); + int32_t num = upb_fielddef_number(f); + if (num) + lua_pushnumber(L, num); + else + lua_pushnil(L); + return 1; +} + +static int lupb_fielddef_selectorbase(lua_State *L) { + const upb_fielddef *f = lupb_fielddef_check(L, 1); + if (!upb_fielddef_isfrozen(f)) + luaL_error(L, "_selectorbase is only defined for frozen fielddefs"); + lua_pushnumber(L, f->selector_base); + return 1; +} + +static int lupb_fielddef_hassubdef(lua_State *L) { + const upb_fielddef *f = lupb_fielddef_check(L, 1); + lua_pushboolean(L, upb_fielddef_hassubdef(f)); + return 1; +} + +static int lupb_fielddef_msgdef(lua_State *L) { + const upb_fielddef *f = lupb_fielddef_check(L, 1); + lupb_def_pushwrapper(L, upb_upcast(upb_fielddef_msgdef(f)), NULL); + return 1; +} + +static int lupb_fielddef_subdef(lua_State *L) { + const upb_fielddef *f = lupb_fielddef_check(L, 1); + if (!upb_fielddef_hassubdef(f)) + luaL_error(L, "Tried to get subdef of non-message field"); + const upb_def *def = upb_fielddef_subdef(f); + lupb_def_pushwrapper(L, def, NULL); + return 1; +} + +static int lupb_fielddef_subdefname(lua_State *L) { + upb_fielddef *f = lupb_fielddef_checkmutable(L, 1); + if (!upb_fielddef_hassubdef(f)) + luaL_error(L, "Tried to get subdef name of non-message field"); + lua_pushstring(L, upb_fielddef_subdefname(f)); + return 1; +} + +static int lupb_fielddef_type(lua_State *L) { + const upb_fielddef *f = lupb_fielddef_check(L, 1); + lua_pushnumber(L, upb_fielddef_type(f)); + return 1; } static int lupb_fielddef_gc(lua_State *L) { - lupb_fielddef *lfielddef = lupb_fielddef_check(L, 1); - upb_fielddef_unref(lfielddef->field); + lupb_refcounted *r = luaL_checkudata(L, 1, LUPB_FIELDDEF); + upb_def_unref(r->def, r); + r->refcounted = NULL; return 0; } +static const struct luaL_Reg lupb_fielddef_m[] = { + LUPB_COMMON_DEF_METHODS + + {"default", lupb_fielddef_default}, + {"has_subdef", lupb_fielddef_hassubdef}, + {"label", lupb_fielddef_label}, + {"msgdef", lupb_fielddef_msgdef}, + {"name", lupb_def_fullname}, // name() is just an alias for fullname() + {"number", lupb_fielddef_number}, + {"subdef", lupb_fielddef_subdef}, + {"subdef_name", lupb_fielddef_subdefname}, + {"type", lupb_fielddef_type}, + + {"set_default", lupb_fielddef_setdefault}, + {"set_label", lupb_fielddef_setlabel}, + {"set_name", lupb_def_setfullname}, // name() is just an alias for fullname() + {"set_number", lupb_fielddef_setnumber}, + {"set_subdef", lupb_fielddef_setsubdef}, + {"set_subdef_name", lupb_fielddef_setsubdefname}, + {"set_type", lupb_fielddef_settype}, + + // Internal-only. + {"_selector_base", lupb_fielddef_selectorbase}, + + {NULL, NULL} +}; + static const struct luaL_Reg lupb_fielddef_mm[] = { {"__gc", lupb_fielddef_gc}, - {"__index", lupb_fielddef_index}, - {"__newindex", lupb_fielddef_newindex}, {NULL, NULL} }; /* lupb_msgdef ****************************************************************/ -static upb_msgdef *lupb_msgdef_check(lua_State *L, int narg) { - lupb_def *ldef = luaL_checkudata(L, narg, "upb.msgdef"); - luaL_argcheck(L, ldef != NULL, narg, "upb msgdef expected"); - return upb_downcast_msgdef(ldef->def); +const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg) { + lupb_refcounted *r = luaL_checkudata(L, narg, LUPB_MSGDEF); + if (!r) luaL_typerror(L, narg, LUPB_MSGDEF); + if (!r->refcounted) luaL_error(L, "called into dead msgdef"); + return upb_downcast_msgdef(r->def); } -static int lupb_msgdef_gc(lua_State *L) { - lupb_def *ldef = luaL_checkudata(L, 1, "upb.msgdef"); - upb_def_unref(ldef->def); - return 0; +static upb_msgdef *lupb_msgdef_checkmutable(lua_State *L, int narg) { + const upb_msgdef *m = lupb_msgdef_check(L, narg); + if (upb_msgdef_isfrozen(m)) + luaL_typerror(L, narg, "not allowed on frozen value"); + return (upb_msgdef*)m; } -static int lupb_msgdef_call(lua_State *L) { - upb_msgdef *md = lupb_msgdef_check(L, 1); - lupb_msg_pushnew(L, md); - return 1; +static int lupb_msgdef_gc(lua_State *L) { + lupb_refcounted *r = luaL_checkudata(L, 1, LUPB_MSGDEF); + upb_def_unref(r->def, r); + r->refcounted = NULL; + return 0; } static int lupb_msgdef_new(lua_State *L) { - upb_msgdef *md = upb_msgdef_new(); - lupb_cache_create(L, md, "upb.msgdef"); + int narg = lua_gettop(L); + upb_msgdef *md = upb_msgdef_new(&md); + lupb_def_pushnewrapper(L, upb_upcast(md), &md); - if (lua_gettop(L) == 0) return 1; + if (narg == 0) return 1; // User can specify initialization values like so: - // upb.MessageDef{fqname="MyMessage", extstart=8000, fields={...}} + // upb.MessageDef{full_name="MyMessage", extstart=8000, fields={...}} luaL_checktype(L, 1, LUA_TTABLE); - // Iterate over table. - lua_pushnil(L); // first key - while (lua_next(L, 1)) { + for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { luaL_checktype(L, -2, LUA_TSTRING); const char *key = lua_tostring(L, -2); - if (streql(key, "fqname")) { // fqname="MyMessage" + if (streql(key, "full_name")) { // full_name="MyMessage" const char *fqname = lua_tostring(L, -1); - if (!fqname || !upb_def_setfqname(UPB_UPCAST(md), fqname)) - luaL_error(L, "Invalid fqname"); + if (!fqname || !upb_def_setfullname(upb_upcast(md), fqname)) + luaL_error(L, "Invalid full_name"); } else if (streql(key, "fields")) { // fields={...} // Iterate over the list of fields. - lua_pushnil(L); - luaL_checktype(L, -2, LUA_TTABLE); - while (lua_next(L, -2)) { - lupb_fielddef *f = lupb_fielddef_check(L, -1); - if (!upb_msgdef_addfield(md, f->field)) { + luaL_checktype(L, -1, LUA_TTABLE); + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + upb_fielddef *f = lupb_fielddef_checkmutable(L, -1); + if (!upb_msgdef_addfield(md, f, NULL)) { // TODO: more specific error. luaL_error(L, "Could not add field."); } - lua_pop(L, 1); } } else { // TODO: extrange= luaL_error(L, "Unknown initializer key '%s'", key); } + } + return 1; +} + +static int lupb_msgdef_add(lua_State *L) { + upb_msgdef *m = lupb_msgdef_checkmutable(L, 1); + luaL_checktype(L, 2, LUA_TTABLE); + int n = lua_rawlen(L, 2); + // TODO: add upb interface that lets us avoid this malloc/free. + upb_fielddef **fields = malloc(n * sizeof(upb_fielddef*)); + for (int i = 0; i < n; i++) { + lua_rawgeti(L, -1, i + 1); + fields[i] = lupb_fielddef_checkmutable(L, -1); lua_pop(L, 1); } + + bool success = upb_msgdef_addfields(m, fields, n, NULL); + free(fields); + if (!success) luaL_error(L, "fields could not be added"); + return 0; +} + +static int lupb_msgdef_len(lua_State *L) { + const upb_msgdef *m = lupb_msgdef_check(L, 1); + lua_pushinteger(L, upb_msgdef_numfields(m)); return 1; } -static int lupb_msgdef_fqname(lua_State *L) { - upb_msgdef *m = lupb_msgdef_check(L, 1); - lua_pushstring(L, m->base.fqname); +static int lupb_msgdef_selectorcount(lua_State *L) { + const upb_msgdef *m = lupb_msgdef_check(L, 1); + lua_pushinteger(L, m->selector_count); return 1; } -static int lupb_msgdef_fieldbyname(lua_State *L) { - upb_msgdef *m = lupb_msgdef_check(L, 1); - upb_fielddef *f = upb_msgdef_ntof(m, luaL_checkstring(L, 2)); - if (f) { - lupb_fielddef_getorcreate(L, f); +static int lupb_msgdef_field(lua_State *L) { + const upb_msgdef *m = lupb_msgdef_check(L, 1); + int type = lua_type(L, 2); + const upb_fielddef *f; + if (type == LUA_TNUMBER) { + f = upb_msgdef_itof(m, lua_tointeger(L, 2)); + } else if (type == LUA_TSTRING) { + f = upb_msgdef_ntof(m, lua_tostring(L, 2)); } else { - lua_pushnil(L); + const char *msg = lua_pushfstring(L, "number or string expected, got %s", + luaL_typename(L, 2)); + return luaL_argerror(L, 2, msg); } + + lupb_def_pushwrapper(L, upb_upcast(f), NULL); return 1; } -static int lupb_msgdef_fieldbynum(lua_State *L) { - upb_msgdef *m = lupb_msgdef_check(L, 1); - int num = luaL_checkint(L, 2); - upb_fielddef *f = upb_msgdef_itof(m, num); - if (f) { - lupb_fielddef_getorcreate(L, f); - } else { - lua_pushnil(L); - } +static int lupb_msgiter_next(lua_State *L) { + upb_msg_iter *i = lua_touserdata(L, lua_upvalueindex(1)); + if (upb_msg_done(i)) return 0; + lupb_def_pushwrapper(L, upb_upcast(upb_msg_iter_field(i)), NULL); + upb_msg_next(i); + return 1; +} + +static int lupb_msgdef_fields(lua_State *L) { + const upb_msgdef *m = lupb_msgdef_check(L, 1); + upb_msg_iter *i = lua_newuserdata(L, sizeof(upb_msg_iter)); + upb_msg_begin(i, m); + lua_pushcclosure(L, &lupb_msgiter_next, 1); return 1; } static const struct luaL_Reg lupb_msgdef_mm[] = { - {"__call", lupb_msgdef_call}, {"__gc", lupb_msgdef_gc}, + {"__len", lupb_msgdef_len}, {NULL, NULL} }; static const struct luaL_Reg lupb_msgdef_m[] = { - {"fieldbyname", lupb_msgdef_fieldbyname}, - {"fieldbynum", lupb_msgdef_fieldbynum}, - {"fqname", lupb_msgdef_fqname}, + LUPB_COMMON_DEF_METHODS + {"add", lupb_msgdef_add}, + {"field", lupb_msgdef_field}, + {"fields", lupb_msgdef_fields}, + + // Internal-only. + {"_selector_count", lupb_msgdef_selectorcount}, + {NULL, NULL} }; /* lupb_enumdef ***************************************************************/ -static upb_enumdef *lupb_enumdef_check(lua_State *L, int narg) { - lupb_def *ldef = luaL_checkudata(L, narg, "upb.enumdef"); - return upb_downcast_enumdef(ldef->def); +const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg) { + lupb_refcounted *r = luaL_checkudata(L, narg, LUPB_ENUMDEF); + if (!r) luaL_typerror(L, narg, LUPB_ENUMDEF); + if (!r->refcounted) luaL_error(L, "called into dead enumdef"); + return upb_downcast_enumdef(r->def); +} + +static upb_enumdef *lupb_enumdef_checkmutable(lua_State *L, int narg) { + const upb_enumdef *f = lupb_enumdef_check(L, narg); + if (upb_enumdef_isfrozen(f)) + luaL_typerror(L, narg, "not allowed on frozen value"); + return (upb_enumdef*)f; } static int lupb_enumdef_gc(lua_State *L) { - upb_enumdef *e = lupb_enumdef_check(L, 1); - upb_def_unref(UPB_UPCAST(e)); + lupb_refcounted *r = luaL_checkudata(L, 1, LUPB_ENUMDEF); + upb_def_unref(r->def, r); + r->refcounted = NULL; return 0; } -static int lupb_enumdef_name(lua_State *L) { - upb_enumdef *e = lupb_enumdef_check(L, 1); - lua_pushstring(L, e->base.fqname); +static int lupb_enumdef_new(lua_State *L) { + int narg = lua_gettop(L); + upb_enumdef *e = upb_enumdef_new(&e); + lupb_def_pushnewrapper(L, upb_upcast(e), &e); + + if (narg == 0) return 1; + + // User can specify initialization values like so: + // upb.EnumDef{full_name="MyEnum", + // values={ + // {"FOO_VALUE_1", 1}, + // {"FOO_VALUE_2", 2} + // } + // } + luaL_checktype(L, 1, LUA_TTABLE); + for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { + luaL_checktype(L, -2, LUA_TSTRING); + const char *key = lua_tostring(L, -2); + if (streql(key, "values")) { + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + lua_rawgeti(L, -1, 1); + luaL_checktype(L, -1, LUA_TSTRING); + const char *name = lua_tostring(L, -1); + lua_rawgeti(L, -2, 2); + int32_t num = lupb_checkint32(L, -1, "value"); + upb_status status = UPB_STATUS_INIT; + upb_enumdef_addval(e, name, num, &status); + lupb_checkstatus(L, &status); + lua_pop(L, 2); // The key/val we got from lua_rawgeti() + } + } else if (streql(key, "full_name")) { + const char *fullname = lua_tostring(L, -1); + if (!fullname || !upb_def_setfullname(upb_upcast(e), fullname)) + luaL_error(L, "Invalid full_name"); + } else { + luaL_error(L, "Unknown initializer key '%s'", key); + } + } + return 1; +} + +static int lupb_enumdef_add(lua_State *L) { + upb_enumdef *e = lupb_enumdef_checkmutable(L, 1); + const char *name = lupb_checkname(L, 2); + int32_t num = lupb_checkint32(L, 3, "value"); + upb_status status = UPB_STATUS_INIT; + upb_enumdef_addval(e, name, num, &status); + lupb_checkstatus(L, &status); + return 0; +} + +static int lupb_enumdef_len(lua_State *L) { + const upb_enumdef *e = lupb_enumdef_check(L, 1); + lua_pushinteger(L, upb_enumdef_numvals(e)); + return 1; +} + +static int lupb_enumdef_value(lua_State *L) { + const upb_enumdef *e = lupb_enumdef_check(L, 1); + int type = lua_type(L, 2); + if (type == LUA_TNUMBER) { + // Pushes "nil" for a NULL pointer. + lua_pushstring(L, upb_enumdef_iton(e, lupb_checkint32(L, 2, "value"))); + } else if (type == LUA_TSTRING) { + int32_t num; + if (upb_enumdef_ntoi(e, lua_tostring(L, 2), &num)) { + lua_pushnumber(L, num); + } else { + lua_pushnil(L); + } + } else { + const char *msg = lua_pushfstring(L, "number or string expected, got %s", + luaL_typename(L, 2)); + return luaL_argerror(L, 2, msg); + } + return 1; +} + +static int lupb_enumiter_next(lua_State *L) { + upb_enum_iter *i = lua_touserdata(L, lua_upvalueindex(1)); + if (upb_enum_done(i)) return 0; + lua_pushstring(L, upb_enum_iter_name(i)); + lua_pushnumber(L, upb_enum_iter_number(i)); + upb_enum_next(i); + return 2; +} + +static int lupb_enumdef_values(lua_State *L) { + const upb_enumdef *e = lupb_enumdef_check(L, 1); + upb_enum_iter *i = lua_newuserdata(L, sizeof(upb_enum_iter)); + upb_enum_begin(i, e); + lua_pushcclosure(L, &lupb_enumiter_next, 1); return 1; } static const struct luaL_Reg lupb_enumdef_mm[] = { {"__gc", lupb_enumdef_gc}, + {"__len", lupb_enumdef_len}, {NULL, NULL} }; static const struct luaL_Reg lupb_enumdef_m[] = { - {"name", lupb_enumdef_name}, + LUPB_COMMON_DEF_METHODS + {"add", lupb_enumdef_add}, + {"value", lupb_enumdef_value}, + {"values", lupb_enumdef_values}, {NULL, NULL} }; /* lupb_symtab ****************************************************************/ -typedef struct { - upb_symtab *symtab; -} lupb_symtab; - -static upb_accessor_vtbl *lupb_accessor(upb_fielddef *f); - // Inherits a ref on the symtab. // Checks that narg is a proper lupb_symtab object. If it is, leaves its // metatable on the stack for cache lookups/updates. -lupb_symtab *lupb_symtab_check(lua_State *L, int narg) { - return luaL_checkudata(L, narg, "upb.symtab"); +upb_symtab *lupb_symtab_check(lua_State *L, int narg) { + lupb_refcounted *r = luaL_checkudata(L, narg, LUPB_SYMTAB); + if (!r) luaL_typerror(L, narg, LUPB_SYMTAB); + if (!r->refcounted) luaL_error(L, "called into dead symtab"); + return r->symtab; } // narg is a lua table containing a list of defs to add. @@ -509,93 +867,91 @@ void lupb_symtab_doadd(lua_State *L, upb_symtab *s, int narg) { // Iterate over table twice. First iteration to count entries and // check constraints. int n = 0; - lua_pushnil(L); // first key - while (lua_next(L, narg)) { + for (lua_pushnil(L); lua_next(L, narg); lua_pop(L, 1)) { lupb_def_check(L, -1); ++n; - lua_pop(L, 1); } // Second iteration to build deflist and layout. upb_def **defs = malloc(n * sizeof(*defs)); n = 0; - lua_pushnil(L); // first key - while (lua_next(L, 1)) { - upb_def *def = lupb_def_check(L, -1)->def; + for (lua_pushnil(L); lua_next(L, narg); lua_pop(L, 1)) { + upb_def *def = lupb_def_checkmutable(L, -1); defs[n++] = def; - upb_msgdef *md = upb_dyncast_msgdef(def); - if (md) { - upb_msg_iter i; - for(i = upb_msg_begin(md); !upb_msg_done(i); i = upb_msg_next(md, i)) { - upb_fielddef *f = upb_msg_iter_field(i); - upb_fielddef_setaccessor(f, lupb_accessor(f)); - } - upb_msgdef_layout(md); - } - lua_pop(L, 1); } upb_status status = UPB_STATUS_INIT; - upb_symtab_add(s, defs, n, &status); + upb_symtab_add(s, defs, n, NULL, &status); free(defs); lupb_checkstatus(L, &status); } static int lupb_symtab_new(lua_State *L) { - upb_symtab *s = upb_symtab_new(); - lupb_cache_create(L, s, "upb.symtab"); - if (lua_gettop(L) == 0) return 1; - lupb_symtab_doadd(L, s, 1); + int narg = lua_gettop(L); + upb_symtab *s = upb_symtab_new(&s); + lupb_refcounted_pushnewrapper(L, upb_upcast(s), LUPB_SYMTAB, &s); + if (narg > 0) lupb_symtab_doadd(L, s, 1); return 1; } static int lupb_symtab_add(lua_State *L) { - lupb_symtab *s = lupb_symtab_check(L, 1); - lupb_symtab_doadd(L, s->symtab, 2); + lupb_symtab_doadd(L, lupb_symtab_check(L, 1), 2); return 0; } static int lupb_symtab_gc(lua_State *L) { - lupb_symtab *s = lupb_symtab_check(L, 1); - upb_symtab_unref(s->symtab); + lupb_refcounted *r = luaL_checkudata(L, 1, LUPB_SYMTAB); + upb_symtab_unref(r->symtab, r); + r->refcounted = NULL; return 0; } static int lupb_symtab_lookup(lua_State *L) { - lupb_symtab *s = lupb_symtab_check(L, 1); + upb_symtab *s = lupb_symtab_check(L, 1); for (int i = 2; i <= lua_gettop(L); i++) { - const upb_def *def = upb_symtab_lookup(s->symtab, luaL_checkstring(L, i)); - if (def) { - lupb_def_getorcreate(L, def, true); - } else { - lua_pushnil(L); - } + const upb_def *def = + upb_symtab_lookup(s, luaL_checkstring(L, i), &def); + lupb_def_pushwrapper(L, def, &def); lua_replace(L, i); } return lua_gettop(L) - 1; } static int lupb_symtab_getdefs(lua_State *L) { - lupb_symtab *s = lupb_symtab_check(L, 1); + upb_symtab *s = lupb_symtab_check(L, 1); upb_deftype_t type = luaL_checkint(L, 2); int count; - const upb_def **defs = upb_symtab_getdefs(s->symtab, &count, type); + const upb_def **defs = upb_symtab_getdefs(s, type, &defs, &count); // Create the table in which we will return the defs. lua_createtable(L, count, 0); for (int i = 0; i < count; i++) { const upb_def *def = defs[i]; - lupb_def_getorcreate(L, def, true); + lupb_def_pushwrapper(L, def, &defs); lua_rawseti(L, -2, i + 1); } free(defs); return 1; } +// This is a *temporary* API that will be removed once pending refactorings are +// complete (it does not belong here in core because it depends on both +// the descriptor.proto schema and the protobuf binary format. +static int lupb_symtab_load_descriptor(lua_State *L) { + size_t len; + upb_symtab *s = lupb_symtab_check(L, 1); + const char *str = luaL_checklstring(L, 2, &len); + upb_status status = UPB_STATUS_INIT; + upb_load_descriptor_into_symtab(s, str, len, &status); + lupb_checkstatus(L, &status); + return 0; +} + static const struct luaL_Reg lupb_symtab_m[] = { {"add", lupb_symtab_add}, {"getdefs", lupb_symtab_getdefs}, {"lookup", lupb_symtab_lookup}, + {"load_descriptor", lupb_symtab_load_descriptor}, {NULL, NULL} }; @@ -605,343 +961,28 @@ static const struct luaL_Reg lupb_symtab_mm[] = { }; -/* lupb_msg********************************************************************/ - -// Messages are userdata. Primitive values (numbers and bools, and their -// hasbits) are stored right in the userdata. Other values are stored using -// integer entries in the environment table and no hasbits are used (since -// "nil" in the environment table can indicate "not present"). -// -// The environment table looks like: -// {msgdef, } - -// Must pass a upb_fielddef as the pointer. -static void lupb_array_pushnew(lua_State *L, const void *f); - -static void *lupb_msg_check(lua_State *L, int narg, upb_msgdef **md) { - void *msg = luaL_checkudata(L, narg, "upb.msg"); - luaL_argcheck(L, msg != NULL, narg, "msg expected"); - // If going all the way to the environment table for the msgdef is an - // efficiency issue, we could put the pointer right in the userdata. - lua_getfenv(L, narg); - lua_rawgeti(L, -1, 1); - // Shouldn't have to check msgdef userdata validity, environment table can't - // be accessed from Lua. - lupb_def *lmd = lua_touserdata(L, -1); - *md = upb_downcast_msgdef(lmd->def); - return msg; -} - -static void lupb_msg_pushnew(lua_State *L, const void *md) { - void *msg = lua_newuserdata(L, upb_msgdef_size(md)); - luaL_getmetatable(L, "upb.msg"); - assert(!lua_isnil(L, -1)); // Should have been created by luaopen_upb. - lua_setmetatable(L, -2); - upb_msg_clear(msg, md); - lua_getfenv(L, -1); - lupb_cache_getorcreate(L, (void*)md, "upb.msgdef"); - lua_rawseti(L, -2, 1); - lua_pop(L, 1); // Pop the fenv. -} - -static int lupb_msg_new(lua_State *L) { - upb_msgdef *md = lupb_msgdef_check(L, 1); - lupb_msg_pushnew(L, md); - return 1; -} - -static int lupb_msg_index(lua_State *L) { - upb_msgdef *md; - void *m = lupb_msg_check(L, 1, &md); - upb_fielddef *f = upb_msgdef_ntof(md, luaL_checkstring(L, 2)); - if (!f) luaL_argerror(L, 2, "not a field name"); - if (upb_isprimitivetype(upb_fielddef_type(f))) { - upb_value v = upb_msg_has(m, f) ? upb_msg_get(m, f) : upb_fielddef_default(f); - lupb_pushvalue(L, v, f); - } else { - lua_getfenv(L, 1); - lua_rawgeti(L, -1, f->offset); - if (lua_isnil(L, -1)) { - // Need to lazily create array, string, or submessage. - if (upb_isseq(f)) { - lupb_array_pushnew(L, f); - } else if (upb_isstring(f)) { - // TODO: (need to figure out default string ownership). - } else if (upb_issubmsg(f)) { - lupb_msg_pushnew(L, upb_downcast_msgdef(upb_fielddef_subdef(f))); - } else { - luaL_error(L, "internal error"); - } - lua_rawseti(L, -2, f->offset); - } - } - return 1; -} - -static int lupb_msg_newindex(lua_State *L) { - upb_msgdef *md; - void *m = lupb_msg_check(L, 1, &md); - upb_fielddef *f = upb_msgdef_ntof(md, luaL_checkstring(L, 2)); - if (!f) luaL_error(L, "not a field name"); - if (upb_isprimitivetype(upb_fielddef_type(f))) { - if (lua_isnil(L, 3)) - upb_msg_clearbit(m, f); - else - upb_msg_set(m, f, lupb_getvalue(L, 3, f, NULL)); - } else { - if (!lua_isnil(L, 3)) lupb_typecheck(L, 3, f); - lua_getfenv(L, 1); - lua_pushvalue(L, 3); - lua_rawseti(L, -1, f->offset); - } - return 0; -} - -static const struct luaL_Reg lupb_msg_mm[] = { - {"__index", lupb_msg_index}, - {"__newindex", lupb_msg_newindex}, - {NULL, NULL} -}; - -// Functions that operate on msgdefs but do not live in the msgdef namespace. -static int lupb_clear(lua_State *L) { - upb_msgdef *md; - void *m = lupb_msg_check(L, 1, &md); - upb_msg_clear(m, md); - return 0; -} - -static int lupb_has(lua_State *L) { - upb_msgdef *md; - void *m = lupb_msg_check(L, 1, &md); - upb_fielddef *f = upb_msgdef_ntof(md, luaL_checkstring(L, 2)); - if (!f) luaL_argerror(L, 2, "not a field name"); - lua_pushboolean(L, upb_msg_has(m, f)); - return 1; -} - -static int lupb_msgdef(lua_State *L) { - upb_msgdef *md; - lupb_msg_check(L, 1, &md); - lupb_def_getorcreate(L, UPB_UPCAST(md), false); - return 1; -} +/* lupb toplevel **************************************************************/ -// Accessors for arrays, strings, and submessages need access to the current -// userdata's environment table, which can only be stored in Lua space. -// Options for storing it are: -// -// - put the env tables for all messages and arrays in the registry, keyed by -// userdata pointer (light userdata), or by a reference using luaL_ref(). -// Then we can just let upb's parse stack track the stack of env tables. -// Easy but requires all messages and arrays to be in the registry, which -// seems too heavyweight. -// -// - store the stack of env tables in the Lua stack. Convenient, but requires -// special code to handle resumable decoders. -// -// There is also the question of how to obtain the lua_State* pointer. -// The main options for this are: -// -// - make our closure point to a struct: -// struct { void *msg; lua_State *L; } -// But then we can't use standard accessors, which expect the closure -// to point to the data itself. Using the standard accessors for -// primitive values is both a simplicity and a performance win. -// -// - store a lua_State* pointer inside each userdata. Convenient and -// efficient, but makes every message sizeof(void*) larger. -// Currently we take this route. -// -// - use thread-local storage. Convenient and efficient, but not portable. - -typedef void createfunc_t(lua_State *L, const void *param); - -static upb_sflow_t lupb_msg_start(void *m, const upb_fielddef *f, bool array, - createfunc_t *pushnew, const void *param) { - lua_State *L = *(lua_State**)m; - int offset = array ? lua_rawlen(L, -1) : f->offset; - if (!lua_checkstack(L, 3)) luaL_error(L, "stack full"); - lua_rawgeti(L, -1, offset); - if (lua_isnil(L, -1)) { - lua_pop(L, 1); - pushnew(L, param); - lua_pushvalue(L, -1); - lua_rawseti(L, -3, offset); +static int lupb_def_freeze(lua_State *L) { + int n = lua_gettop(L); + upb_def **defs = malloc(n * sizeof(upb_def*)); + for (int i = 0; i < n; i++) { + // Could allow an array of defs here also. + defs[i] = lupb_def_checkmutable(L, i + 1); } - void *subval = lua_touserdata(L, -1); - lua_getfenv(L, -1); - lua_replace(L, -2); // Replace subval userdata with fenv. - return UPB_CONTINUE_WITH(subval); -} - -static upb_flow_t lupb_msg_string(void *m, upb_value fval, upb_value val, - bool array) { - // Could add lazy materialization of strings here. - const upb_fielddef *f = upb_value_getfielddef(fval); - lua_State *L = *(lua_State**)m; - int offset = array ? lua_rawlen(L, -1) : f->offset; - if (!lua_checkstack(L, 1)) luaL_error(L, "stack full"); - lupb_pushstring(L, upb_value_getstrref(val)); - lua_rawseti(L, -2, offset); - return UPB_CONTINUE; -} - -static upb_sflow_t lupb_msg_startseq(void *m, upb_value fval) { - const upb_fielddef *f = upb_value_getfielddef(fval); - return lupb_msg_start(m, f, false, lupb_array_pushnew, f); -} - -static upb_sflow_t lupb_msg_startsubmsg(void *m, upb_value fval) { - const upb_fielddef *f = upb_value_getfielddef(fval); - return lupb_msg_start(m, f, false, lupb_msg_pushnew, upb_fielddef_subdef(f)); -} - -static upb_sflow_t lupb_msg_startsubmsg_r(void *a, upb_value fval) { - const upb_fielddef *f = upb_value_getfielddef(fval); - return lupb_msg_start(a, f, true, lupb_msg_pushnew, upb_fielddef_subdef(f)); -} - -static upb_flow_t lupb_msg_stringval(void *m, upb_value fval, upb_value val) { - return lupb_msg_string(m, fval, val, false); -} - -static upb_flow_t lupb_msg_stringval_r(void *a, upb_value fval, upb_value val) { - return lupb_msg_string(a, fval, val, true); -} - -#define STDMSG(type, size) static upb_accessor_vtbl vtbl = { \ - &lupb_msg_startsubmsg, \ - &upb_stdmsg_set ## type, \ - &lupb_msg_startseq, \ - &lupb_msg_startsubmsg_r, \ - &upb_stdmsg_set ## type ## _r, \ - &upb_stdmsg_has, \ - &upb_stdmsg_getptr, \ - &upb_stdmsg_get ## type, \ - &upb_stdmsg_seqbegin, \ - &upb_stdmsg_ ## size ## byte_seqnext, \ - &upb_stdmsg_seqget ## type}; - -#define RETURN_STDMSG(type, size) { STDMSG(type, size); return &vtbl; } - -static upb_accessor_vtbl *lupb_accessor(upb_fielddef *f) { - switch (f->type) { - case UPB_TYPE(DOUBLE): RETURN_STDMSG(double, 8) - case UPB_TYPE(FLOAT): RETURN_STDMSG(float, 4) - case UPB_TYPE(UINT64): - case UPB_TYPE(FIXED64): RETURN_STDMSG(uint64, 8) - case UPB_TYPE(INT64): - case UPB_TYPE(SFIXED64): - case UPB_TYPE(SINT64): RETURN_STDMSG(int64, 8) - case UPB_TYPE(INT32): - case UPB_TYPE(SINT32): - case UPB_TYPE(ENUM): - case UPB_TYPE(SFIXED32): RETURN_STDMSG(int32, 4) - case UPB_TYPE(UINT32): - case UPB_TYPE(FIXED32): RETURN_STDMSG(uint32, 4) - case UPB_TYPE(BOOL): { STDMSG(bool, 1); return &vtbl; } - case UPB_TYPE(GROUP): - case UPB_TYPE(MESSAGE): RETURN_STDMSG(ptr, 8) // TODO: 32-bit - case UPB_TYPE(STRING): - case UPB_TYPE(BYTES): { - STDMSG(ptr, 8); - vtbl.set = &lupb_msg_stringval; - vtbl.append = &lupb_msg_stringval_r; - return &vtbl; - } - } - return NULL; -} - - -/* lupb_array ****************************************************************/ - -// Array: we store all elements in the environment table. Could optimize by -// storing primitive arrays in our own memory; this would be significantly more -// space efficient. Lua array elements are 16 bytes each; our own array would -// be 1/4 the space for 32-bit integers, or 1/16 the space for booleans. -// -// The first element of the environment table stores our type (which will be -// either an integer from upb.TYPE_* or a upb.msgdef), the remaining elements -// store the elements. We always keep all elements contiguous so we can use -// lua_objlen()/lua_rawlen() (for Lua 5.1/5.2 respectively) to report its len). - -// narg is offset of environment table. -static size_t lupb_array_getlen(lua_State *L, int narg) { - return lua_rawlen(L, narg) - 1; -} - -static void lupb_array_check(lua_State *L, int narg) { - if (!luaL_checkudata(L, narg, "upb.array")) - luaL_typerror(L, narg, "upb array"); -} - -static void lupb_array_pushnew(lua_State *L, const void *f) { - (void)L; - (void)f; -} - -static int lupb_array_new(lua_State *L) { - (void)L; - return 0; -} - -static int lupb_array_len(lua_State *L) { - lupb_array_check(L, 1); - lua_getfenv(L, 1); - lua_pushnumber(L, lupb_array_getlen(L, -1)); - return 1; -} - -static int lupb_array_index(lua_State *L) { - assert(lua_gettop(L) == 2); // __index should always be called with 2 args. - lupb_array_check(L, 1); - lua_Number num = luaL_checknumber(L, 2); - if (!lupb_isint(num)) luaL_typerror(L, 2, "integer"); - - lua_getfenv(L, 1); - size_t len = lupb_array_getlen(L, -1); - if (num < 1 || num > len) luaL_error(L, "array bounds check failed"); - lua_rawgeti(L, -1, num + 1); - return 1; -} - -static int lupb_array_newindex(lua_State *L) { - assert(lua_gettop(L) == 3); // __newindex should always be called with 3 args. - lupb_array_check(L, 1); - lua_Number num = luaL_checknumber(L, 2); - if (rint(num) != num) luaL_typerror(L, 2, "integer"); - - lua_getfenv(L, 1); - size_t len = lupb_array_getlen(L, -1); - // We only allow extending the index one beyond the end. - if (num < 1 || num > len + 1) luaL_error(L, "array bounds check failed"); - lua_pushvalue(L, 3); - lua_rawseti(L, -2, num); + upb_status s = UPB_STATUS_INIT; + upb_def_freeze(defs, n, &s); + free(defs); + lupb_checkstatus(L, &s); return 0; } -static const struct luaL_Reg lupb_array_mm[] = { - {"__len", lupb_array_len}, - {"__index", lupb_array_index}, - {"__newindex", lupb_array_newindex}, - {NULL, NULL} -}; - -/* lupb toplevel **************************************************************/ - static const struct luaL_Reg lupb_toplevel_m[] = { - {"SymbolTable", lupb_symtab_new}, - {"MessageDef", lupb_msgdef_new}, + {"EnumDef", lupb_enumdef_new}, {"FieldDef", lupb_fielddef_new}, - - {"Message", lupb_msg_new}, - {"Array", lupb_array_new}, - - {"clear", lupb_clear}, - {"msgdef", lupb_msgdef}, - {"has", lupb_has}, + {"MessageDef", lupb_msgdef_new}, + {"SymbolTable", lupb_symtab_new}, + {"freeze", lupb_def_freeze}, {NULL, NULL} }; @@ -950,14 +991,12 @@ static const struct luaL_Reg lupb_toplevel_m[] = { static void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m, const luaL_Reg *mm) { luaL_newmetatable(L, name); - luaL_register(L, NULL, mm); // Register all mm in the metatable. + lupb_setfuncs(L, mm); // Register all mm in the metatable. lua_createtable(L, 0, 0); - if (m) { - // Methods go in the mt's __index method. This implies that you can't - // implement __index and also set methods yourself. - luaL_register(L, NULL, m); - lua_setfield(L, -2, "__index"); - } + // Methods go in the mt's __index method. This implies that you can't + // implement __index. + lupb_setfuncs(L, m); + lua_setfield(L, -2, "__index"); lua_pop(L, 1); // The mt. } @@ -967,46 +1006,71 @@ static void lupb_setfieldi(lua_State *L, const char *field, int i) { } int luaopen_upb(lua_State *L) { - lupb_register_type(L, "upb.msgdef", lupb_msgdef_m, lupb_msgdef_mm); - lupb_register_type(L, "upb.enumdef", lupb_enumdef_m, lupb_enumdef_mm); - lupb_register_type(L, "upb.fielddef", NULL, lupb_fielddef_mm); - lupb_register_type(L, "upb.symtab", lupb_symtab_m, lupb_symtab_mm); - - lupb_register_type(L, "upb.msg", NULL, lupb_msg_mm); - lupb_register_type(L, "upb.array", NULL, lupb_msg_mm); + lupb_register_type(L, LUPB_MSGDEF, lupb_msgdef_m, lupb_msgdef_mm); + lupb_register_type(L, LUPB_ENUMDEF, lupb_enumdef_m, lupb_enumdef_mm); + lupb_register_type(L, LUPB_FIELDDEF, lupb_fielddef_m, lupb_fielddef_mm); + lupb_register_type(L, LUPB_SYMTAB, lupb_symtab_m, lupb_symtab_mm); // Create our object cache. - lua_createtable(L, 0, 0); + lua_newtable(L); lua_createtable(L, 0, 1); // Cache metatable. lua_pushstring(L, "v"); // Values are weak. lua_setfield(L, -2, "__mode"); - lua_setfield(L, LUA_REGISTRYINDEX, "upb.objcache"); + lua_setmetatable(L, -2); + lua_setfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE); + + lupb_newlib(L, "upb", lupb_toplevel_m); + + // Define a couple functions as Lua source (kept here instead of a separate + // Lua file so that upb.so is self-contained) + const char *lua_source = + "return function(upb)\n" + " upb.build_defs = function(defs)\n" + " local symtab = upb.SymbolTable(defs)\n" + " return symtab:getdefs(upb.DEF_ANY)\n" + " end\n" + "end"; - luaL_register(L, "upb", lupb_toplevel_m); + if (luaL_dostring(L, lua_source) != 0) + lua_error(L); + + // Call the chunk that will define the extra functions on upb, passing our + // package dictionary as the argument. + lua_pushvalue(L, -2); + lua_call(L, 1, 0); // Register constants. lupb_setfieldi(L, "LABEL_OPTIONAL", UPB_LABEL(OPTIONAL)); lupb_setfieldi(L, "LABEL_REQUIRED", UPB_LABEL(REQUIRED)); lupb_setfieldi(L, "LABEL_REPEATED", UPB_LABEL(REPEATED)); - lupb_setfieldi(L, "TYPE_DOUBLE", UPB_TYPE(DOUBLE)); - lupb_setfieldi(L, "TYPE_FLOAT", UPB_TYPE(FLOAT)); - lupb_setfieldi(L, "TYPE_INT64", UPB_TYPE(INT64)); - lupb_setfieldi(L, "TYPE_UINT64", UPB_TYPE(UINT64)); - lupb_setfieldi(L, "TYPE_INT32", UPB_TYPE(INT32)); - lupb_setfieldi(L, "TYPE_FIXED64", UPB_TYPE(FIXED64)); - lupb_setfieldi(L, "TYPE_FIXED32", UPB_TYPE(FIXED32)); - lupb_setfieldi(L, "TYPE_BOOL", UPB_TYPE(BOOL)); - lupb_setfieldi(L, "TYPE_STRING", UPB_TYPE(STRING)); - lupb_setfieldi(L, "TYPE_GROUP", UPB_TYPE(GROUP)); - lupb_setfieldi(L, "TYPE_MESSAGE", UPB_TYPE(MESSAGE)); - lupb_setfieldi(L, "TYPE_BYTES", UPB_TYPE(BYTES)); - lupb_setfieldi(L, "TYPE_UINT32", UPB_TYPE(UINT32)); - lupb_setfieldi(L, "TYPE_ENUM", UPB_TYPE(ENUM)); - lupb_setfieldi(L, "TYPE_SFIXED32", UPB_TYPE(SFIXED32)); - lupb_setfieldi(L, "TYPE_SFIXED64", UPB_TYPE(SFIXED64)); - lupb_setfieldi(L, "TYPE_SINT32", UPB_TYPE(SINT32)); - lupb_setfieldi(L, "TYPE_SINT64", UPB_TYPE(SINT64)); + lupb_setfieldi(L, "TYPE_DOUBLE", UPB_TYPE(DOUBLE)); + lupb_setfieldi(L, "TYPE_FLOAT", UPB_TYPE(FLOAT)); + lupb_setfieldi(L, "TYPE_INT64", UPB_TYPE(INT64)); + lupb_setfieldi(L, "TYPE_UINT64", UPB_TYPE(UINT64)); + lupb_setfieldi(L, "TYPE_INT32", UPB_TYPE(INT32)); + lupb_setfieldi(L, "TYPE_FIXED64", UPB_TYPE(FIXED64)); + lupb_setfieldi(L, "TYPE_FIXED32", UPB_TYPE(FIXED32)); + lupb_setfieldi(L, "TYPE_BOOL", UPB_TYPE(BOOL)); + lupb_setfieldi(L, "TYPE_STRING", UPB_TYPE(STRING)); + lupb_setfieldi(L, "TYPE_GROUP", UPB_TYPE(GROUP)); + lupb_setfieldi(L, "TYPE_MESSAGE", UPB_TYPE(MESSAGE)); + lupb_setfieldi(L, "TYPE_BYTES", UPB_TYPE(BYTES)); + lupb_setfieldi(L, "TYPE_UINT32", UPB_TYPE(UINT32)); + lupb_setfieldi(L, "TYPE_ENUM", UPB_TYPE(ENUM)); + lupb_setfieldi(L, "TYPE_SFIXED32", UPB_TYPE(SFIXED32)); + lupb_setfieldi(L, "TYPE_SFIXED64", UPB_TYPE(SFIXED64)); + lupb_setfieldi(L, "TYPE_SINT32", UPB_TYPE(SINT32)); + lupb_setfieldi(L, "TYPE_SINT64", UPB_TYPE(SINT64)); + + lupb_setfieldi(L, "DEF_MSG", UPB_DEF_MSG); + lupb_setfieldi(L, "DEF_FIELD", UPB_DEF_FIELD); + lupb_setfieldi(L, "DEF_ENUM", UPB_DEF_ENUM); + lupb_setfieldi(L, "DEF_SERVICE", UPB_DEF_SERVICE); + lupb_setfieldi(L, "DEF_ANY", UPB_DEF_ANY); return 1; // Return package table. } + +// Alternate names so that the library can be loaded as upb5_1 etc. +int LUPB_OPENFUNC(upb)(lua_State *L) { return luaopen_upb(L); } diff --git a/bindings/lua/upb.h b/bindings/lua/upb.h new file mode 100644 index 0000000..e6b4f2f --- /dev/null +++ b/bindings/lua/upb.h @@ -0,0 +1,45 @@ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2012 Google Inc. See LICENSE for details. + * Author: Josh Haberman + * + * Shared definitions for upb Lua modules. + */ + +#ifndef UPB_LUA_UPB_H_ +#define UPB_LUA_UPB_H_ + +#include "upb/def.h" + +// Lua 5.1/5.2 compatibility code. +#if LUA_VERSION_NUM == 501 + +#define lua_rawlen lua_objlen +#define lupb_newlib(L, name, l) luaL_register(L, name, l) +#define lupb_setfuncs(L, l) luaL_register(L, NULL, l) +#define LUPB_OPENFUNC(mod) luaopen_ ## mod ## upb5_1 + +void *luaL_testudata(lua_State *L, int ud, const char *tname); + +#elif LUA_VERSION_NUM == 502 + +// Lua 5.2 modules are not expected to set a global variable, so "name" is +// unused. +#define lupb_newlib(L, name, l) luaL_newlib(L, l) +#define lupb_setfuncs(L, l) luaL_setfuncs(L, l, 0) +int luaL_typerror(lua_State *L, int narg, const char *tname); +#define LUPB_OPENFUNC(mod) luaopen_ ## mod ## upb5_2 + +#else +#error Only Lua 5.1 and 5.2 are supported +#endif + +const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg); +const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg); +const char *lupb_checkname(lua_State *L, int narg); +bool lupb_def_pushwrapper(lua_State *L, const upb_def *def, const void *owner); +void lupb_def_pushnewrapper(lua_State *L, const upb_def *def, + const void *owner); + +#endif // UPB_LUA_UPB_H_ -- cgit v1.2.3