aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrooks Davis <brooks@FreeBSD.org>2020-03-17 16:48:52 +0000
committerBrooks Davis <brooks@FreeBSD.org>2020-03-17 16:48:52 +0000
commit4b2c3eb9d49a797f91eee2be4e41cacb3b8167e7 (patch)
treecfd66184091e02da4c65f7db8ef829133a8da4f8
Import lutok, a Lightweight C++ API for Lua.vendor/lutok/0.4-8f8eaefvendor/lutok
This a snapshot of the latest version with git hash: 8f8eaef. Obtained from: https://github.com/jmmv/lutok Sponsored by: DARPA
Notes
Notes: svn path=/vendor/lutok/dist/; revision=359039 svn path=/vendor/lutok/0.4-8f8eaef/; revision=359040; tag=vendor/lutok/0.4-8f8eaef
-rw-r--r--.gitignore21
-rw-r--r--.travis.yml25
-rw-r--r--AUTHORS1
-rw-r--r--COPYING27
-rw-r--r--Doxyfile.in53
-rw-r--r--INSTALL181
-rw-r--r--Kyuafile11
-rw-r--r--Makefile.am221
-rw-r--r--NEWS68
-rw-r--r--README27
-rw-r--r--admin/.gitignore8
-rwxr-xr-xadmin/clean-all.sh90
-rwxr-xr-xadmin/travis-build.sh51
-rwxr-xr-xadmin/travis-install-deps.sh109
-rw-r--r--c_gate.cpp76
-rw-r--r--c_gate.hpp71
-rw-r--r--c_gate_test.cpp74
-rw-r--r--configure.ac70
-rw-r--r--debug.cpp192
-rw-r--r--debug.hpp90
-rw-r--r--debug_test.cpp68
-rw-r--r--examples/Makefile67
-rw-r--r--examples/bindings.cpp133
-rw-r--r--examples/hello.cpp58
-rw-r--r--examples/interpreter.cpp83
-rw-r--r--examples/raii.cpp126
-rwxr-xr-xexamples_test.sh115
-rw-r--r--exceptions.cpp126
-rw-r--r--exceptions.hpp83
-rw-r--r--exceptions_test.cpp88
-rw-r--r--include/lutok/README4
-rw-r--r--include/lutok/c_gate.hpp1
-rw-r--r--include/lutok/debug.hpp1
-rw-r--r--include/lutok/exceptions.hpp1
-rw-r--r--include/lutok/operations.hpp1
-rw-r--r--include/lutok/stack_cleaner.hpp1
-rw-r--r--include/lutok/state.hpp1
-rw-r--r--include/lutok/state.ipp1
-rw-r--r--lutok.pc.in8
-rw-r--r--m4/.gitignore5
-rw-r--r--m4/compiler-features.m4100
-rw-r--r--m4/compiler-flags.m4159
-rw-r--r--m4/developer-mode.m4112
-rw-r--r--m4/doxygen.m462
-rw-r--r--m4/lua.m469
-rw-r--r--operations.cpp153
-rw-r--r--operations.hpp55
-rw-r--r--operations_test.cpp372
-rw-r--r--stack_cleaner.cpp91
-rw-r--r--stack_cleaner.hpp93
-rw-r--r--stack_cleaner_test.cpp108
-rw-r--r--state.cpp904
-rw-r--r--state.hpp145
-rw-r--r--state.ipp67
-rw-r--r--state_test.cpp1164
-rw-r--r--test_utils.hpp141
56 files changed, 6232 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000000..f1e0159d4427
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.la
+*.lo
+*.o
+*_test
+
+.deps
+.libs
+Doxyfile
+Makefile
+Makefile.in
+aclocal.m4
+api-docs
+autom4te.cache
+config.h
+config.h.in
+config.log
+config.status
+configure
+libtool
+lutok.pc
+stamp-h1
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000000..1e2fb77df152
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,25 @@
+language: cpp
+
+compiler:
+ - gcc
+ - clang
+
+before_install:
+ - ./admin/travis-install-deps.sh
+
+env:
+ - ARCH=amd64 AS_ROOT=no
+ - ARCH=amd64 AS_ROOT=yes
+ - ARCH=i386 AS_ROOT=no
+
+matrix:
+ exclude:
+ - compiler: clang
+ env: ARCH=i386 AS_ROOT=no
+
+script:
+ - ./admin/travis-build.sh
+
+notifications:
+ email:
+ - lutok-log@googlegroups.com
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 000000000000..0f707683aa82
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+* Julio Merino <jmmv@google.com>
diff --git a/COPYING b/COPYING
new file mode 100644
index 000000000000..be29aafc53cf
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,27 @@
+Copyright 2011, 2012 Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+* Neither the name of Google Inc. nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Doxyfile.in b/Doxyfile.in
new file mode 100644
index 000000000000..73ad7bfdbe23
--- /dev/null
+++ b/Doxyfile.in
@@ -0,0 +1,53 @@
+# Copyright 2010 Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Google Inc. nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+BUILTIN_STL_SUPPORT = YES
+ENABLE_PREPROCESSING = YES
+EXTRACT_ANON_NSPACES = YES
+EXTRACT_LOCAL_CLASSES = YES
+EXTRACT_PRIVATE = YES
+EXTRACT_STATIC = YES
+EXPAND_ONLY_PREDEF = YES
+FILE_PATTERNS = *.cpp *.hpp *.ipp
+GENERATE_LATEX = NO
+GENERATE_TAGFILE = @top_builddir@/api-docs/api-docs.tag
+INPUT = @top_srcdir@
+INPUT_ENCODING = ISO-8859-1
+JAVADOC_AUTOBRIEF = YES
+MACRO_EXPANSION = YES
+OUTPUT_DIRECTORY = @top_builddir@/api-docs
+OUTPUT_LANGUAGE = English
+PREDEFINED = "UTILS_UNUSED_PARAM(name)=unused_ ## name"
+PROJECT_NAME = "@PACKAGE_NAME@"
+PROJECT_NUMBER = @VERSION@
+QUIET = YES
+RECURSIVE = NO
+SHORT_NAMES = YES # Cope with gnutar limitations during 'make dist'.
+SORT_BY_SCOPE_NAME = YES
+SORT_MEMBERS_CTORS_1ST = YES
+WARN_NO_PARAMDOC = YES
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 000000000000..890b0e1b05af
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,181 @@
+Introduction
+============
+
+Lutok uses the GNU Automake, GNU Autoconf and GNU Libtool utilities as
+its build system. These are used only when compiling the library from
+the source code package. If you want to install Lutok from a binary
+package, you do not need to read this document.
+
+For the impatient:
+
+ $ ./configure
+ $ make
+ $ make check
+ Gain root privileges
+ # make install
+ Drop root privileges
+ $ make installcheck
+
+Or alternatively, install as a regular user into your home directory:
+
+ $ ./configure --prefix ~/local
+ $ make
+ $ make check
+ $ make install
+ $ make installcheck
+
+
+Dependencies
+============
+
+To build and use Lutok successfully you need:
+
+* A standards-compliant C++ complier.
+* Lua 5.1 or greater.
+* pkg-config.
+
+Optionally, if you want to build and run the tests (recommended), you
+need:
+
+* Kyua 0.5 or greater.
+* ATF 0.15 or greater.
+
+If you are building Lutok from the code on the repository, you will also
+need the following tools:
+
+* GNU Autoconf.
+* GNU Automake.
+* GNU Libtool.
+
+
+Regenerating the build system
+=============================
+
+This is not necessary if you are building from a formal release
+distribution file.
+
+On the other hand, if you are building Lutok from code extracted from
+the repository, you must first regenerate the files used by the build
+system. You will also need to do this if you modify configure.ac,
+Makefile.am or any of the other build system files. To do this, simply
+run:
+
+ $ autoreconf -i -s
+
+If ATF is installed in a different prefix than Autoconf, you will also
+need to tell autoreconf where the ATF M4 macros are located. Otherwise,
+the configure script will be incomplete and will show confusing syntax
+errors mentioning, for example, ATF_CHECK_SH. To fix this, you have
+to run autoreconf in the following manner, replacing '<atf-prefix>' with
+the appropriate path:
+
+ $ autoreconf -i -s -I <atf-prefix>/share/aclocal
+
+
+General build procedure
+=======================
+
+To build and install the source package, you must follow these steps:
+
+1. Configure the sources to adapt to your operating system. This is
+ done using the 'configure' script located on the sources' top
+ directory, and it is usually invoked without arguments unless you
+ want to change the installation prefix. More details on this
+ procedure are given on a later section.
+
+2. Build the sources to generate the binaries and scripts. Simply run
+ 'make' on the sources' top directory after configuring them. No
+ problems should arise.
+
+3. Install the library by running 'make install'. You may need to
+ become root to issue this step.
+
+4. Issue any manual installation steps that may be required. These are
+ described later in their own section.
+
+5. Check that the installed library works by running 'make
+ installcheck'. You do not need to be root to do this.
+
+
+Configuration flags
+===================
+
+The most common, standard flags given to 'configure' are:
+
+* --prefix=directory
+ Possible values: Any path
+ Default: /usr/local
+
+ Specifies where the library (binaries and all associated files) will
+ be installed.
+
+* --help
+ Shows information about all available flags and exits immediately,
+ without running any configuration tasks.
+
+The following flags are specific to Lutok's 'configure' script:
+
+* --enable-developer
+ Possible values: yes, no
+ Default: 'yes' in Git HEAD builds; 'no' in formal releases.
+
+ Enables several features useful for development, such as the inclusion
+ of debugging symbols in all objects or the enforcement of compilation
+ warnings.
+
+ The compiler will be executed with an exhaustive collection of warning
+ detection features regardless of the value of this flag. However, such
+ warnings are only fatal when --enable-developer is 'yes'.
+
+* --with-atf
+ Possible values: yes, no, auto.
+ Default: auto.
+
+ Enables usage of ATF to build (and later install) the tests.
+
+ Setting this to 'yes' causes the configure script to look for ATF
+ unconditionally and abort if not found. Setting this to 'auto' lets
+ configure perform the best decision based on availability of ATF.
+ Setting this to 'no' explicitly disables ATF usage.
+
+ When support for tests is enabled, the build process will generate the
+ test programs and will later install them into the tests tree.
+ Running 'make check' or 'make installcheck' from within the source
+ directory will cause these tests to be run with Kyua (assuming it is
+ also installed).
+
+* --with-doxygen
+ Possible values: yes, no, auto or a path.
+ Default: auto.
+
+ Enables usage of Doxygen to generate documentation for internal APIs.
+
+ Setting this to 'yes' causes the configure script to look for Doxygen
+ unconditionally and abort if not found. Setting this to 'auto' lets
+ configure perform the best decision based on availability of Doxygen.
+ Setting this to 'no' explicitly disables Doxygen usage. And, lastly,
+ setting this to a path forces configure to use a specific Doxygen
+ binary, which must exist.
+
+ When support for Doxygen is enabled, the build process will generate
+ HTML documentation for the Lutok API. This documentation will later
+ be installed in the HTML directory specified by the configure script.
+ You can change the location of the HTML documents by providing your
+ desired override with the '--htmldir' flag to the configure script.
+
+
+Run the tests!
+==============
+
+Lastly, after a successful installation (and assuming you built the
+sources with support for ATF), you should periodically run the tests
+from the final location to ensure things remain stable. Do so as
+follows:
+
+ $ kyua test -k /usr/local/tests/lutok/Kyuafile
+
+And if you see any tests fail, do not hesitate to report them in:
+
+ https://github.com/jmmv/lutok/issues/
+
+Thank you!
diff --git a/Kyuafile b/Kyuafile
new file mode 100644
index 000000000000..48c912dab730
--- /dev/null
+++ b/Kyuafile
@@ -0,0 +1,11 @@
+syntax("kyuafile", 1)
+
+test_suite("lutok")
+
+atf_test_program{name="c_gate_test"}
+atf_test_program{name="debug_test"}
+atf_test_program{name="examples_test"}
+atf_test_program{name="exceptions_test"}
+atf_test_program{name="operations_test"}
+atf_test_program{name="stack_cleaner_test"}
+atf_test_program{name="state_test"}
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 000000000000..ec7e87814548
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,221 @@
+# Copyright 2010 Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Google Inc. nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ACLOCAL_AMFLAGS = -I m4
+
+doc_DATA = AUTHORS COPYING NEWS README
+noinst_DATA = INSTALL README
+EXTRA_DIST = $(doc_DATA) INSTALL README
+
+LUTOK_CFLAGS = -I$(srcdir)/include $(LUA_CFLAGS)
+LUTOK_LIBS = liblutok.la $(LUA_LIBS)
+
+pkginclude_HEADERS = c_gate.hpp
+pkginclude_HEADERS += debug.hpp
+pkginclude_HEADERS += exceptions.hpp
+pkginclude_HEADERS += operations.hpp
+pkginclude_HEADERS += stack_cleaner.hpp
+pkginclude_HEADERS += state.hpp
+pkginclude_HEADERS += state.ipp
+pkginclude_HEADERS += test_utils.hpp
+
+EXTRA_DIST += include/lutok/README
+EXTRA_DIST += include/lutok/c_gate.hpp
+EXTRA_DIST += include/lutok/debug.hpp
+EXTRA_DIST += include/lutok/exceptions.hpp
+EXTRA_DIST += include/lutok/operations.hpp
+EXTRA_DIST += include/lutok/stack_cleaner.hpp
+EXTRA_DIST += include/lutok/state.hpp
+EXTRA_DIST += include/lutok/state.ipp
+
+lib_LTLIBRARIES = liblutok.la
+liblutok_la_SOURCES = c_gate.cpp
+liblutok_la_SOURCES += c_gate.hpp
+liblutok_la_SOURCES += debug.cpp
+liblutok_la_SOURCES += debug.hpp
+liblutok_la_SOURCES += exceptions.cpp
+liblutok_la_SOURCES += exceptions.hpp
+liblutok_la_SOURCES += operations.cpp
+liblutok_la_SOURCES += operations.hpp
+liblutok_la_SOURCES += stack_cleaner.cpp
+liblutok_la_SOURCES += stack_cleaner.hpp
+liblutok_la_SOURCES += state.cpp
+liblutok_la_SOURCES += state.hpp
+liblutok_la_SOURCES += state.ipp
+liblutok_la_SOURCES += test_utils.hpp
+liblutok_la_CPPFLAGS = $(LUTOK_CFLAGS)
+liblutok_la_LDFLAGS = -version-info 3:0:0
+liblutok_la_LIBADD = $(LUA_LIBS)
+
+pkgconfig_DATA = lutok.pc
+CLEANFILES = lutok.pc
+EXTRA_DIST += lutok.pc.in
+lutok.pc: $(srcdir)/lutok.pc.in Makefile
+ $(AM_V_GEN)sed -e 's#__INCLUDEDIR__#$(includedir)#g' \
+ -e 's#__LIBDIR__#$(libdir)#g' \
+ -e 's#__LUA_CFLAGS__#$(LUA_CFLAGS)#g' \
+ -e 's#__LUA_LIBS__#$(LUA_LIBS)#g' \
+ -e 's#__VERSION__#$(PACKAGE_VERSION)#g' \
+ <$(srcdir)/lutok.pc.in >lutok.pc.tmp; \
+ mv lutok.pc.tmp lutok.pc
+
+CLEAN_TARGETS =
+DIST_HOOKS =
+PHONY_TARGETS =
+
+examplesdir = $(docdir)/examples
+examples_DATA = examples/Makefile
+examples_DATA += examples/bindings.cpp
+examples_DATA += examples/hello.cpp
+examples_DATA += examples/interpreter.cpp
+examples_DATA += examples/raii.cpp
+EXTRA_DIST += $(examples_DATA)
+
+if WITH_ATF
+tests_DATA = Kyuafile
+EXTRA_DIST += $(tests_DATA)
+
+tests_PROGRAMS = c_gate_test
+c_gate_test_SOURCES = c_gate_test.cpp test_utils.hpp
+c_gate_test_CXXFLAGS = $(LUTOK_CFLAGS) $(ATF_CXX_CFLAGS)
+c_gate_test_LDADD = $(LUTOK_LIBS) $(ATF_CXX_LIBS)
+
+tests_PROGRAMS += debug_test
+debug_test_SOURCES = debug_test.cpp test_utils.hpp
+debug_test_CXXFLAGS = $(LUTOK_CFLAGS) $(ATF_CXX_CFLAGS)
+debug_test_LDADD = $(LUTOK_LIBS) $(ATF_CXX_LIBS)
+
+tests_SCRIPTS = examples_test
+CLEANFILES += examples_test
+EXTRA_DIST += examples_test.sh
+examples_test: $(srcdir)/examples_test.sh
+ $(AM_V_GEN)sed -e 's,__ATF_SH__,$(ATF_SH),g' \
+ -e 's,__CXX__,$(CXX),g' \
+ -e 's,__EXAMPLESDIR__,$(examplesdir),g' \
+ -e 's,__LIBDIR__,$(libdir),g' \
+ <$(srcdir)/examples_test.sh >examples_test.tmp; \
+ chmod +x examples_test.tmp; \
+ rm -f examples_test; \
+ mv examples_test.tmp examples_test
+
+tests_PROGRAMS += exceptions_test
+exceptions_test_SOURCES = exceptions_test.cpp
+exceptions_test_CXXFLAGS = $(LUTOK_CFLAGS) $(ATF_CXX_CFLAGS)
+exceptions_test_LDADD = $(LUTOK_LIBS) $(ATF_CXX_LIBS)
+
+tests_PROGRAMS += operations_test
+operations_test_SOURCES = operations_test.cpp test_utils.hpp
+operations_test_CXXFLAGS = $(LUTOK_CFLAGS) $(ATF_CXX_CFLAGS)
+operations_test_LDADD = $(LUTOK_LIBS) $(ATF_CXX_LIBS)
+
+tests_PROGRAMS += stack_cleaner_test
+stack_cleaner_test_SOURCES = stack_cleaner_test.cpp test_utils.hpp
+stack_cleaner_test_CXXFLAGS = $(LUTOK_CFLAGS) $(ATF_CXX_CFLAGS)
+stack_cleaner_test_LDADD = $(LUTOK_LIBS) $(ATF_CXX_LIBS)
+
+tests_PROGRAMS += state_test
+state_test_SOURCES = state_test.cpp test_utils.hpp
+state_test_CXXFLAGS = $(LUTOK_CFLAGS) $(ATF_CXX_CFLAGS)
+state_test_LDADD = $(LUTOK_LIBS) $(ATF_CXX_LIBS)
+
+if HAVE_KYUA
+check-local: check-kyua
+PHONY_TARGETS += check-kyua
+check-kyua:
+ $(TESTS_ENVIRONMENT) kyua test \
+ --kyuafile='$(top_srcdir)/Kyuafile' --build-root='$(top_builddir)'
+
+installcheck-local: installcheck-kyua
+PHONY_TARGETS += installcheck-kyua
+installcheck-kyua:
+ cd $(testsdir) && $(TESTS_ENVIRONMENT) kyua test
+endif
+else
+DIST_HOOKS += dist-no-atf
+PHONY_TARGETS += dist-no-atf
+dist-no-atf:
+ @echo "Sorry; cannot build a distfile without atf"
+ @false
+endif
+
+if WITH_DOXYGEN
+# This should probably be html-local, but it seems better to generate the
+# documentation in all cases to get warnings about missing documentation every
+# time the code is modified. (And, after all, the documentation is not
+# installed so generating it unconditionally is not a big problem.)
+all-local: api-docs/api-docs.tag
+
+api-docs/api-docs.tag: $(builddir)/Doxyfile $(SOURCES)
+ $(AM_V_GEN)rm -rf api-docs; \
+ mkdir -p api-docs; \
+ ${DOXYGEN} $(builddir)/Doxyfile 2>&1 | tee api-docs/warnings; \
+ rm -f api-docs/html/installdox
+api-docs/html: api-docs/api-docs.tag
+
+CLEAN_TARGETS += clean-api-docs
+clean-api-docs:
+ rm -rf api-docs
+
+EXTRA_DIST += api-docs/html
+else
+DIST_HOOKS += dist-no-doxygen
+PHONY_TARGETS += dist-no-doxygen
+dist-no-doxygen:
+ @echo "Sorry; cannot build a distfile without Doxygen"
+ @false
+endif
+
+install-data-local: install-api-docs
+install-api-docs: install-docDATA
+ @echo "Installing HTML documentation into $(DESTDIR)$(htmldir)"
+ @if [ -d api-docs/html ]; then \
+ test -z "$(htmldir)" || $(MKDIR_P) "$(DESTDIR)$(htmldir)"; \
+ ( cd api-docs/html && tar cf - . ) | \
+ ( cd "$(DESTDIR)$(htmldir)" && tar xf - ); \
+ elif [ -d "$(srcdir)/api-docs/html" ]; then \
+ test -z "$(htmldir)" || $(MKDIR_P) "$(DESTDIR)$(htmldir)"; \
+ ( cd "$(srcdir)/api-docs/html" && tar cf - . ) | \
+ ( cd "$(DESTDIR)$(htmldir)" && tar xf - ); \
+ else \
+ echo "Doxygen not installed and prebuilt documents not found"; \
+ fi
+
+uninstall-local: uninstall-api-docs
+uninstall-api-docs:
+ find "$(DESTDIR)$(htmldir)" -type d -exec chmod 755 {} \;
+ rm -rf "$(DESTDIR)$(htmldir)"
+
+clean-local: $(CLEAN_TARGETS)
+
+PHONY_TARGETS += clean-all
+clean-all:
+ GIT="$(GIT)" $(SH) $(srcdir)/admin/clean-all.sh
+
+dist-hook: $(DIST_HOOKS)
+
+.PHONY: ${PHONY_TARGETS}
diff --git a/NEWS b/NEWS
new file mode 100644
index 000000000000..3cb25cd53458
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,68 @@
+Changes in version 0.4
+======================
+
+Released on 2013/12/07.
+
+* Cope with the lack of AM_PROG_AR in configure.ac, which first
+ appeared in Automake 1.11.2. Fixes a problem in Ubuntu 10.04
+ LTS, which appears stuck in 1.11.1.
+
+* Stopped shipping an Atffile. The only supported way to run the tests
+ is via Kyua.
+
+Interface changes:
+
+* Issue 5: New methods added to the state class: open_all.
+
+* Removed default parameter values from all state methods and all
+ standalone operations. It is often unclear what the default value is
+ given that it depends on the specific Lua operation. Being explicit
+ on the caller side is clearer.
+
+* Modified operations do_file and do_string to support passing a number
+ of arguments to the loaded chunks and an error handler to the backing
+ pcall call.
+
+
+Changes in version 0.3
+======================
+
+Released on 2013/06/14.
+
+* Issue 1: Added support for Lua 5.2 while maintaining support for Lua
+ 5.1. Applications using Lutok can be modified to use the new
+ interface in this new version and thus support both Lua releases.
+ However, because of incompatible changes to the Lua API, this release
+ of Lutok is incompatible with previous releases as well.
+
+* Issue 3: Tweaked configure to look for Lua using the pkg-config names
+ lua-5.2 and lua-5.1. These are the names used by FreeBSD.
+
+Interface changes:
+
+* New global constants: registry_index.
+
+* New methods added to the state class: get_global_table.
+
+* Removed global constants: globals_index.
+
+
+Changes in version 0.2
+======================
+
+Released on 2012/05/30.
+
+* New global constants: globals_index.
+
+* New methods added to the state class: get_metafield, get_metatable,
+ insert, push_value, raw_get and raw_set.
+
+* Acknowledged that Lua 5.2 is currently not supported.
+
+
+Changes in version 0.1
+======================
+
+Released on 2012/01/29.
+
+* This is the first public release of the Lutok package.
diff --git a/README b/README
new file mode 100644
index 000000000000..f39d33d77bb4
--- /dev/null
+++ b/README
@@ -0,0 +1,27 @@
+Lutok is a lightweight C++ API library for Lua.
+
+Lutok provides thin C++ wrappers around the Lua C API to ease the
+interaction between C++ and Lua. These wrappers make intensive use of
+RAII to prevent resource leakage, expose C++-friendly data types, report
+errors by means of exceptions and ensure that the Lua stack is always
+left untouched in the face of errors. The library also provides a small
+subset of miscellaneous utility functions built on top of the wrappers.
+
+Lutok focuses on providing a clean and safe C++ interface; the drawback
+is that it is not suitable for performance-critical environments. In
+order to implement error-safe C++ wrappers on top of a Lua C binary
+library, Lutok adds several layers or abstraction and error checking
+that go against the original spirit of the Lua C API and thus degrade
+performance.
+
+For further information on the contents of this distribution file,
+please refer to the following other documents:
+
+* AUTHORS: List of authors and contributors to this project.
+* COPYING: License information.
+* INSTALL: Compilation and installation instructions.
+* NEWS: List of major changes between formal releases.
+
+For general project information, please visit:
+
+ https://github.com/jmmv/lutok/
diff --git a/admin/.gitignore b/admin/.gitignore
new file mode 100644
index 000000000000..64f348c68ebf
--- /dev/null
+++ b/admin/.gitignore
@@ -0,0 +1,8 @@
+ar-lib
+compile
+config.guess
+config.sub
+depcomp
+install-sh
+ltmain.sh
+missing
diff --git a/admin/clean-all.sh b/admin/clean-all.sh
new file mode 100755
index 000000000000..27244437ac82
--- /dev/null
+++ b/admin/clean-all.sh
@@ -0,0 +1,90 @@
+#! /bin/sh
+# Copyright 2010 Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Google Inc. nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Prog_Name=${0##*/}
+
+if [ ! -f ./state.hpp ]; then
+ echo "${Prog_Name}: must be run from the source top directory" 1>&2
+ exit 1
+fi
+
+if [ ! -f configure ]; then
+ echo "${Prog_Name}: configure not found; nothing to clean?" 1>&2
+ exit 1
+fi
+
+[ -f Makefile ] || ./configure
+make distclean
+
+# Top-level directory.
+rm -f Makefile.in
+rm -f aclocal.m4
+rm -rf autom4te.cache
+rm -f config.h.in
+rm -f configure
+rm -f mkinstalldirs
+rm -f lutok-*.tar.gz
+
+# admin directory.
+rm -f admin/compile
+rm -f admin/config.guess
+rm -f admin/config.sub
+rm -f admin/depcomp
+rm -f admin/install-sh
+rm -f admin/ltmain.sh
+rm -f admin/mdate-sh
+rm -f admin/missing
+rm -f admin/texinfo.tex
+
+# bootstrap directory.
+rm -f bootstrap/package.m4
+rm -f bootstrap/testsuite
+
+# doc directory.
+rm -f doc/*.info
+rm -f doc/stamp-vti
+rm -f doc/version.texi
+
+# m4 directory.
+rm -f m4/libtool.m4
+rm -f m4/lt*.m4
+
+# Files and directories spread all around the tree.
+find . -name '#*' | xargs rm -rf
+find . -name '*~' | xargs rm -rf
+find . -name .deps | xargs rm -rf
+find . -name .gdb_history | xargs rm -rf
+find . -name .libs | xargs rm -rf
+find . -name .tmp | xargs rm -rf
+
+# Show remaining files.
+if [ -n "${GIT}" ]; then
+ echo ">>> untracked and ignored files"
+ "${GIT}" status --porcelain --ignored | grep -E '^(\?\?|!!)' || true
+fi
diff --git a/admin/travis-build.sh b/admin/travis-build.sh
new file mode 100755
index 000000000000..1da582fb3308
--- /dev/null
+++ b/admin/travis-build.sh
@@ -0,0 +1,51 @@
+#! /bin/sh
+# Copyright 2014 Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Google Inc. nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -e -x
+
+if [ -d /usr/local/share/aclocal ]; then
+ autoreconf -isv -I/usr/local/share/aclocal
+else
+ autoreconf -isv
+fi
+./configure
+
+archflags=
+[ "${ARCH?}" != i386 ] || archflags=-m32
+
+f=
+f="${f} CPPFLAGS='-I/usr/local/include'"
+f="${f} CXX='${CXX} ${archflags}'"
+f="${f} LDFLAGS='-L/usr/local/lib -Wl,-R/usr/local/lib'"
+f="${f} PKG_CONFIG_PATH='/usr/local/lib/pkgconfig'"
+if [ "${AS_ROOT:-no}" = yes ]; then
+ sudo -H PATH="${PATH}" make distcheck DISTCHECK_CONFIGURE_FLAGS="${f}"
+else
+ make distcheck DISTCHECK_CONFIGURE_FLAGS="${f}"
+fi
diff --git a/admin/travis-install-deps.sh b/admin/travis-install-deps.sh
new file mode 100755
index 000000000000..a6d4cc53f8ce
--- /dev/null
+++ b/admin/travis-install-deps.sh
@@ -0,0 +1,109 @@
+#! /bin/sh
+# Copyright 2014 Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Google Inc. nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -e -x
+
+install_deps() {
+ sudo apt-get update -qq
+
+ local pkgsuffix=
+ local packages=
+ if [ "${ARCH?}" = i386 ]; then
+ pkgsuffix=:i386
+ packages="${packages} gcc-multilib"
+ packages="${packages} g++-multilib"
+ fi
+ packages="${packages} doxygen"
+ packages="${packages} gdb"
+ packages="${packages} liblua5.2-0${pkgsuffix}"
+ packages="${packages} liblua5.2-dev${pkgsuffix}"
+ packages="${packages} libsqlite3-0${pkgsuffix}"
+ packages="${packages} libsqlite3-dev${pkgsuffix}"
+ packages="${packages} pkg-config${pkgsuffix}"
+ packages="${packages} sqlite3"
+ sudo apt-get install -y ${packages}
+}
+
+install_from_github() {
+ local project="${1}"; shift
+ local name="${1}"; shift
+ local release="${1}"; shift
+
+ local distname="${name}-${release}"
+
+ local baseurl="https://github.com/jmmv/${project}"
+ wget --no-check-certificate \
+ "${baseurl}/releases/download/${distname}/${distname}.tar.gz"
+ tar -xzvf "${distname}.tar.gz"
+
+ local archflags=
+ [ "${ARCH?}" != i386 ] || archflags=-m32
+
+ cd "${distname}"
+ ./configure \
+ --disable-developer \
+ --without-atf \
+ --without-doxygen \
+ CFLAGS="${archflags}" \
+ CPPFLAGS="-I/usr/local/include" \
+ CXXFLAGS="${archflags}" \
+ LDFLAGS="-L/usr/local/lib -Wl,-R/usr/local/lib" \
+ PKG_CONFIG_PATH="/usr/local/lib/pkgconfig"
+ make
+ sudo make install
+ cd -
+
+ rm -rf "${distname}" "${distname}.tar.gz"
+}
+
+install_from_bintray() {
+ case "${ARCH?}" in
+ amd64)
+ name="20160204-usr-local-kyua-ubuntu-12-04-amd64-${CC?}.tar.gz"
+ ;;
+ i386)
+ name="20160714-usr-local-kyua-ubuntu-12-04-i386-${CC?}.tar.gz"
+ ;;
+ *)
+ echo "ERROR: Unknown ARCH value ${ARCH}" 1>&2
+ exit 1
+ ;;
+ esac
+ wget "http://dl.bintray.com/jmmv/kyua/${name}" || return 1
+ sudo tar -xzvp -C / -f "${name}"
+ rm -f "${name}"
+}
+
+install_deps
+if ! install_from_bintray; then
+ install_from_github atf atf 0.20
+ install_from_github lutok lutok 0.4
+ install_from_github kyua kyua-testers 0.2
+ install_from_github kyua kyua-cli 0.8
+fi
diff --git a/c_gate.cpp b/c_gate.cpp
new file mode 100644
index 000000000000..dde366e2dee2
--- /dev/null
+++ b/c_gate.cpp
@@ -0,0 +1,76 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "c_gate.hpp"
+#include "state.ipp"
+
+
+/// Creates a new gateway to an existing C++ Lua state.
+///
+/// \param state_ The state to connect to. This object must remain alive while
+/// the newly-constructed state_c_gate is alive.
+lutok::state_c_gate::state_c_gate(state& state_) :
+ _state(state_)
+{
+}
+
+
+/// Destructor.
+///
+/// Destroying this object has no implications on the life cycle of the Lua
+/// state. Only the corresponding state object controls when the Lua state is
+/// closed.
+lutok::state_c_gate::~state_c_gate(void)
+{
+}
+
+
+/// Creates a C++ state for a C Lua state.
+///
+/// \warning The created state object does NOT own the C state. You must take
+/// care to properly destroy the input lua_State when you are done with it to
+/// not leak resources.
+///
+/// \param raw_state The raw state to wrap temporarily.
+///
+/// \return The wrapped state without strong ownership on the input state.
+lutok::state
+lutok::state_c_gate::connect(lua_State* raw_state)
+{
+ return state(static_cast< void* >(raw_state));
+}
+
+
+/// Returns the C native Lua state.
+///
+/// \return A native lua_State object holding the Lua C API state.
+lua_State*
+lutok::state_c_gate::c_state(void)
+{
+ return static_cast< lua_State* >(_state.raw_state());
+}
diff --git a/c_gate.hpp b/c_gate.hpp
new file mode 100644
index 000000000000..36bc9c228f84
--- /dev/null
+++ b/c_gate.hpp
@@ -0,0 +1,71 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file c_gate.hpp
+/// Provides direct access to the C state of the Lua wrappers.
+
+#if !defined(LUTOK_C_GATE_HPP)
+#define LUTOK_C_GATE_HPP
+
+#include <lua.hpp>
+
+namespace lutok {
+
+
+class state;
+
+
+/// Gateway to the raw C state of Lua.
+///
+/// This class provides a mechanism to muck with the internals of the state
+/// wrapper class. Client code may wish to do so if Lutok is missing some
+/// features of the performance of Lutok in a particular situation is not
+/// reasonable.
+///
+/// \warning The use of this class is discouraged. By using this class, you are
+/// entering the world of unsafety. Anything you do through the objects exposed
+/// through this class will not be controlled by RAII patterns not validated in
+/// any other way, so you can end up corrupting the Lua state and later get
+/// crashes on otherwise perfectly-valid C++ code.
+class state_c_gate {
+ /// The C++ state that this class wraps.
+ state& _state;
+
+public:
+ state_c_gate(state&);
+ ~state_c_gate(void);
+
+ static state connect(lua_State*);
+
+ lua_State* c_state(void);
+};
+
+
+} // namespace lutok
+
+#endif // !defined(LUTOK_C_GATE_HPP)
diff --git a/c_gate_test.cpp b/c_gate_test.cpp
new file mode 100644
index 000000000000..33e3d10da457
--- /dev/null
+++ b/c_gate_test.cpp
@@ -0,0 +1,74 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "c_gate.hpp"
+
+#include <atf-c++.hpp>
+#include <lua.hpp>
+
+#include "state.ipp"
+#include "test_utils.hpp"
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(connect);
+ATF_TEST_CASE_BODY(connect)
+{
+ lua_State* raw_state = luaL_newstate();
+ ATF_REQUIRE(raw_state != NULL);
+
+ {
+ lutok::state state = lutok::state_c_gate::connect(raw_state);
+ lua_pushinteger(raw(state), 123);
+ }
+ // If the wrapper object had closed the Lua state, we could very well crash
+ // here.
+ ATF_REQUIRE_EQ(123, lua_tointeger(raw_state, -1));
+
+ lua_close(raw_state);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(c_state);
+ATF_TEST_CASE_BODY(c_state)
+{
+ lutok::state state;
+ state.push_integer(5);
+ {
+ lutok::state_c_gate gate(state);
+ lua_State* raw_state = gate.c_state();
+ ATF_REQUIRE_EQ(5, lua_tointeger(raw_state, -1));
+ }
+ state.pop(1);
+}
+
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, c_state);
+ ATF_ADD_TEST_CASE(tcs, connect);
+}
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 000000000000..66c7c5d6b613
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,70 @@
+dnl Copyright 2011 Google Inc.
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions are
+dnl met:
+dnl
+dnl * Redistributions of source code must retain the above copyright
+dnl notice, this list of conditions and the following disclaimer.
+dnl * Redistributions in binary form must reproduce the above copyright
+dnl notice, this list of conditions and the following disclaimer in the
+dnl documentation and/or other materials provided with the distribution.
+dnl * Neither the name of Google Inc. nor the names of its contributors
+dnl may be used to endorse or promote products derived from this software
+dnl without specific prior written permission.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+AC_INIT([Lutok], [0.4],
+ [lutok-discuss@googlegroups.com], [lutok],
+ [https://github.com/jmmv/lutok/])
+AC_PREREQ([2.65])
+
+
+AC_COPYRIGHT([Copyright 2011 Google Inc.])
+AC_CONFIG_AUX_DIR([admin])
+AC_CONFIG_FILES([Doxyfile Makefile])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_SRCDIR([state.hpp])
+
+
+AM_INIT_AUTOMAKE([1.9 check-news foreign subdir-objects -Wall])
+m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
+LT_INIT
+
+
+AC_LANG([C++])
+AC_PROG_CXX
+KYUA_REQUIRE_CXX
+KYUA_DEVELOPER_MODE([C++])
+
+
+ATF_CHECK_CXX([>= 0.15])
+ATF_CHECK_SH([>= 0.15])
+ATF_ARG_WITH
+KYUA_DOXYGEN
+KYUA_LUA
+
+
+AC_PATH_PROG([KYUA], [kyua])
+AM_CONDITIONAL([HAVE_KYUA], [test -n "${KYUA}"])
+AC_PATH_PROG([GIT], [git])
+
+
+AC_SUBST(pkgconfigdir, \${libdir}/pkgconfig)
+AC_SUBST(testsdir, \${exec_prefix}/tests/lutok)
+
+
+AC_OUTPUT
diff --git a/debug.cpp b/debug.cpp
new file mode 100644
index 000000000000..e0a0b5eb2113
--- /dev/null
+++ b/debug.cpp
@@ -0,0 +1,192 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <cassert>
+
+#include <lua.hpp>
+
+#include <lutok/c_gate.hpp>
+#include <lutok/debug.hpp>
+#include <lutok/exceptions.hpp>
+#include <lutok/state.ipp>
+
+
+/// Internal implementation for lutok::debug.
+struct lutok::debug::impl {
+ /// The Lua internal debug state.
+ lua_Debug lua_debug;
+};
+
+
+/// Constructor for an empty debug structure.
+lutok::debug::debug(void) :
+ _pimpl(new impl())
+{
+}
+
+
+/// Destructor.
+lutok::debug::~debug(void)
+{
+}
+
+
+/// Wrapper around lua_getinfo.
+///
+/// \param s The Lua state.
+/// \param what_ The second parameter to lua_getinfo.
+///
+/// \warning Terminates execution if there is not enough memory to manipulate
+/// the Lua stack.
+void
+lutok::debug::get_info(state& s, const std::string& what_)
+{
+ lua_State* raw_state = state_c_gate(s).c_state();
+
+ if (lua_getinfo(raw_state, what_.c_str(), &_pimpl->lua_debug) == 0)
+ throw lutok::api_error::from_stack(s, "lua_getinfo");
+}
+
+
+/// Wrapper around lua_getstack.
+///
+/// \param s The Lua state.
+/// \param level The second parameter to lua_getstack.
+void
+lutok::debug::get_stack(state& s, const int level)
+{
+ lua_State* raw_state = state_c_gate(s).c_state();
+
+ lua_getstack(raw_state, level, &_pimpl->lua_debug);
+}
+
+
+/// Accessor for the 'event' field of lua_Debug.
+///
+/// \return Returns the 'event' field of the internal lua_Debug structure.
+int
+lutok::debug::event(void) const
+{
+ return _pimpl->lua_debug.event;
+}
+
+
+/// Accessor for the 'name' field of lua_Debug.
+///
+/// \return Returns the 'name' field of the internal lua_Debug structure.
+std::string
+lutok::debug::name(void) const
+{
+ assert(_pimpl->lua_debug.name != NULL);
+ return _pimpl->lua_debug.name;
+}
+
+
+/// Accessor for the 'namewhat' field of lua_Debug.
+///
+/// \return Returns the 'namewhat' field of the internal lua_Debug structure.
+std::string
+lutok::debug::name_what(void) const
+{
+ assert(_pimpl->lua_debug.namewhat != NULL);
+ return _pimpl->lua_debug.namewhat;
+}
+
+
+/// Accessor for the 'what' field of lua_Debug.
+///
+/// \return Returns the 'what' field of the internal lua_Debug structure.
+std::string
+lutok::debug::what(void) const
+{
+ assert(_pimpl->lua_debug.what != NULL);
+ return _pimpl->lua_debug.what;
+}
+
+
+/// Accessor for the 'source' field of lua_Debug.
+///
+/// \return Returns the 'source' field of the internal lua_Debug structure.
+std::string
+lutok::debug::source(void) const
+{
+ assert(_pimpl->lua_debug.source != NULL);
+ return _pimpl->lua_debug.source;
+}
+
+
+/// Accessor for the 'currentline' field of lua_Debug.
+///
+/// \return Returns the 'currentline' field of the internal lua_Debug structure.
+int
+lutok::debug::current_line(void) const
+{
+ return _pimpl->lua_debug.currentline;
+}
+
+
+/// Accessor for the 'nups' field of lua_Debug.
+///
+/// \return Returns the 'nups' field of the internal lua_Debug structure.
+int
+lutok::debug::n_ups(void) const
+{
+ return _pimpl->lua_debug.nups;
+}
+
+
+/// Accessor for the 'linedefined' field of lua_Debug.
+///
+/// \return Returns the 'linedefined' field of the internal lua_Debug structure.
+int
+lutok::debug::line_defined(void) const
+{
+ return _pimpl->lua_debug.linedefined;
+}
+
+
+/// Accessor for the 'lastlinedefined' field of lua_Debug.
+///
+/// \return Returns the 'lastlinedefined' field of the internal lua_Debug
+/// structure.
+int
+lutok::debug::last_line_defined(void) const
+{
+ return _pimpl->lua_debug.lastlinedefined;
+}
+
+
+/// Accessor for the 'short_src' field of lua_Debug.
+///
+/// \return Returns the 'short_src' field of the internal lua_Debug structure.
+std::string
+lutok::debug::short_src(void) const
+{
+ assert(_pimpl->lua_debug.short_src != NULL);
+ return _pimpl->lua_debug.short_src;
+}
diff --git a/debug.hpp b/debug.hpp
new file mode 100644
index 000000000000..6fc074d95d41
--- /dev/null
+++ b/debug.hpp
@@ -0,0 +1,90 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file debug.hpp
+/// Provides the debug wrapper class for the Lua C debug state.
+
+#if !defined(LUTOK_DEBUG_HPP)
+#define LUTOK_DEBUG_HPP
+
+#include <string>
+#if defined(_LIBCPP_VERSION) || __cplusplus >= 201103L
+#include <memory>
+#else
+#include <tr1/memory>
+#endif
+
+namespace lutok {
+
+
+class state;
+
+
+/// A model for the Lua debug state.
+///
+/// This extremely-simple class provides a mechanism to hide the internals of
+/// the C native lua_Debug type, exposing its internal fields using friendlier
+/// C++ types.
+///
+/// This class also acts as a complement to the state class by exposing any
+/// state-related functions as methods of this function. For example, while it
+/// might seem that get_info() belongs in state, we expose it from here because
+/// its result is really mutating a debug object, not the state object.
+class debug {
+ struct impl;
+
+ /// Pointer to the shared internal implementation.
+#if defined(_LIBCPP_VERSION) || __cplusplus >= 201103L
+ std::shared_ptr< impl > _pimpl;
+#else
+ std::tr1::shared_ptr< impl > _pimpl;
+#endif
+
+public:
+ debug(void);
+ ~debug(void);
+
+ void get_info(state&, const std::string&);
+ void get_stack(state&, const int);
+
+ int event(void) const;
+ std::string name(void) const;
+ std::string name_what(void) const;
+ std::string what(void) const;
+ std::string source(void) const;
+ int current_line(void) const;
+ int n_ups(void) const;
+ int line_defined(void) const;
+ int last_line_defined(void) const;
+ std::string short_src(void) const;
+};
+
+
+} // namespace lutok
+
+#endif // !defined(LUTOK_DEBUG_HPP)
diff --git a/debug_test.cpp b/debug_test.cpp
new file mode 100644
index 000000000000..a88b892d0fa3
--- /dev/null
+++ b/debug_test.cpp
@@ -0,0 +1,68 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "debug.hpp"
+
+#include <atf-c++.hpp>
+#include <lua.hpp>
+
+#include "state.ipp"
+#include "test_utils.hpp"
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_info);
+ATF_TEST_CASE_BODY(get_info)
+{
+ lutok::state state;
+ ATF_REQUIRE(luaL_dostring(raw(state), "\n\nfunction hello() end\n"
+ "return hello") == 0);
+ lutok::debug debug;
+ debug.get_info(state, ">S");
+ ATF_REQUIRE_EQ(3, debug.line_defined());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_stack);
+ATF_TEST_CASE_BODY(get_stack)
+{
+ lutok::state state;
+ ATF_REQUIRE(luaL_dostring(raw(state), "error('Hello')") == 1);
+ lutok::debug debug;
+ debug.get_stack(state, 0);
+ lua_pop(raw(state), 1);
+ // Not sure if we can actually validate anything here, other than we did not
+ // crash... (because get_stack only is supposed to update internal values of
+ // the debug structure).
+}
+
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, get_info);
+ ATF_ADD_TEST_CASE(tcs, get_stack);
+}
diff --git a/examples/Makefile b/examples/Makefile
new file mode 100644
index 000000000000..834b20fc114e
--- /dev/null
+++ b/examples/Makefile
@@ -0,0 +1,67 @@
+# Copyright 2012 Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Google Inc. nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+CXX ?= c++
+CPPFLAGS ?=
+CXXFLAGS ?= -Wall -O2
+LDFLAGS ?=
+LIBS ?=
+
+LUTOK_CPPFLAGS = $$(pkg-config --cflags-only-I lutok)
+LUTOK_CXXFLAGS = $$(pkg-config --cflags-only-other lutok)
+LUTOK_LDFLAGS = $$(pkg-config --libs-only-L lutok) \
+ $$(pkg-config --libs-only-other lutok)
+LUTOK_LIBS = $$(pkg-config --libs-only-l lutok)
+
+BUILD = $(CXX) \
+ $(CPPFLAGS) $(LUTOK_CPPFLAGS) \
+ $(CXXFLAGS) $(LUTOK_CXXFLAGS) \
+ $(LDFLAGS) $(LUTOK_LDFLAGS) \
+ -o $${target} $${source} \
+ $(LIBS) $(LUTOK_LIBS)
+
+PROGRAMS = bindings hello interpreter raii
+
+.PHONY: all
+all: $(PROGRAMS)
+
+bindings: bindings.cpp
+ @target=bindings source=bindings.cpp; echo $(BUILD); $(BUILD)
+
+hello: hello.cpp
+ @target=hello source=hello.cpp; echo $(BUILD); $(BUILD)
+
+interpreter: interpreter.cpp
+ @target=interpreter source=interpreter.cpp; echo $(BUILD); $(BUILD)
+
+raii: raii.cpp
+ @target=raii source=raii.cpp; echo $(BUILD); $(BUILD)
+
+.PHONY: clean
+clean:
+ rm -f $(PROGRAMS)
diff --git a/examples/bindings.cpp b/examples/bindings.cpp
new file mode 100644
index 000000000000..dca235a8962d
--- /dev/null
+++ b/examples/bindings.cpp
@@ -0,0 +1,133 @@
+// Copyright 2012 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file examples/bindings.cpp
+/// Showcases how to define Lua functions from C++ code.
+///
+/// A major selling point of Lua is that it is very easy too hook native C and
+/// C++ functions into the runtime environment so that Lua can call them. The
+/// purpose of this example program is to show how this is done by using Lutok.
+
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+
+#include <lutok/exceptions.hpp>
+#include <lutok/operations.hpp>
+#include <lutok/state.ipp>
+
+
+/// Calculates the factorial of a given number.
+///
+/// \param i The postivie number to calculate the factorial of.
+///
+/// \return The factorial of i.
+static int
+factorial(const int i)
+{
+ assert(i >= 0);
+
+ if (i == 0)
+ return 1;
+ else
+ return i * factorial(i - 1);
+}
+
+
+/// A custom factorial function for Lua.
+///
+/// \pre stack(-1) contains the number to calculate the factorial of.
+/// \post stack(-1) contains the result of the operation.
+///
+/// \param state The Lua state from which to get the function arguments and into
+/// which to push the results.
+///
+/// \return The number of results pushed onto the stack, i.e. 1.
+///
+/// \throw std::runtime_error If the input parameters are invalid. Note that
+/// Lutok will convert this exception to lutok::error.
+static int
+lua_factorial(lutok::state& state)
+{
+ if (!state.is_number(-1))
+ throw std::runtime_error("Argument to factorial must be an integer");
+ const int i = state.to_integer(-1);
+ if (i < 0)
+ throw std::runtime_error("Argument to factorial must be positive");
+ state.push_integer(factorial(i));
+ return 1;
+}
+
+
+/// Program's entry point.
+///
+/// \param argc Length of argv. Must be 2.
+/// \param argv Command-line arguments to the program. The first argument to
+/// the tool has to be a number.
+///
+/// \return A system exit code.
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ std::cerr << "Usage: bindings <number>\n";
+ return EXIT_FAILURE;
+ }
+
+ // Create a new Lua session and load the print() function.
+ lutok::state state;
+ state.open_base();
+
+ // Construct a 'module' that contains an entry point to our native factorial
+ // function. A module is just a Lua table that contains a mapping of names
+ // to functions. Instead of creating a module by using our create_module()
+ // helper function, we could have used push_cxx_function on the state to
+ // define the function ourselves.
+ std::map< std::string, lutok::cxx_function > module;
+ module["factorial"] = lua_factorial;
+ lutok::create_module(state, "native", module);
+
+ // Use a little Lua script to call our native factorial function providing
+ // it the first argument passed to the program. Note that this will error
+ // out in a controlled manner if the passed argument is not an integer. The
+ // important thing to notice is that the exception comes from our own C++
+ // binding and that it has been converted to a lutok::error.
+ std::ostringstream script;
+ script << "print(native.factorial(" << argv[1] << "))";
+ try {
+ lutok::do_string(state, script.str(), 0, 0, 0);
+ return EXIT_SUCCESS;
+ } catch (const lutok::error& e) {
+ std::cerr << "ERROR: " << e.what() << '\n';
+ return EXIT_FAILURE;
+ }
+}
diff --git a/examples/hello.cpp b/examples/hello.cpp
new file mode 100644
index 000000000000..40afdd5724b7
--- /dev/null
+++ b/examples/hello.cpp
@@ -0,0 +1,58 @@
+// Copyright 2012 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file examples/hello.cpp
+/// Minimal example using Lua code to print a traditional hello-world message.
+
+#include <cstdlib>
+
+#include <lutok/state.ipp>
+
+
+/// Program's entry point.
+///
+/// \return A system exit code.
+int
+main(void)
+{
+ // Initializes a new Lua state. Every Lua state is independent from each
+ // other.
+ lutok::state state;
+
+ // Loads the standard library into the Lua state. Among many other
+ // functions, this gives us access to print(), which is used below.
+ state.open_base();
+
+ // Pushes the print() function into the Lua stack, then its only argument,
+ // and then proceeds to execute it within the Lua state.
+ state.get_global("print");
+ state.push_string("Hello, world!");
+ state.pcall(1, 0, 0);
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples/interpreter.cpp b/examples/interpreter.cpp
new file mode 100644
index 000000000000..08fba796a45d
--- /dev/null
+++ b/examples/interpreter.cpp
@@ -0,0 +1,83 @@
+// Copyright 2012 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file examples/interpreter.cpp
+/// Implementation of a basic command-line Lua interpreter.
+
+#include <cstdlib>
+#include <iostream>
+#include <string>
+
+#include <lutok/exceptions.hpp>
+#include <lutok/operations.hpp>
+#include <lutok/state.ipp>
+
+
+/// Executes a Lua statement provided by the user with error checking.
+///
+/// \param state The Lua state in which to process the statement.
+/// \param line The textual statement provided by the user.
+static void
+run_statement(lutok::state& state, const std::string& line)
+{
+ try {
+ // This utility function allows us to feed a given piece of Lua code to
+ // the interpreter and process it. The piece of code can include
+ // multiple statements separated by a semicolon or by a newline
+ // character.
+ lutok::do_string(state, line, 0, 0, 0);
+ } catch (const lutok::error& error) {
+ std::cerr << "ERROR: " << error.what() << '\n';
+ }
+}
+
+
+/// Program's entry point.
+///
+/// \return A system exit code.
+int
+main(void)
+{
+ // Create a new session and load some standard libraries.
+ lutok::state state;
+ state.open_base();
+ state.open_string();
+ state.open_table();
+
+ for (;;) {
+ std::cout << "lua> ";
+ std::cout.flush();
+
+ std::string line;
+ if (!std::getline(std::cin, line).good())
+ break;
+ run_statement(state, line);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples/raii.cpp b/examples/raii.cpp
new file mode 100644
index 000000000000..eae76538e992
--- /dev/null
+++ b/examples/raii.cpp
@@ -0,0 +1,126 @@
+// Copyright 2012 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file examples/raii.cpp
+/// Demonstrates how RAII helps in keeping the Lua state consistent.
+///
+/// One of the major complains that is raised against the Lua C API is that it
+/// is very hard to ensure it remains consistent during the execution of the
+/// program. In the case of native C code, there exist many tools that help the
+/// developer catch memory leaks, access to uninitialized variables, etc.
+/// However, when using the Lua C API, none of these tools can validate that,
+/// for example, the Lua stack remains balanced across calls.
+///
+/// Enter RAII. The RAII pattern, intensively applied by Lutok, helps the
+/// developer in maintaining the Lua state consistent at all times in a
+/// transparent manner. This example program attempts to illustrate this.
+
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+#include <string>
+
+#include <lutok/operations.hpp>
+#include <lutok/stack_cleaner.hpp>
+#include <lutok/state.ipp>
+
+
+/// Prints the string-typed field of a table.
+///
+/// If the field contains a string, this function prints its value. If the
+/// field contains any other type, this prints an error message.
+///
+/// \pre The top of the Lua stack in 'state' references a table.
+///
+/// \param state The Lua state.
+/// \param field The name of the string-typed field.
+static void
+print_table_field(lutok::state& state, const std::string& field)
+{
+ assert(state.is_table(-1));
+
+ // Bring in some RAII magic: the stack_cleaner object captures the current
+ // height of the Lua stack at this point. Whenever the object goes out of
+ // scope, it will pop as many entries from the stack as necessary to restore
+ // the stack to its previous level.
+ //
+ // This ensures that, no matter how we exit the function, we do not leak
+ // objects in the stack.
+ lutok::stack_cleaner cleaner(state);
+
+ // Stack contents: -1: table.
+ state.push_string(field);
+ // Stack contents: -2: table, -1: field name.
+ state.get_table(-2);
+ // Stack contents: -2: table, -1: field value.
+
+ if (!state.is_string(-1)) {
+ std::cout << "The field " << field << " does not contain a string\n";
+ // Stack contents: -2: table, -1: field value.
+ //
+ // This is different than when we started! We should pop our extra
+ // value from the stack at this point. However, it is extremely common
+ // for software to have bugs (in this case, leaks) in error paths,
+ // mostly because such code paths are rarely exercised.
+ //
+ // By using the stack_cleaner object, we can be confident that the Lua
+ // stack will be cleared for us at this point, no matter what happened
+ // earlier on the stack nor how we exit the function.
+ return;
+ }
+
+ std::cout << "String in field " << field << ": " << state.to_string(-1)
+ << '\n';
+ // A well-behaved program explicitly pops anything extra from the stack to
+ // return it to its original state. Mostly for clarity.
+ state.pop(1);
+
+ // Stack contents: -1: table. Same as when we started.
+}
+
+
+/// Program's entry point.
+///
+/// \return A system exit code.
+int
+main(void)
+{
+ lutok::state state;
+ state.open_base();
+
+ lutok::do_string(state, "example = {foo='hello', bar=123, baz='bye'}",
+ 0, 0, 0);
+
+ state.get_global("example");
+ print_table_field(state, "foo");
+ print_table_field(state, "bar");
+ print_table_field(state, "baz");
+ state.pop(1);
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples_test.sh b/examples_test.sh
new file mode 100755
index 000000000000..936fa865d0d4
--- /dev/null
+++ b/examples_test.sh
@@ -0,0 +1,115 @@
+#! __ATF_SH__
+# Copyright 2012 Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Google Inc. nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Cxx="__CXX__"
+ExamplesDir="__EXAMPLESDIR__"
+LibDir="__LIBDIR__"
+
+
+make_example() {
+ cp "${ExamplesDir}/Makefile" "${ExamplesDir}/${1}.cpp" .
+ make CXX="${Cxx}" "${1}"
+
+ # Ensure that the binary we just built can find liblutok. This is
+ # needed because the lutok.pc file (which the Makefile used above
+ # queries) does not provide rpaths to the installed library and
+ # therefore the binary may not be able to locate it. Hardcoding the
+ # rpath flags into lutok.pc is non-trivial because we simply don't
+ # have any knowledge about what the correct flag to set an rpath is.
+ #
+ # Additionally, setting rpaths is not always the right thing to do.
+ # For example, pkgsrc will automatically change lutok.pc to add the
+ # missing rpath, in which case this is unnecessary. But in the case
+ # of Fedora, adding rpaths goes against the packaging guidelines.
+ if [ -n "${LD_LIBRARY_PATH}" ]; then
+ export LD_LIBRARY_PATH="${LibDir}:${LD_LIBRARY_PATH}"
+ else
+ export LD_LIBRARY_PATH="${LibDir}"
+ fi
+}
+
+
+example_test_case() {
+ local name="${1}"; shift
+
+ atf_test_case "${name}"
+ eval "${name}_head() { \
+ atf_set 'require.files' '${ExamplesDir}/${name}.cpp'; \
+ atf_set 'require.progs' 'make pkg-config'; \
+ }"
+ eval "${name}_body() { \
+ make_example '${name}'; \
+ ${name}_validate; \
+ }"
+}
+
+
+example_test_case bindings
+bindings_validate() {
+ atf_check -s exit:0 -o inline:'120\n' ./bindings 5
+ atf_check -s exit:1 -e match:'Argument.*must be an integer' ./bindings foo
+ atf_check -s exit:1 -e match:'Argument.*must be positive' ./bindings -5
+}
+
+
+example_test_case hello
+hello_validate() {
+ atf_check -s exit:0 -o inline:'Hello, world!\n' ./hello
+}
+
+
+example_test_case interpreter
+interpreter_validate() {
+ cat >script.lua <<EOF
+test_variable = 12345
+print("From the interpreter: " .. (test_variable - 345))
+EOF
+
+ atf_check -s exit:0 -o match:"From the interpreter: 12000" \
+ -x "./interpreter <script.lua"
+}
+
+
+example_test_case raii
+raii_validate() {
+cat >expout <<EOF
+String in field foo: hello
+String in field bar: 123
+String in field baz: bye
+EOF
+ atf_check -s exit:0 -o file:expout ./raii
+}
+
+
+atf_init_test_cases() {
+ atf_add_test_case bindings
+ atf_add_test_case hello
+ atf_add_test_case interpreter
+ atf_add_test_case raii
+}
diff --git a/exceptions.cpp b/exceptions.cpp
new file mode 100644
index 000000000000..2b1c968f3f34
--- /dev/null
+++ b/exceptions.cpp
@@ -0,0 +1,126 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <cassert>
+
+#include <lua.hpp>
+
+#include "c_gate.hpp"
+#include "exceptions.hpp"
+#include "state.ipp"
+
+
+/// Constructs a new error with a plain-text message.
+///
+/// \param message The plain-text error message.
+lutok::error::error(const std::string& message) :
+ std::runtime_error(message)
+{
+}
+
+
+/// Destructor for the error.
+lutok::error::~error(void) throw()
+{
+}
+
+
+/// Constructs a new error.
+///
+/// \param api_function_ The name of the API function that caused the error.
+/// \param message The plain-text error message provided by Lua.
+lutok::api_error::api_error(const std::string& api_function_,
+ const std::string& message) :
+ error(message),
+ _api_function(api_function_)
+{
+}
+
+
+/// Destructor for the error.
+lutok::api_error::~api_error(void) throw()
+{
+}
+
+
+/// Constructs a new api_error with the message on the top of the Lua stack.
+///
+/// \pre There is an error message on the top of the stack.
+/// \post The error message is popped from the stack.
+///
+/// \param state_ The Lua state.
+/// \param api_function_ The name of the Lua API function that caused the error.
+///
+/// \return A new api_error with the popped message.
+lutok::api_error
+lutok::api_error::from_stack(state& state_, const std::string& api_function_)
+{
+ lua_State* raw_state = lutok::state_c_gate(state_).c_state();
+
+ assert(lua_isstring(raw_state, -1));
+ const std::string message = lua_tostring(raw_state, -1);
+ lua_pop(raw_state, 1);
+ return lutok::api_error(api_function_, message);
+}
+
+
+/// Gets the name of the Lua API function that caused this error.
+///
+/// \return The name of the function.
+const std::string&
+lutok::api_error::api_function(void) const
+{
+ return _api_function;
+}
+
+
+/// Constructs a new error.
+///
+/// \param filename_ The file that count not be found.
+lutok::file_not_found_error::file_not_found_error(
+ const std::string& filename_) :
+ error("File '" + filename_ + "' not found"),
+ _filename(filename_)
+{
+}
+
+
+/// Destructor for the error.
+lutok::file_not_found_error::~file_not_found_error(void) throw()
+{
+}
+
+
+/// Gets the name of the file that could not be found.
+///
+/// \return The name of the file.
+const std::string&
+lutok::file_not_found_error::filename(void) const
+{
+ return _filename;
+}
diff --git a/exceptions.hpp b/exceptions.hpp
new file mode 100644
index 000000000000..93a794873fad
--- /dev/null
+++ b/exceptions.hpp
@@ -0,0 +1,83 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file exceptions.hpp
+/// Exception types raised by lutok.
+
+#if !defined(LUTOK_EXCEPTIONS_HPP)
+#define LUTOK_EXCEPTIONS_HPP
+
+#include <stdexcept>
+#include <string>
+
+namespace lutok {
+
+
+class state;
+
+
+/// Base exception for lua errors.
+class error : public std::runtime_error {
+public:
+ explicit error(const std::string&);
+ virtual ~error(void) throw();
+};
+
+
+/// Exception for errors raised by the Lua API library.
+class api_error : public error {
+ /// Name of the Lua C API function that caused the error.
+ std::string _api_function;
+
+public:
+ explicit api_error(const std::string&, const std::string&);
+ virtual ~api_error(void) throw();
+
+ static api_error from_stack(state&, const std::string&);
+
+ const std::string& api_function(void) const;
+};
+
+
+/// File not found error.
+class file_not_found_error : public error {
+ /// Name of the not-found file.
+ std::string _filename;
+
+public:
+ explicit file_not_found_error(const std::string&);
+ virtual ~file_not_found_error(void) throw();
+
+ const std::string& filename(void) const;
+};
+
+
+} // namespace lutok
+
+
+#endif // !defined(LUTOK_EXCEPTIONS_HPP)
diff --git a/exceptions_test.cpp b/exceptions_test.cpp
new file mode 100644
index 000000000000..81f2c33d2b7b
--- /dev/null
+++ b/exceptions_test.cpp
@@ -0,0 +1,88 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "exceptions.hpp"
+
+#include <cstring>
+
+#include <atf-c++.hpp>
+
+#include "state.ipp"
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(error);
+ATF_TEST_CASE_BODY(error)
+{
+ const lutok::error e("Some text");
+ ATF_REQUIRE(std::strcmp("Some text", e.what()) == 0);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(api_error__explicit);
+ATF_TEST_CASE_BODY(api_error__explicit)
+{
+ const lutok::api_error e("some_function", "Some text");
+ ATF_REQUIRE(std::strcmp("Some text", e.what()) == 0);
+ ATF_REQUIRE_EQ("some_function", e.api_function());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(api_error__from_stack);
+ATF_TEST_CASE_BODY(api_error__from_stack)
+{
+ lutok::state state;
+ state.push_integer(123);
+ state.push_string("The error message");
+ const lutok::api_error e = lutok::api_error::from_stack(state,
+ "the_function");
+ ATF_REQUIRE_EQ(1, state.get_top());
+ ATF_REQUIRE_EQ(123, state.to_integer(-1));
+ state.pop(1);
+ ATF_REQUIRE(std::strcmp("The error message", e.what()) == 0);
+ ATF_REQUIRE_EQ("the_function", e.api_function());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(file_not_found_error);
+ATF_TEST_CASE_BODY(file_not_found_error)
+{
+ const lutok::file_not_found_error e("missing-file");
+ ATF_REQUIRE(std::strcmp("File 'missing-file' not found", e.what()) == 0);
+ ATF_REQUIRE_EQ("missing-file", e.filename());
+}
+
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, error);
+
+ ATF_ADD_TEST_CASE(tcs, api_error__explicit);
+ ATF_ADD_TEST_CASE(tcs, api_error__from_stack);
+
+ ATF_ADD_TEST_CASE(tcs, file_not_found_error);
+}
diff --git a/include/lutok/README b/include/lutok/README
new file mode 100644
index 000000000000..1177b530f9ee
--- /dev/null
+++ b/include/lutok/README
@@ -0,0 +1,4 @@
+This directory contains forward includes for the public header files of
+Lutok. These files are only necessary during the build of Lutok itself
+so that the compiler can locate the include files in a path that mimics
+the final installation location.
diff --git a/include/lutok/c_gate.hpp b/include/lutok/c_gate.hpp
new file mode 100644
index 000000000000..4fabe16a0ed8
--- /dev/null
+++ b/include/lutok/c_gate.hpp
@@ -0,0 +1 @@
+#include "../../c_gate.hpp"
diff --git a/include/lutok/debug.hpp b/include/lutok/debug.hpp
new file mode 100644
index 000000000000..b942bbd01ccd
--- /dev/null
+++ b/include/lutok/debug.hpp
@@ -0,0 +1 @@
+#include "../../debug.hpp"
diff --git a/include/lutok/exceptions.hpp b/include/lutok/exceptions.hpp
new file mode 100644
index 000000000000..97f49a15fd9f
--- /dev/null
+++ b/include/lutok/exceptions.hpp
@@ -0,0 +1 @@
+#include "../../exceptions.hpp"
diff --git a/include/lutok/operations.hpp b/include/lutok/operations.hpp
new file mode 100644
index 000000000000..87d105376d1d
--- /dev/null
+++ b/include/lutok/operations.hpp
@@ -0,0 +1 @@
+#include "../../operations.hpp"
diff --git a/include/lutok/stack_cleaner.hpp b/include/lutok/stack_cleaner.hpp
new file mode 100644
index 000000000000..99edfb8dfd01
--- /dev/null
+++ b/include/lutok/stack_cleaner.hpp
@@ -0,0 +1 @@
+#include "../../stack_cleaner.hpp"
diff --git a/include/lutok/state.hpp b/include/lutok/state.hpp
new file mode 100644
index 000000000000..48ac65ceb64f
--- /dev/null
+++ b/include/lutok/state.hpp
@@ -0,0 +1 @@
+#include "../../state.hpp"
diff --git a/include/lutok/state.ipp b/include/lutok/state.ipp
new file mode 100644
index 000000000000..531d9c19dacd
--- /dev/null
+++ b/include/lutok/state.ipp
@@ -0,0 +1 @@
+#include "../../state.ipp"
diff --git a/lutok.pc.in b/lutok.pc.in
new file mode 100644
index 000000000000..2886dfc3eeb8
--- /dev/null
+++ b/lutok.pc.in
@@ -0,0 +1,8 @@
+includedir=__INCLUDEDIR__
+libdir=__LIBDIR__
+
+Name: lutok
+Description: Lightweight C++ API for Lua
+Version: __VERSION__
+Cflags: __LUA_CFLAGS__ -I${includedir}
+Libs: __LUA_LIBS__ -L${libdir} -llutok
diff --git a/m4/.gitignore b/m4/.gitignore
new file mode 100644
index 000000000000..38066ddf7cad
--- /dev/null
+++ b/m4/.gitignore
@@ -0,0 +1,5 @@
+libtool.m4
+ltoptions.m4
+ltsugar.m4
+ltversion.m4
+lt~obsolete.m4
diff --git a/m4/compiler-features.m4 b/m4/compiler-features.m4
new file mode 100644
index 000000000000..55ff4f42b261
--- /dev/null
+++ b/m4/compiler-features.m4
@@ -0,0 +1,100 @@
+dnl Copyright 2010 Google Inc.
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions are
+dnl met:
+dnl
+dnl * Redistributions of source code must retain the above copyright
+dnl notice, this list of conditions and the following disclaimer.
+dnl * Redistributions in binary form must reproduce the above copyright
+dnl notice, this list of conditions and the following disclaimer in the
+dnl documentation and/or other materials provided with the distribution.
+dnl * Neither the name of Google Inc. nor the names of its contributors
+dnl may be used to endorse or promote products derived from this software
+dnl without specific prior written permission.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+dnl
+dnl KYUA_REQUIRE_CXX
+dnl
+dnl Ensures the C++ compiler detected by AC_PROG_CXX is present and works.
+dnl The compiler check should be performed here, but it seems like Autoconf
+dnl does not allow it.
+dnl
+AC_DEFUN([KYUA_REQUIRE_CXX], [
+ AC_CACHE_CHECK([whether the C++ compiler works],
+ [atf_cv_prog_cxx_works],
+ [AC_LANG_PUSH([C++])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])],
+ [atf_cv_prog_cxx_works=yes],
+ [atf_cv_prog_cxx_works=no])
+ AC_LANG_POP([C++])])
+ if test "${atf_cv_prog_cxx_works}" = no; then
+ AC_MSG_ERROR([C++ compiler cannot create executables])
+ fi
+])
+
+dnl
+dnl KYUA_ATTRIBUTE_NORETURN
+dnl
+dnl Checks if the current compiler has a way to mark functions that do not
+dnl return and defines ATTRIBUTE_NORETURN to the appropriate string.
+dnl
+AC_DEFUN([KYUA_ATTRIBUTE_NORETURN], [
+ dnl This check is overly simple and should be fixed. For example,
+ dnl Sun's cc does support the noreturn attribute but CC (the C++
+ dnl compiler) does not. And in that case, CC just raises a warning
+ dnl during compilation, not an error.
+ AC_MSG_CHECKING(whether __attribute__((noreturn)) is supported)
+ AC_RUN_IFELSE([AC_LANG_PROGRAM([], [
+#if ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ return 0;
+#else
+ return 1;
+#endif
+ ])],
+ [AC_MSG_RESULT(yes)
+ value="__attribute__((noreturn))"],
+ [AC_MSG_RESULT(no)
+ value=""]
+ )
+ AC_SUBST([ATTRIBUTE_NORETURN], [${value}])
+])
+
+
+dnl
+dnl KYUA_ATTRIBUTE_UNUSED
+dnl
+dnl Checks if the current compiler has a way to mark parameters as unused
+dnl so that the -Wunused-parameter warning can be avoided.
+dnl
+AC_DEFUN([KYUA_ATTRIBUTE_UNUSED], [
+ AC_MSG_CHECKING(whether __attribute__((__unused__)) is supported)
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM([
+static void
+function(int a __attribute__((__unused__)))
+{
+}], [
+ function(3);
+ return 0;
+])],
+ [AC_MSG_RESULT(yes)
+ value="__attribute__((__unused__))"],
+ [AC_MSG_RESULT(no)
+ value=""]
+ )
+ AC_SUBST([ATTRIBUTE_UNUSED], [${value}])
+])
diff --git a/m4/compiler-flags.m4 b/m4/compiler-flags.m4
new file mode 100644
index 000000000000..480e5c740a2a
--- /dev/null
+++ b/m4/compiler-flags.m4
@@ -0,0 +1,159 @@
+dnl Copyright 2010 Google Inc.
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions are
+dnl met:
+dnl
+dnl * Redistributions of source code must retain the above copyright
+dnl notice, this list of conditions and the following disclaimer.
+dnl * Redistributions in binary form must reproduce the above copyright
+dnl notice, this list of conditions and the following disclaimer in the
+dnl documentation and/or other materials provided with the distribution.
+dnl * Neither the name of Google Inc. nor the names of its contributors
+dnl may be used to endorse or promote products derived from this software
+dnl without specific prior written permission.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+dnl \file compiler-flags.m4
+dnl
+dnl Macros to check for the existence of compiler flags. The macros in this
+dnl file support both C and C++.
+dnl
+dnl Be aware that, in order to detect a flag accurately, we may need to enable
+dnl strict warning checking in the compiler (i.e. enable -Werror). Some
+dnl compilers, e.g. Clang, report unknown -W flags as warnings unless -Werror is
+dnl selected. This fact would confuse the flag checks below because we would
+dnl conclude that a flag is valid while in reality it is not. To resolve this,
+dnl the macros below will pass -Werror to the compiler along with any other flag
+dnl being checked.
+
+
+dnl Checks for a compiler flag and sets a result variable.
+dnl
+dnl This is an auxiliary macro for the implementation of _KYUA_FLAG.
+dnl
+dnl \param 1 The shell variable containing the compiler name. Used for
+dnl reporting purposes only. C or CXX.
+dnl \param 2 The shell variable containing the flags for the compiler.
+dnl CFLAGS or CXXFLAGS.
+dnl \param 3 The name of the compiler flag to check for.
+dnl \param 4 The shell variable to set with the result of the test. Will
+dnl be set to 'yes' if the flag is valid, 'no' otherwise.
+dnl \param 5 Additional, optional flags to pass to the C compiler while
+dnl looking for the flag in $3. We use this here to pass -Werror to the
+dnl flag checks (unless we are checking for -Werror already).
+AC_DEFUN([_KYUA_FLAG_AUX], [
+ if test x"${$4-unset}" = xunset; then
+ AC_MSG_CHECKING(whether ${$1} supports $3)
+ saved_flags="${$2}"
+ $4=no
+ $2="${$2} $5 $3"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([], [return 0;])],
+ AC_MSG_RESULT(yes)
+ $4=yes,
+ AC_MSG_RESULT(no))
+ $2="${saved_flags}"
+ fi
+])
+
+
+dnl Checks for a compiler flag and appends it to a result variable.
+dnl
+dnl \param 1 The shell variable containing the compiler name. Used for
+dnl reporting purposes only. CC or CXX.
+dnl \param 2 The shell variable containing the flags for the compiler.
+dnl CFLAGS or CXXFLAGS.
+dnl \param 3 The name of the compiler flag to check for.
+dnl \param 4 The shell variable to which to append $3 if the flag is valid.
+AC_DEFUN([_KYUA_FLAG], [
+ _KYUA_FLAG_AUX([$1], [$2], [-Werror], [kyua_$1_has_werror])
+ if test "$3" = "-Werror"; then
+ found=${kyua_$1_has_werror}
+ else
+ found=unset
+ if test ${kyua_$1_has_werror} = yes; then
+ _KYUA_FLAG_AUX([$1], [$2], [$3], [found], [-Werror])
+ else
+ _KYUA_FLAG_AUX([$1], [$2], [$3], [found], [])
+ fi
+ fi
+ if test ${found} = yes; then
+ $4="${$4} $3"
+ fi
+])
+
+
+dnl Checks for a C compiler flag and appends it to a variable.
+dnl
+dnl \pre The current language is C.
+dnl
+dnl \param 1 The name of the compiler flag to check for.
+dnl \param 2 The shell variable to which to append $1 if the flag is valid.
+AC_DEFUN([KYUA_CC_FLAG], [
+ AC_LANG_ASSERT([C])
+ _KYUA_FLAG([CC], [CFLAGS], [$1], [$2])
+])
+
+
+dnl Checks for a C++ compiler flag and appends it to a variable.
+dnl
+dnl \pre The current language is C++.
+dnl
+dnl \param 1 The name of the compiler flag to check for.
+dnl \param 2 The shell variable to which to append $1 if the flag is valid.
+AC_DEFUN([KYUA_CXX_FLAG], [
+ AC_LANG_ASSERT([C++])
+ _KYUA_FLAG([CXX], [CXXFLAGS], [$1], [$2])
+])
+
+
+dnl Checks for a set of C compiler flags and appends them to CFLAGS.
+dnl
+dnl The checks are performed independently and only when all the checks are
+dnl done, the output variable is modified.
+dnl
+dnl \param 1 Whitespace-separated list of C flags to check.
+AC_DEFUN([KYUA_CC_FLAGS], [
+ AC_LANG_PUSH([C])
+ valid_cflags=
+ for f in $1; do
+ KYUA_CC_FLAG(${f}, valid_cflags)
+ done
+ if test -n "${valid_cflags}"; then
+ CFLAGS="${CFLAGS} ${valid_cflags}"
+ fi
+ AC_LANG_POP([C])
+])
+
+
+dnl Checks for a set of C++ compiler flags and appends them to CXXFLAGS.
+dnl
+dnl The checks are performed independently and only when all the checks are
+dnl done, the output variable is modified.
+dnl
+dnl \pre The current language is C++.
+dnl
+dnl \param 1 Whitespace-separated list of C flags to check.
+AC_DEFUN([KYUA_CXX_FLAGS], [
+ AC_LANG_PUSH([C++])
+ valid_cxxflags=
+ for f in $1; do
+ KYUA_CXX_FLAG(${f}, valid_cxxflags)
+ done
+ if test -n "${valid_cxxflags}"; then
+ CXXFLAGS="${CXXFLAGS} ${valid_cxxflags}"
+ fi
+ AC_LANG_POP([C++])
+])
diff --git a/m4/developer-mode.m4 b/m4/developer-mode.m4
new file mode 100644
index 000000000000..9c9118bf1f86
--- /dev/null
+++ b/m4/developer-mode.m4
@@ -0,0 +1,112 @@
+dnl Copyright 2010 Google Inc.
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions are
+dnl met:
+dnl
+dnl * Redistributions of source code must retain the above copyright
+dnl notice, this list of conditions and the following disclaimer.
+dnl * Redistributions in binary form must reproduce the above copyright
+dnl notice, this list of conditions and the following disclaimer in the
+dnl documentation and/or other materials provided with the distribution.
+dnl * Neither the name of Google Inc. nor the names of its contributors
+dnl may be used to endorse or promote products derived from this software
+dnl without specific prior written permission.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+dnl \file developer-mode.m4
+dnl
+dnl "Developer mode" is a mode in which the build system reports any
+dnl build-time warnings as fatal errors. This helps in minimizing the
+dnl amount of trivial coding problems introduced in the code.
+dnl Unfortunately, this is not bullet-proof due to the wide variety of
+dnl compilers available and their different warning diagnostics.
+dnl
+dnl When developer mode support is added to a package, the compilation will
+dnl gain a bunch of extra warning diagnostics. These will NOT be enforced
+dnl unless developer mode is enabled.
+dnl
+dnl Developer mode is enabled when the user requests it through the
+dnl configure command line, or when building from the repository. The
+dnl latter is to minimize the risk of committing new code with warnings
+dnl into the tree.
+
+
+dnl Adds "developer mode" support to the package.
+dnl
+dnl This macro performs the actual definition of the --enable-developer
+dnl flag and implements all of its logic. See the file-level comment for
+dnl details as to what this implies.
+AC_DEFUN([KYUA_DEVELOPER_MODE], [
+ m4_foreach([language], [$1], [m4_set_add([languages], language)])
+
+ AC_ARG_ENABLE(
+ [developer],
+ AS_HELP_STRING([--enable-developer], [enable developer features]),,
+ [if test -d ${srcdir}/.git; then
+ AC_MSG_NOTICE([building from HEAD; developer mode autoenabled])
+ enable_developer=yes
+ else
+ enable_developer=no
+ fi])
+
+ #
+ # The following warning flags should also be enabled but cannot be.
+ # Reasons given below.
+ #
+ # -Wold-style-cast: Raises errors when using TIOCGWINSZ, at least under
+ # Mac OS X. This is due to the way _IOR is defined.
+ #
+
+ try_c_cxx_flags="-D_FORTIFY_SOURCE=2 \
+ -Wall \
+ -Wcast-qual \
+ -Wextra \
+ -Wpointer-arith \
+ -Wredundant-decls \
+ -Wreturn-type \
+ -Wshadow \
+ -Wsign-compare \
+ -Wswitch \
+ -Wwrite-strings"
+
+ try_c_flags="-Wmissing-prototypes \
+ -Wno-traditional \
+ -Wstrict-prototypes"
+
+ try_cxx_flags="-Wabi \
+ -Wctor-dtor-privacy \
+ -Wno-deprecated \
+ -Wno-non-template-friend \
+ -Wno-pmf-conversions \
+ -Wnon-virtual-dtor \
+ -Woverloaded-virtual \
+ -Wreorder \
+ -Wsign-promo \
+ -Wsynth"
+
+ if test ${enable_developer} = yes; then
+ try_werror=yes
+ try_c_cxx_flags="${try_c_cxx_flags} -g -Werror"
+ else
+ try_werror=no
+ try_c_cxx_flags="${try_c_cxx_flags} -DNDEBUG"
+ fi
+
+ m4_set_contains([languages], [C],
+ [KYUA_CC_FLAGS(${try_c_cxx_flags} ${try_c_flags})])
+ m4_set_contains([languages], [C++],
+ [KYUA_CXX_FLAGS(${try_c_cxx_flags} ${try_cxx_flags})])
+])
diff --git a/m4/doxygen.m4 b/m4/doxygen.m4
new file mode 100644
index 000000000000..a9b7222a5b42
--- /dev/null
+++ b/m4/doxygen.m4
@@ -0,0 +1,62 @@
+dnl Copyright 2010 Google Inc.
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions are
+dnl met:
+dnl
+dnl * Redistributions of source code must retain the above copyright
+dnl notice, this list of conditions and the following disclaimer.
+dnl * Redistributions in binary form must reproduce the above copyright
+dnl notice, this list of conditions and the following disclaimer in the
+dnl documentation and/or other materials provided with the distribution.
+dnl * Neither the name of Google Inc. nor the names of its contributors
+dnl may be used to endorse or promote products derived from this software
+dnl without specific prior written permission.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+dnl
+dnl KYUA_DOXYGEN
+dnl
+dnl Adds a --with-doxygen flag to the configure script and, when Doxygen support
+dnl is requested by the user, sets DOXYGEN to the path of the Doxygen binary and
+dnl enables the WITH_DOXYGEN Automake conditional.
+dnl
+AC_DEFUN([KYUA_DOXYGEN], [
+ AC_ARG_WITH([doxygen],
+ AS_HELP_STRING([--with-doxygen],
+ [build documentation for internal APIs]),
+ [],
+ [with_doxygen=auto])
+
+ if test "${with_doxygen}" = yes; then
+ AC_PATH_PROG([DOXYGEN], [doxygen], [])
+ if test -z "${DOXYGEN}"; then
+ AC_MSG_ERROR([Doxygen explicitly requested but not found])
+ fi
+ elif test "${with_doxygen}" = auto; then
+ AC_PATH_PROG([DOXYGEN], [doxygen], [])
+ elif test "${with_doxygen}" = no; then
+ DOXYGEN=
+ else
+ AC_MSG_CHECKING([for doxygen])
+ DOXYGEN="${with_doxygen}"
+ AC_MSG_RESULT([${DOXYGEN}])
+ if test ! -x "${DOXYGEN}"; then
+ AC_MSG_ERROR([Doxygen binary ${DOXYGEN} is not executable])
+ fi
+ fi
+ AM_CONDITIONAL([WITH_DOXYGEN], [test -n "${DOXYGEN}"])
+ AC_SUBST([DOXYGEN])
+])
diff --git a/m4/lua.m4 b/m4/lua.m4
new file mode 100644
index 000000000000..0d075c576210
--- /dev/null
+++ b/m4/lua.m4
@@ -0,0 +1,69 @@
+dnl Copyright 2011 Google Inc.
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions are
+dnl met:
+dnl
+dnl * Redistributions of source code must retain the above copyright
+dnl notice, this list of conditions and the following disclaimer.
+dnl * Redistributions in binary form must reproduce the above copyright
+dnl notice, this list of conditions and the following disclaimer in the
+dnl documentation and/or other materials provided with the distribution.
+dnl * Neither the name of Google Inc. nor the names of its contributors
+dnl may be used to endorse or promote products derived from this software
+dnl without specific prior written permission.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+dnl
+dnl KYUA_LUA
+dnl
+dnl Helper macro to detect Lua in a variety of systems.
+dnl
+AC_DEFUN([KYUA_LUA], [
+ lua_found=no
+
+ for lua_release in 5.2 5.1; do
+ if test "${lua_found}" = no; then
+ PKG_CHECK_MODULES([LUA], [lua${lua_release} >= ${lua_release}],
+ [lua_found=yes], [true])
+ fi
+ if test "${lua_found}" = no; then
+ PKG_CHECK_MODULES([LUA], [lua-${lua_release} >= ${lua_release}],
+ [lua_found=yes], [true])
+ fi
+ if test "${lua_found}" = no; then
+ PKG_CHECK_MODULES([LUA], [lua >= ${lua_release}],
+ [lua_found=yes], [true])
+ fi
+
+ test "${lua_found}" = no || break
+ done
+
+ if test "${lua_found}" = no; then
+ AC_PATH_PROGS([LUA_CONFIG], [lua-config], [unset])
+ if test "${LUA_CONFIG}" != unset; then
+ AC_SUBST([LUA_CFLAGS], [$(${LUA_CONFIG} --include)])
+ AC_SUBST([LUA_LIBS], [$(${LUA_CONFIG} --libs)])
+ lua_found=yes
+ fi
+ fi
+
+ if test "${lua_found}" = no; then
+ AC_MSG_ERROR([lua (5.1 or newer) is required])
+ else
+ AC_MSG_NOTICE([using LUA_CFLAGS = ${LUA_CFLAGS}])
+ AC_MSG_NOTICE([using LUA_LIBS = ${LUA_LIBS}])
+ fi
+])
diff --git a/operations.cpp b/operations.cpp
new file mode 100644
index 000000000000..1b70dc4ed7a4
--- /dev/null
+++ b/operations.cpp
@@ -0,0 +1,153 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <cassert>
+
+#include <lua.hpp>
+
+#include "exceptions.hpp"
+#include "operations.hpp"
+#include "stack_cleaner.hpp"
+#include "state.hpp"
+
+
+/// Creates a module: i.e. a table with a set of methods in it.
+///
+/// \param s The Lua state.
+/// \param name The name of the module to create.
+/// \param members The list of member functions to add to the module.
+void
+lutok::create_module(state& s, const std::string& name,
+ const std::map< std::string, cxx_function >& members)
+{
+ stack_cleaner cleaner(s);
+ s.new_table();
+ for (std::map< std::string, cxx_function >::const_iterator
+ iter = members.begin(); iter != members.end(); iter++) {
+ s.push_string((*iter).first);
+ s.push_cxx_function((*iter).second);
+ s.set_table(-3);
+ }
+ s.set_global(name);
+}
+
+
+/// Loads and processes a Lua file.
+///
+/// This is a replacement for luaL_dofile but with proper error reporting
+/// and stack control.
+///
+/// \param s The Lua state.
+/// \param file The file to load.
+/// \param nargs The number of arguments on the stack to pass to the file.
+/// \param nresults The number of results to expect; -1 for any.
+/// \param errfunc If not 0, index of a function in the stack to act as an
+/// error handler.
+///
+/// \return The number of results left on the stack.
+///
+/// \throw error If there is a problem processing the file.
+unsigned int
+lutok::do_file(state& s, const std::string& file, const int nargs,
+ const int nresults, const int errfunc)
+{
+ assert(nresults >= -1);
+ const int height = s.get_top() - nargs;
+
+ try {
+ s.load_file(file);
+ if (nargs > 0)
+ s.insert(-nargs - 1);
+ s.pcall(nargs, nresults == -1 ? LUA_MULTRET : nresults,
+ errfunc == 0 ? 0 : errfunc - 1);
+ } catch (const lutok::api_error& e) {
+ throw lutok::error("Failed to load Lua file '" + file + "': " +
+ e.what());
+ }
+
+ const int actual_results = s.get_top() - height;
+ assert(nresults == -1 || actual_results == nresults);
+ assert(actual_results >= 0);
+ return static_cast< unsigned int >(actual_results);
+}
+
+
+/// Processes a Lua script.
+///
+/// This is a replacement for luaL_dostring but with proper error reporting
+/// and stack control.
+///
+/// \param s The Lua state.
+/// \param str The string to process.
+/// \param nargs The number of arguments on the stack to pass to the chunk.
+/// \param nresults The number of results to expect; -1 for any.
+/// \param errfunc If not 0, index of a function in the stack to act as an
+/// error handler.
+///
+/// \return The number of results left on the stack.
+///
+/// \throw error If there is a problem processing the string.
+unsigned int
+lutok::do_string(state& s, const std::string& str, const int nargs,
+ const int nresults, const int errfunc)
+{
+ assert(nresults >= -1);
+ const int height = s.get_top() - nargs;
+
+ try {
+ s.load_string(str);
+ if (nargs > 0)
+ s.insert(-nargs - 1);
+ s.pcall(nargs, nresults == -1 ? LUA_MULTRET : nresults,
+ errfunc == 0 ? 0 : errfunc - 1);
+ } catch (const lutok::api_error& e) {
+ throw lutok::error("Failed to process Lua string '" + str + "': " +
+ e.what());
+ }
+
+ const int actual_results = s.get_top() - height;
+ assert(nresults == -1 || actual_results == nresults);
+ assert(actual_results >= 0);
+ return static_cast< unsigned int >(actual_results);
+}
+
+
+/// Convenience function to evaluate a Lua expression.
+///
+/// \param s The Lua state.
+/// \param expression The textual expression to evaluate.
+/// \param nresults The number of results to leave on the stack. Must be
+/// positive.
+///
+/// \throw api_error If there is a problem evaluating the expression.
+void
+lutok::eval(state& s, const std::string& expression, const int nresults)
+{
+ assert(nresults > 0);
+ do_string(s, "return " + expression, 0, nresults, 0);
+}
diff --git a/operations.hpp b/operations.hpp
new file mode 100644
index 000000000000..ead7c77d33bb
--- /dev/null
+++ b/operations.hpp
@@ -0,0 +1,55 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file operations.hpp
+/// Extra generic functions to interact with Lua.
+
+#if !defined(LUTOK_OPERATIONS_HPP)
+#define LUTOK_OPERATIONS_HPP
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <lutok/state.hpp>
+
+namespace lutok {
+
+
+void create_module(state&, const std::string&,
+ const std::map< std::string, cxx_function >&);
+unsigned int do_file(state&, const std::string&, const int, const int,
+ const int);
+unsigned int do_string(state&, const std::string&, const int, const int,
+ const int);
+void eval(state&, const std::string&, const int);
+
+
+} // namespace lutok
+
+#endif // !defined(LUTOK_OPERATIONS_HPP)
diff --git a/operations_test.cpp b/operations_test.cpp
new file mode 100644
index 000000000000..72800f7d9bab
--- /dev/null
+++ b/operations_test.cpp
@@ -0,0 +1,372 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "operations.hpp"
+
+#include <fstream>
+
+#include <atf-c++.hpp>
+
+#include "exceptions.hpp"
+#include "state.ipp"
+#include "test_utils.hpp"
+
+
+namespace {
+
+
+/// Addition function for injection into Lua.
+///
+/// \pre stack(-2) The first summand.
+/// \pre stack(-1) The second summand.
+/// \post stack(-1) The result of the sum.
+///
+/// \param state The Lua state.
+///
+/// \return The number of results (1).
+static int
+hook_add(lutok::state& state)
+{
+ state.push_integer(state.to_integer(-1) + state.to_integer(-2));
+ return 1;
+}
+
+
+/// Multiplication function for injection into Lua.
+///
+/// \pre stack(-2) The first factor.
+/// \pre stack(-1) The second factor.
+/// \post stack(-1) The product.
+///
+/// \param state The Lua state.
+///
+/// \return The number of results (1).
+static int
+hook_multiply(lutok::state& state)
+{
+ state.push_integer(state.to_integer(-1) * state.to_integer(-2));
+ return 1;
+}
+
+
+} // anonymous namespace
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(create_module__empty);
+ATF_TEST_CASE_BODY(create_module__empty)
+{
+ lutok::state state;
+ std::map< std::string, lutok::cxx_function > members;
+ lutok::create_module(state, "my_math", members);
+
+ state.open_base();
+ lutok::do_string(state, "return next(my_math) == nil", 0, 1, 0);
+ ATF_REQUIRE(state.to_boolean(-1));
+ state.pop(1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(create_module__one);
+ATF_TEST_CASE_BODY(create_module__one)
+{
+ lutok::state state;
+ std::map< std::string, lutok::cxx_function > members;
+ members["add"] = hook_add;
+ lutok::create_module(state, "my_math", members);
+
+ lutok::do_string(state, "return my_math.add(10, 20)", 0, 1, 0);
+ ATF_REQUIRE_EQ(30, state.to_integer(-1));
+ state.pop(1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(create_module__many);
+ATF_TEST_CASE_BODY(create_module__many)
+{
+ lutok::state state;
+ std::map< std::string, lutok::cxx_function > members;
+ members["add"] = hook_add;
+ members["multiply"] = hook_multiply;
+ members["add2"] = hook_add;
+ lutok::create_module(state, "my_math", members);
+
+ lutok::do_string(state, "return my_math.add(10, 20)", 0, 1, 0);
+ ATF_REQUIRE_EQ(30, state.to_integer(-1));
+ lutok::do_string(state, "return my_math.multiply(10, 20)", 0, 1, 0);
+ ATF_REQUIRE_EQ(200, state.to_integer(-1));
+ lutok::do_string(state, "return my_math.add2(20, 30)", 0, 1, 0);
+ ATF_REQUIRE_EQ(50, state.to_integer(-1));
+ state.pop(3);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_file__some_args);
+ATF_TEST_CASE_BODY(do_file__some_args)
+{
+ std::ofstream output("test.lua");
+ output << "local a1, a2 = ...\nreturn a1 * 2, a2 * 2\n";
+ output.close();
+
+ lutok::state state;
+ state.push_integer(456);
+ state.push_integer(3);
+ state.push_integer(5);
+ state.push_integer(123);
+ ATF_REQUIRE_EQ(2, lutok::do_file(state, "test.lua", 3, -1, 0));
+ ATF_REQUIRE_EQ(3, state.get_top());
+ ATF_REQUIRE_EQ(456, state.to_integer(-3));
+ ATF_REQUIRE_EQ(6, state.to_integer(-2));
+ ATF_REQUIRE_EQ(10, state.to_integer(-1));
+ state.pop(3);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_file__any_results);
+ATF_TEST_CASE_BODY(do_file__any_results)
+{
+ std::ofstream output("test.lua");
+ output << "return 10, 20, 30\n";
+ output.close();
+
+ lutok::state state;
+ ATF_REQUIRE_EQ(3, lutok::do_file(state, "test.lua", 0, -1, 0));
+ ATF_REQUIRE_EQ(3, state.get_top());
+ ATF_REQUIRE_EQ(10, state.to_integer(-3));
+ ATF_REQUIRE_EQ(20, state.to_integer(-2));
+ ATF_REQUIRE_EQ(30, state.to_integer(-1));
+ state.pop(3);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_file__no_results);
+ATF_TEST_CASE_BODY(do_file__no_results)
+{
+ std::ofstream output("test.lua");
+ output << "return 10, 20, 30\n";
+ output.close();
+
+ lutok::state state;
+ ATF_REQUIRE_EQ(0, lutok::do_file(state, "test.lua", 0, 0, 0));
+ ATF_REQUIRE_EQ(0, state.get_top());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_file__many_results);
+ATF_TEST_CASE_BODY(do_file__many_results)
+{
+ std::ofstream output("test.lua");
+ output << "return 10, 20, 30\n";
+ output.close();
+
+ lutok::state state;
+ ATF_REQUIRE_EQ(2, lutok::do_file(state, "test.lua", 0, 2, 0));
+ ATF_REQUIRE_EQ(2, state.get_top());
+ ATF_REQUIRE_EQ(10, state.to_integer(-2));
+ ATF_REQUIRE_EQ(20, state.to_integer(-1));
+ state.pop(2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_file__not_found);
+ATF_TEST_CASE_BODY(do_file__not_found)
+{
+ lutok::state state;
+ stack_balance_checker checker(state);
+ ATF_REQUIRE_THROW_RE(lutok::file_not_found_error, "missing.lua",
+ lutok::do_file(state, "missing.lua", 0, 0, 0));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_file__error);
+ATF_TEST_CASE_BODY(do_file__error)
+{
+ std::ofstream output("test.lua");
+ output << "a b c\n";
+ output.close();
+
+ lutok::state state;
+ stack_balance_checker checker(state);
+ ATF_REQUIRE_THROW_RE(lutok::error, "Failed to load Lua file 'test.lua'",
+ lutok::do_file(state, "test.lua", 0, 0, 0));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_file__error_with_errfunc);
+ATF_TEST_CASE_BODY(do_file__error_with_errfunc)
+{
+ std::ofstream output("test.lua");
+ output << "unknown_function()\n";
+ output.close();
+
+ lutok::state state;
+ lutok::eval(state, "function(message) return 'This is an error!' end", 1);
+ {
+ stack_balance_checker checker(state);
+ ATF_REQUIRE_THROW_RE(lutok::error, "This is an error!",
+ lutok::do_file(state, "test.lua", 0, 0, -2));
+ }
+ state.pop(1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_string__some_args);
+ATF_TEST_CASE_BODY(do_string__some_args)
+{
+ lutok::state state;
+ state.push_integer(456);
+ state.push_integer(3);
+ state.push_integer(5);
+ state.push_integer(123);
+ ATF_REQUIRE_EQ(2, lutok::do_string(
+ state, "local a1, a2 = ...\nreturn a1 * 2, a2 * 2\n", 3, -1, 0));
+ ATF_REQUIRE_EQ(3, state.get_top());
+ ATF_REQUIRE_EQ(456, state.to_integer(-3));
+ ATF_REQUIRE_EQ(6, state.to_integer(-2));
+ ATF_REQUIRE_EQ(10, state.to_integer(-1));
+ state.pop(3);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_string__any_results);
+ATF_TEST_CASE_BODY(do_string__any_results)
+{
+ lutok::state state;
+ ATF_REQUIRE_EQ(3, lutok::do_string(state, "return 10, 20, 30", 0, -1, 0));
+ ATF_REQUIRE_EQ(3, state.get_top());
+ ATF_REQUIRE_EQ(10, state.to_integer(-3));
+ ATF_REQUIRE_EQ(20, state.to_integer(-2));
+ ATF_REQUIRE_EQ(30, state.to_integer(-1));
+ state.pop(3);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_string__no_results);
+ATF_TEST_CASE_BODY(do_string__no_results)
+{
+ lutok::state state;
+ ATF_REQUIRE_EQ(0, lutok::do_string(state, "return 10, 20, 30", 0, 0, 0));
+ ATF_REQUIRE_EQ(0, state.get_top());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_string__many_results);
+ATF_TEST_CASE_BODY(do_string__many_results)
+{
+ lutok::state state;
+ ATF_REQUIRE_EQ(2, lutok::do_string(state, "return 10, 20, 30", 0, 2, 0));
+ ATF_REQUIRE_EQ(2, state.get_top());
+ ATF_REQUIRE_EQ(10, state.to_integer(-2));
+ ATF_REQUIRE_EQ(20, state.to_integer(-1));
+ state.pop(2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_string__error);
+ATF_TEST_CASE_BODY(do_string__error)
+{
+ lutok::state state;
+ stack_balance_checker checker(state);
+ ATF_REQUIRE_THROW_RE(lutok::error, "Failed to process Lua string 'a b c'",
+ lutok::do_string(state, "a b c", 0, 0, 0));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(do_string__error_with_errfunc);
+ATF_TEST_CASE_BODY(do_string__error_with_errfunc)
+{
+ lutok::state state;
+ lutok::eval(state, "function(message) return 'This is an error!' end", 1);
+ {
+ stack_balance_checker checker(state);
+ ATF_REQUIRE_THROW_RE(lutok::error, "This is an error!",
+ lutok::do_string(state, "unknown_function()",
+ 0, 0, -2));
+ }
+ state.pop(1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(eval__one_result);
+ATF_TEST_CASE_BODY(eval__one_result)
+{
+ lutok::state state;
+ stack_balance_checker checker(state);
+ lutok::eval(state, "3 + 10", 1);
+ ATF_REQUIRE_EQ(13, state.to_integer(-1));
+ state.pop(1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(eval__many_results);
+ATF_TEST_CASE_BODY(eval__many_results)
+{
+ lutok::state state;
+ stack_balance_checker checker(state);
+ lutok::eval(state, "5, 8, 10", 3);
+ ATF_REQUIRE_EQ(5, state.to_integer(-3));
+ ATF_REQUIRE_EQ(8, state.to_integer(-2));
+ ATF_REQUIRE_EQ(10, state.to_integer(-1));
+ state.pop(3);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(eval__error);
+ATF_TEST_CASE_BODY(eval__error)
+{
+ lutok::state state;
+ stack_balance_checker checker(state);
+ ATF_REQUIRE_THROW(lutok::error,
+ lutok::eval(state, "non_existent.method()", 1));
+}
+
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, create_module__empty);
+ ATF_ADD_TEST_CASE(tcs, create_module__one);
+ ATF_ADD_TEST_CASE(tcs, create_module__many);
+
+ ATF_ADD_TEST_CASE(tcs, do_file__some_args);
+ ATF_ADD_TEST_CASE(tcs, do_file__any_results);
+ ATF_ADD_TEST_CASE(tcs, do_file__no_results);
+ ATF_ADD_TEST_CASE(tcs, do_file__many_results);
+ ATF_ADD_TEST_CASE(tcs, do_file__not_found);
+ ATF_ADD_TEST_CASE(tcs, do_file__error);
+ ATF_ADD_TEST_CASE(tcs, do_file__error_with_errfunc);
+
+ ATF_ADD_TEST_CASE(tcs, do_string__some_args);
+ ATF_ADD_TEST_CASE(tcs, do_string__any_results);
+ ATF_ADD_TEST_CASE(tcs, do_string__no_results);
+ ATF_ADD_TEST_CASE(tcs, do_string__many_results);
+ ATF_ADD_TEST_CASE(tcs, do_string__error);
+ ATF_ADD_TEST_CASE(tcs, do_string__error_with_errfunc);
+
+ ATF_ADD_TEST_CASE(tcs, eval__one_result);
+ ATF_ADD_TEST_CASE(tcs, eval__many_results);
+ ATF_ADD_TEST_CASE(tcs, eval__error);
+}
diff --git a/stack_cleaner.cpp b/stack_cleaner.cpp
new file mode 100644
index 000000000000..419e55a12981
--- /dev/null
+++ b/stack_cleaner.cpp
@@ -0,0 +1,91 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <cassert>
+#include <cstring>
+
+#include "stack_cleaner.hpp"
+#include "state.ipp"
+
+
+/// Internal implementation for lutok::stack_cleaner.
+struct lutok::stack_cleaner::impl {
+ /// Reference to the Lua state this stack_cleaner refers to.
+ state& state_ref;
+
+ /// The depth of the Lua stack to be restored.
+ unsigned int original_depth;
+
+ /// Constructor.
+ ///
+ /// \param state_ref_ Reference to the Lua state.
+ /// \param original_depth_ The depth of the Lua stack.
+ impl(state& state_ref_, const unsigned int original_depth_) :
+ state_ref(state_ref_),
+ original_depth(original_depth_)
+ {
+ }
+};
+
+
+/// Creates a new stack cleaner.
+///
+/// This gathers the current height of the stack so that extra elements can be
+/// popped during destruction.
+///
+/// \param state_ The Lua state.
+lutok::stack_cleaner::stack_cleaner(state& state_) :
+ _pimpl(new impl(state_, state_.get_top()))
+{
+}
+
+
+/// Pops any values from the stack not known at construction time.
+///
+/// \pre The current height of the stack must be equal or greater to the height
+/// of the stack when this object was instantiated.
+lutok::stack_cleaner::~stack_cleaner(void)
+{
+ const unsigned int current_depth = _pimpl->state_ref.get_top();
+ assert(current_depth >= _pimpl->original_depth);
+ const unsigned int diff = current_depth - _pimpl->original_depth;
+ if (diff > 0)
+ _pimpl->state_ref.pop(diff);
+}
+
+
+/// Forgets about any elements currently in the stack.
+///
+/// This allows a function to return values on the stack because all the
+/// elements that are currently in the stack will not be touched during
+/// destruction when the function is called.
+void
+lutok::stack_cleaner::forget(void)
+{
+ _pimpl->original_depth = _pimpl->state_ref.get_top();
+}
diff --git a/stack_cleaner.hpp b/stack_cleaner.hpp
new file mode 100644
index 000000000000..cd3e1468656f
--- /dev/null
+++ b/stack_cleaner.hpp
@@ -0,0 +1,93 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file stack_cleaner.hpp
+/// Provides the stack_cleaner class.
+
+#if !defined(LUTOK_STACK_CLEANER_HPP)
+#define LUTOK_STACK_CLEANER_HPP
+
+#include <memory>
+
+#include <lutok/state.hpp>
+
+namespace lutok {
+
+
+/// A RAII model for values on the Lua stack.
+///
+/// At creation time, the object records the current depth of the Lua stack and,
+/// during destruction, restores the recorded depth by popping as many stack
+/// entries as required. As a corollary, the stack can only grow during the
+/// lifetime of a stack_cleaner object (or shrink, but cannot become shorter
+/// than the depth recorded at creation time).
+///
+/// Use this class as follows:
+///
+/// state s;
+/// {
+/// stack_cleaner cleaner1(s);
+/// s.push_integer(3);
+/// s.push_integer(5);
+/// ... do stuff here ...
+/// for (...) {
+/// stack_cleaner cleaner2(s);
+/// s.load_string("...");
+/// s.pcall(0, 1, 0);
+/// ... do stuff here ...
+/// }
+/// // cleaner2 destroyed; the result of pcall is gone.
+/// }
+/// // cleaner1 destroyed; the integers 3 and 5 are gone.
+///
+/// You must give a name to the instantiated objects even if they cannot be
+/// accessed later. Otherwise, the instance will be destroyed right away and
+/// will not have the desired effect.
+class stack_cleaner {
+ struct impl;
+
+ /// Pointer to the shared internal implementation.
+ std::auto_ptr< impl > _pimpl;
+
+ /// Disallow copies.
+ stack_cleaner(const stack_cleaner&);
+
+ /// Disallow assignment.
+ stack_cleaner& operator=(const stack_cleaner&);
+
+public:
+ stack_cleaner(state&);
+ ~stack_cleaner(void);
+
+ void forget(void);
+};
+
+
+} // namespace lutok
+
+#endif // !defined(LUTOK_STACK_CLEANER_HPP)
diff --git a/stack_cleaner_test.cpp b/stack_cleaner_test.cpp
new file mode 100644
index 000000000000..2aee28baa361
--- /dev/null
+++ b/stack_cleaner_test.cpp
@@ -0,0 +1,108 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "stack_cleaner.hpp"
+
+#include <atf-c++.hpp>
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(empty);
+ATF_TEST_CASE_BODY(empty)
+{
+ lutok::state state;
+ {
+ lutok::stack_cleaner cleaner(state);
+ ATF_REQUIRE_EQ(0, state.get_top());
+ }
+ ATF_REQUIRE_EQ(0, state.get_top());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(some);
+ATF_TEST_CASE_BODY(some)
+{
+ lutok::state state;
+ {
+ lutok::stack_cleaner cleaner(state);
+ state.push_integer(15);
+ ATF_REQUIRE_EQ(1, state.get_top());
+ state.push_integer(30);
+ ATF_REQUIRE_EQ(2, state.get_top());
+ }
+ ATF_REQUIRE_EQ(0, state.get_top());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(nested);
+ATF_TEST_CASE_BODY(nested)
+{
+ lutok::state state;
+ {
+ lutok::stack_cleaner cleaner1(state);
+ state.push_integer(10);
+ ATF_REQUIRE_EQ(1, state.get_top());
+ ATF_REQUIRE_EQ(10, state.to_integer(-1));
+ {
+ lutok::stack_cleaner cleaner2(state);
+ state.push_integer(20);
+ ATF_REQUIRE_EQ(2, state.get_top());
+ ATF_REQUIRE_EQ(20, state.to_integer(-1));
+ ATF_REQUIRE_EQ(10, state.to_integer(-2));
+ }
+ ATF_REQUIRE_EQ(1, state.get_top());
+ ATF_REQUIRE_EQ(10, state.to_integer(-1));
+ }
+ ATF_REQUIRE_EQ(0, state.get_top());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(forget);
+ATF_TEST_CASE_BODY(forget)
+{
+ lutok::state state;
+ {
+ lutok::stack_cleaner cleaner(state);
+ state.push_integer(15);
+ state.push_integer(30);
+ cleaner.forget();
+ state.push_integer(60);
+ ATF_REQUIRE_EQ(3, state.get_top());
+ }
+ ATF_REQUIRE_EQ(2, state.get_top());
+ ATF_REQUIRE_EQ(30, state.to_integer(-1));
+ state.pop(2);
+}
+
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, empty);
+ ATF_ADD_TEST_CASE(tcs, some);
+ ATF_ADD_TEST_CASE(tcs, nested);
+ ATF_ADD_TEST_CASE(tcs, forget);
+}
diff --git a/state.cpp b/state.cpp
new file mode 100644
index 000000000000..3c140388e0cb
--- /dev/null
+++ b/state.cpp
@@ -0,0 +1,904 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+extern "C" {
+#include <unistd.h>
+}
+
+#include <cassert>
+#include <cstring>
+
+#include "c_gate.hpp"
+#include "exceptions.hpp"
+#include "state.ipp"
+
+
+namespace {
+
+
+/// Wrapper around lua_getglobal to run in a protected environment.
+///
+/// \pre stack(-1) is the name of the global to get.
+/// \post stack(-1) is the value of the global.
+///
+/// \param state The Lua C API state.
+///
+/// \return The number of return values pushed onto the stack.
+static int
+protected_getglobal(lua_State* state)
+{
+ lua_getglobal(state, lua_tostring(state, -1));
+ return 1;
+}
+
+
+/// Wrapper around lua_gettable to run in a protected environment.
+///
+/// \pre stack(-2) is the table to get the element from.
+/// \pre stack(-1) is the table index.
+/// \post stack(-1) is the value of stack(-2)[stack(-1)].
+///
+/// \param state The Lua C API state.
+///
+/// \return The number of return values pushed onto the stack.
+static int
+protected_gettable(lua_State* state)
+{
+ lua_gettable(state, -2);
+ return 1;
+}
+
+
+/// Wrapper around lua_next to run in a protected environment.
+///
+/// \pre stack(-2) is the table to get the next element from.
+/// \pre stack(-1) is the last processed key.
+/// \post stack(-1) is the value of next(stack(-2), stack(-1)).
+///
+/// \param state The Lua C API state.
+///
+/// \return The number of return values pushed onto the stack.
+static int
+protected_next(lua_State* state)
+{
+ const int more = lua_next(state, -2) != 0;
+ lua_pushboolean(state, more);
+ return more ? 3 : 1;
+}
+
+
+/// Wrapper around lua_setglobal to run in a protected environment.
+///
+/// \pre stack(-2) is the name of the global to set.
+/// \pre stack(-1) is the value to set the global to.
+///
+/// \param state The Lua C API state.
+///
+/// \return The number of return values pushed onto the stack.
+static int
+protected_setglobal(lua_State* state)
+{
+ lua_setglobal(state, lua_tostring(state, -2));
+ return 0;
+}
+
+
+/// Wrapper around lua_settable to run in a protected environment.
+///
+/// \pre stack(-3) is the table to set the element into.
+/// \pre stack(-2) is the table index.
+/// \pre stack(-1) is the value to set.
+///
+/// \param state The Lua C API state.
+///
+/// \return The number of return values pushed onto the stack.
+static int
+protected_settable(lua_State* state)
+{
+ lua_settable(state, -3);
+ return 0;
+}
+
+
+/// Calls a C++ Lua function from a C calling environment.
+///
+/// Any errors reported by the C++ function are caught and reported to the
+/// caller as Lua errors.
+///
+/// \param function The C++ function to call.
+/// \param raw_state The raw Lua state.
+///
+/// \return The number of return values pushed onto the Lua stack by the
+/// function.
+static int
+call_cxx_function_from_c(lutok::cxx_function function,
+ lua_State* raw_state) throw()
+{
+ char error_buf[1024];
+
+ try {
+ lutok::state state = lutok::state_c_gate::connect(raw_state);
+ return function(state);
+ } catch (const std::exception& e) {
+ std::strncpy(error_buf, e.what(), sizeof(error_buf));
+ } catch (...) {
+ std::strncpy(error_buf, "Unhandled exception in Lua C++ hook",
+ sizeof(error_buf));
+ }
+ error_buf[sizeof(error_buf) - 1] = '\0';
+ // We raise the Lua error from outside the try/catch context and we use
+ // a stack-based buffer to hold the message to ensure that we do not leak
+ // any C++ objects (and, as a likely result, memory) when Lua performs its
+ // longjmp.
+ return luaL_error(raw_state, "%s", error_buf);
+}
+
+
+/// Lua glue to call a C++ closure.
+///
+/// This Lua binding is actually a closure that we have constructed from the
+/// state.push_cxx_closure() method. The closure contains the same upvalues
+/// provided by the user plus an extra upvalue that contains the address of the
+/// C++ function we have to call. All we do here is safely delegate the
+/// execution to the wrapped C++ closure.
+///
+/// \param raw_state The Lua C API state.
+///
+/// \return The number of return values of the called closure.
+static int
+cxx_closure_trampoline(lua_State* raw_state)
+{
+ lutok::state state = lutok::state_c_gate::connect(raw_state);
+
+ int nupvalues;
+ {
+ lua_Debug debug;
+ lua_getstack(raw_state, 0, &debug);
+ lua_getinfo(raw_state, "u", &debug);
+ nupvalues = debug.nups;
+ }
+
+ lutok::cxx_function* function = state.to_userdata< lutok::cxx_function >(
+ state.upvalue_index(nupvalues));
+ return call_cxx_function_from_c(*function, raw_state);
+}
+
+
+/// Lua glue to call a C++ function.
+///
+/// This Lua binding is actually a closure that we have constructed from the
+/// state.push_cxx_function() method. The closure has a single upvalue that
+/// contains the address of the C++ function we have to call. All we do here is
+/// safely delegate the execution to the wrapped C++ function.
+///
+/// \param raw_state The Lua C API state.
+///
+/// \return The number of return values of the called function.
+static int
+cxx_function_trampoline(lua_State* raw_state)
+{
+ lutok::state state = lutok::state_c_gate::connect(raw_state);
+ lutok::cxx_function* function = state.to_userdata< lutok::cxx_function >(
+ state.upvalue_index(1));
+ return call_cxx_function_from_c(*function, raw_state);
+}
+
+
+} // anonymous namespace
+
+
+const int lutok::registry_index = LUA_REGISTRYINDEX;
+
+
+/// Internal implementation for lutok::state.
+struct lutok::state::impl {
+ /// The Lua internal state.
+ lua_State* lua_state;
+
+ /// Whether we own the state or not (to decide if we close it).
+ bool owned;
+
+ /// Constructor.
+ ///
+ /// \param lua_ The Lua internal state.
+ /// \param owned_ Whether we own the state or not.
+ impl(lua_State* lua_, bool owned_) :
+ lua_state(lua_),
+ owned(owned_)
+ {
+ }
+};
+
+
+/// Initializes the Lua state.
+///
+/// You must share the same state object alongside the lifetime of your Lua
+/// session. As soon as the object is destroyed, the session is terminated.
+lutok::state::state(void)
+{
+ lua_State* lua = luaL_newstate();
+ if (lua == NULL)
+ throw lutok::error("lua open failed");
+ _pimpl.reset(new impl(lua, true));
+}
+
+
+/// Initializes the Lua state from an existing raw state.
+///
+/// Instances constructed using this method do NOT own the raw state. This
+/// means that, on exit, the state will not be destroyed.
+///
+/// \param raw_state_ The raw Lua state to wrap.
+lutok::state::state(void* raw_state_) :
+ _pimpl(new impl(reinterpret_cast< lua_State* >(raw_state_), false))
+{
+}
+
+
+/// Destructor for the Lua state.
+///
+/// Closes the session unless it has already been closed by calling the
+/// close() method. It is recommended to explicitly close the session in the
+/// code.
+lutok::state::~state(void)
+{
+ if (_pimpl->owned && _pimpl->lua_state != NULL)
+ close();
+}
+
+
+/// Terminates this Lua session.
+///
+/// It is recommended to call this instead of relying on the destructor to do
+/// the cleanup, but it is not a requirement to use close().
+///
+/// \pre close() has not yet been called.
+/// \pre The Lua stack is empty. This is not truly necessary but ensures that
+/// our code is consistent and clears the stack explicitly.
+void
+lutok::state::close(void)
+{
+ assert(_pimpl->lua_state != NULL);
+ assert(lua_gettop(_pimpl->lua_state) == 0);
+ lua_close(_pimpl->lua_state);
+ _pimpl->lua_state = NULL;
+}
+
+
+/// Wrapper around lua_getglobal.
+///
+/// \param name The second parameter to lua_getglobal.
+///
+/// \throw api_error If lua_getglobal fails.
+///
+/// \warning Terminates execution if there is not enough memory to manipulate
+/// the Lua stack.
+void
+lutok::state::get_global(const std::string& name)
+{
+ lua_pushcfunction(_pimpl->lua_state, protected_getglobal);
+ lua_pushstring(_pimpl->lua_state, name.c_str());
+ if (lua_pcall(_pimpl->lua_state, 1, 1, 0) != 0)
+ throw lutok::api_error::from_stack(*this, "lua_getglobal");
+}
+
+
+/// Pushes a reference to the global table onto the stack.
+///
+/// This is a wrapper around the incompatible differences between Lua 5.1 and
+/// 5.2 to access to the globals table.
+///
+/// \post state(-1) Contains the reference to the globals table.
+void
+lutok::state::get_global_table(void)
+{
+#if LUA_VERSION_NUM >= 502
+ lua_pushvalue(_pimpl->lua_state, registry_index);
+ lua_pushinteger(_pimpl->lua_state, LUA_RIDX_GLOBALS);
+ lua_gettable(_pimpl->lua_state, -2);
+ lua_remove(_pimpl->lua_state, -2);
+#else
+ lua_pushvalue(_pimpl->lua_state, LUA_GLOBALSINDEX);
+#endif
+}
+
+
+/// Wrapper around luaL_getmetafield.
+///
+/// \param index The second parameter to luaL_getmetafield.
+/// \param name The third parameter to luaL_getmetafield.
+///
+/// \return The return value of luaL_getmetafield.
+///
+/// \warning Terminates execution if there is not enough memory to manipulate
+/// the Lua stack.
+bool
+lutok::state::get_metafield(const int index, const std::string& name)
+{
+ return luaL_getmetafield(_pimpl->lua_state, index, name.c_str()) != 0;
+}
+
+
+/// Wrapper around lua_getmetatable.
+///
+/// \param index The second parameter to lua_getmetatable.
+///
+/// \return The return value of lua_getmetatable.
+bool
+lutok::state::get_metatable(const int index)
+{
+ return lua_getmetatable(_pimpl->lua_state, index) != 0;
+}
+
+
+/// Wrapper around lua_gettable.
+///
+/// \param index The second parameter to lua_gettable.
+///
+/// \throw api_error If lua_gettable fails.
+///
+/// \warning Terminates execution if there is not enough memory to manipulate
+/// the Lua stack.
+void
+lutok::state::get_table(const int index)
+{
+ assert(lua_gettop(_pimpl->lua_state) >= 2);
+ lua_pushcfunction(_pimpl->lua_state, protected_gettable);
+ lua_pushvalue(_pimpl->lua_state, index < 0 ? index - 1 : index);
+ lua_pushvalue(_pimpl->lua_state, -3);
+ if (lua_pcall(_pimpl->lua_state, 2, 1, 0) != 0)
+ throw lutok::api_error::from_stack(*this, "lua_gettable");
+ lua_remove(_pimpl->lua_state, -2);
+}
+
+
+/// Wrapper around lua_gettop.
+///
+/// \return The return value of lua_gettop.
+int
+lutok::state::get_top(void)
+{
+ return lua_gettop(_pimpl->lua_state);
+}
+
+
+/// Wrapper around lua_insert.
+///
+/// \param index The second parameter to lua_insert.
+void
+lutok::state::insert(const int index)
+{
+ lua_insert(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_isboolean.
+///
+/// \param index The second parameter to lua_isboolean.
+///
+/// \return The return value of lua_isboolean.
+bool
+lutok::state::is_boolean(const int index)
+{
+ return lua_isboolean(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_isfunction.
+///
+/// \param index The second parameter to lua_isfunction.
+///
+/// \return The return value of lua_isfunction.
+bool
+lutok::state::is_function(const int index)
+{
+ return lua_isfunction(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_isnil.
+///
+/// \param index The second parameter to lua_isnil.
+///
+/// \return The return value of lua_isnil.
+bool
+lutok::state::is_nil(const int index)
+{
+ return lua_isnil(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_isnumber.
+///
+/// \param index The second parameter to lua_isnumber.
+///
+/// \return The return value of lua_isnumber.
+bool
+lutok::state::is_number(const int index)
+{
+ return lua_isnumber(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_isstring.
+///
+/// \param index The second parameter to lua_isstring.
+///
+/// \return The return value of lua_isstring.
+bool
+lutok::state::is_string(const int index)
+{
+ return lua_isstring(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_istable.
+///
+/// \param index The second parameter to lua_istable.
+///
+/// \return The return value of lua_istable.
+bool
+lutok::state::is_table(const int index)
+{
+ return lua_istable(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_isuserdata.
+///
+/// \param index The second parameter to lua_isuserdata.
+///
+/// \return The return value of lua_isuserdata.
+bool
+lutok::state::is_userdata(const int index)
+{
+ return lua_isuserdata(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around luaL_loadfile.
+///
+/// \param file The second parameter to luaL_loadfile.
+///
+/// \throw api_error If luaL_loadfile returns an error.
+/// \throw file_not_found_error If the file cannot be accessed.
+///
+/// \warning Terminates execution if there is not enough memory.
+void
+lutok::state::load_file(const std::string& file)
+{
+ if (::access(file.c_str(), R_OK) == -1)
+ throw lutok::file_not_found_error(file);
+ if (luaL_loadfile(_pimpl->lua_state, file.c_str()) != 0)
+ throw lutok::api_error::from_stack(*this, "luaL_loadfile");
+}
+
+
+/// Wrapper around luaL_loadstring.
+///
+/// \param str The second parameter to luaL_loadstring.
+///
+/// \throw api_error If luaL_loadstring returns an error.
+///
+/// \warning Terminates execution if there is not enough memory.
+void
+lutok::state::load_string(const std::string& str)
+{
+ if (luaL_loadstring(_pimpl->lua_state, str.c_str()) != 0)
+ throw lutok::api_error::from_stack(*this, "luaL_loadstring");
+}
+
+
+/// Wrapper around lua_newtable.
+///
+/// \warning Terminates execution if there is not enough memory.
+void
+lutok::state::new_table(void)
+{
+ lua_newtable(_pimpl->lua_state);
+}
+
+
+/// Wrapper around lua_newuserdata.
+///
+/// This is internal. The public type-safe interface of this method should be
+/// used instead.
+///
+/// \param size The second parameter to lua_newuserdata.
+///
+/// \return The return value of lua_newuserdata.
+///
+/// \warning Terminates execution if there is not enough memory.
+void*
+lutok::state::new_userdata_voidp(const size_t size)
+{
+ return lua_newuserdata(_pimpl->lua_state, size);
+}
+
+
+/// Wrapper around lua_next.
+///
+/// \param index The second parameter to lua_next.
+///
+/// \return True if there are more elements to process; false otherwise.
+///
+/// \warning Terminates execution if there is not enough memory.
+bool
+lutok::state::next(const int index)
+{
+ assert(lua_istable(_pimpl->lua_state, index));
+ assert(lua_gettop(_pimpl->lua_state) >= 1);
+ lua_pushcfunction(_pimpl->lua_state, protected_next);
+ lua_pushvalue(_pimpl->lua_state, index < 0 ? index - 1 : index);
+ lua_pushvalue(_pimpl->lua_state, -3);
+ if (lua_pcall(_pimpl->lua_state, 2, LUA_MULTRET, 0) != 0)
+ throw lutok::api_error::from_stack(*this, "lua_next");
+ const bool more = lua_toboolean(_pimpl->lua_state, -1);
+ lua_pop(_pimpl->lua_state, 1);
+ if (more)
+ lua_remove(_pimpl->lua_state, -3);
+ else
+ lua_pop(_pimpl->lua_state, 1);
+ return more;
+}
+
+
+/// Wrapper around luaL_openlibs.
+///
+/// \throw api_error If luaL_openlibs fails.
+///
+/// \warning Terminates execution if there is not enough memory.
+void
+lutok::state::open_all(void)
+{
+ luaL_openlibs(_pimpl->lua_state);
+}
+
+
+/// Wrapper around luaopen_base.
+///
+/// \throw api_error If luaopen_base fails.
+///
+/// \warning Terminates execution if there is not enough memory.
+void
+lutok::state::open_base(void)
+{
+ lua_pushcfunction(_pimpl->lua_state, luaopen_base);
+ if (lua_pcall(_pimpl->lua_state, 0, 0, 0) != 0)
+ throw lutok::api_error::from_stack(*this, "luaopen_base");
+}
+
+
+/// Wrapper around luaopen_string.
+///
+/// \throw api_error If luaopen_string fails.
+///
+/// \warning Terminates execution if there is not enough memory.
+void
+lutok::state::open_string(void)
+{
+#if LUA_VERSION_NUM >= 502
+ luaL_requiref(_pimpl->lua_state, LUA_STRLIBNAME, luaopen_string, 1);
+ lua_pop(_pimpl->lua_state, 1);
+#else
+ lua_pushcfunction(_pimpl->lua_state, luaopen_string);
+ if (lua_pcall(_pimpl->lua_state, 0, 0, 0) != 0)
+ throw lutok::api_error::from_stack(*this, "luaopen_string");
+#endif
+}
+
+
+/// Wrapper around luaopen_table.
+///
+/// \throw api_error If luaopen_table fails.
+///
+/// \warning Terminates execution if there is not enough memory.
+void
+lutok::state::open_table(void)
+{
+#if LUA_VERSION_NUM >= 502
+ luaL_requiref(_pimpl->lua_state, LUA_TABLIBNAME, luaopen_table, 1);
+ lua_pop(_pimpl->lua_state, 1);
+#else
+ lua_pushcfunction(_pimpl->lua_state, luaopen_table);
+ if (lua_pcall(_pimpl->lua_state, 0, 0, 0) != 0)
+ throw lutok::api_error::from_stack(*this, "luaopen_table");
+#endif
+}
+
+
+/// Wrapper around lua_pcall.
+///
+/// \param nargs The second parameter to lua_pcall.
+/// \param nresults The third parameter to lua_pcall.
+/// \param errfunc The fourth parameter to lua_pcall.
+///
+/// \throw api_error If lua_pcall returns an error.
+void
+lutok::state::pcall(const int nargs, const int nresults, const int errfunc)
+{
+ if (lua_pcall(_pimpl->lua_state, nargs, nresults, errfunc) != 0)
+ throw lutok::api_error::from_stack(*this, "lua_pcall");
+}
+
+
+/// Wrapper around lua_pop.
+///
+/// \param count The second parameter to lua_pop.
+void
+lutok::state::pop(const int count)
+{
+ assert(count <= lua_gettop(_pimpl->lua_state));
+ lua_pop(_pimpl->lua_state, count);
+ assert(lua_gettop(_pimpl->lua_state) >= 0);
+}
+
+
+/// Wrapper around lua_pushboolean.
+///
+/// \param value The second parameter to lua_pushboolean.
+void
+lutok::state::push_boolean(const bool value)
+{
+ lua_pushboolean(_pimpl->lua_state, value ? 1 : 0);
+}
+
+
+/// Wrapper around lua_pushcclosure.
+///
+/// This is not a pure wrapper around lua_pushcclosure because this has to do
+/// extra magic to allow passing C++ functions instead of plain C functions.
+///
+/// \param function The C++ function to be pushed as a closure.
+/// \param nvalues The number of upvalues that the function receives.
+void
+lutok::state::push_cxx_closure(cxx_function function, const int nvalues)
+{
+ cxx_function *data = static_cast< cxx_function* >(
+ lua_newuserdata(_pimpl->lua_state, sizeof(cxx_function)));
+ *data = function;
+ lua_pushcclosure(_pimpl->lua_state, cxx_closure_trampoline, nvalues + 1);
+}
+
+
+/// Wrapper around lua_pushcfunction.
+///
+/// This is not a pure wrapper around lua_pushcfunction because this has to do
+/// extra magic to allow passing C++ functions instead of plain C functions.
+///
+/// \param function The C++ function to be pushed.
+void
+lutok::state::push_cxx_function(cxx_function function)
+{
+ cxx_function *data = static_cast< cxx_function* >(
+ lua_newuserdata(_pimpl->lua_state, sizeof(cxx_function)));
+ *data = function;
+ lua_pushcclosure(_pimpl->lua_state, cxx_function_trampoline, 1);
+}
+
+
+/// Wrapper around lua_pushinteger.
+///
+/// \param value The second parameter to lua_pushinteger.
+void
+lutok::state::push_integer(const int value)
+{
+ lua_pushinteger(_pimpl->lua_state, value);
+}
+
+
+/// Wrapper around lua_pushnil.
+void
+lutok::state::push_nil(void)
+{
+ lua_pushnil(_pimpl->lua_state);
+}
+
+
+/// Wrapper around lua_pushstring.
+///
+/// \param str The second parameter to lua_pushstring.
+///
+/// \warning Terminates execution if there is not enough memory.
+void
+lutok::state::push_string(const std::string& str)
+{
+ lua_pushstring(_pimpl->lua_state, str.c_str());
+}
+
+
+/// Wrapper around lua_pushvalue.
+///
+/// \param index The second parameter to lua_pushvalue.
+void
+lutok::state::push_value(const int index)
+{
+ lua_pushvalue(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_rawget.
+///
+/// \param index The second parameter to lua_rawget.
+void
+lutok::state::raw_get(const int index)
+{
+ lua_rawget(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_rawset.
+///
+/// \param index The second parameter to lua_rawset.
+///
+/// \warning Terminates execution if there is not enough memory to manipulate
+/// the Lua stack.
+void
+lutok::state::raw_set(const int index)
+{
+ lua_rawset(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_setglobal.
+///
+/// \param name The second parameter to lua_setglobal.
+///
+/// \throw api_error If lua_setglobal fails.
+///
+/// \warning Terminates execution if there is not enough memory to manipulate
+/// the Lua stack.
+void
+lutok::state::set_global(const std::string& name)
+{
+ lua_pushcfunction(_pimpl->lua_state, protected_setglobal);
+ lua_pushstring(_pimpl->lua_state, name.c_str());
+ lua_pushvalue(_pimpl->lua_state, -3);
+ if (lua_pcall(_pimpl->lua_state, 2, 0, 0) != 0)
+ throw lutok::api_error::from_stack(*this, "lua_setglobal");
+ lua_pop(_pimpl->lua_state, 1);
+}
+
+
+/// Wrapper around lua_setmetatable.
+///
+/// \param index The second parameter to lua_setmetatable.
+void
+lutok::state::set_metatable(const int index)
+{
+ lua_setmetatable(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_settable.
+///
+/// \param index The second parameter to lua_settable.
+///
+/// \throw api_error If lua_settable fails.
+///
+/// \warning Terminates execution if there is not enough memory to manipulate
+/// the Lua stack.
+void
+lutok::state::set_table(const int index)
+{
+ lua_pushcfunction(_pimpl->lua_state, protected_settable);
+ lua_pushvalue(_pimpl->lua_state, index < 0 ? index - 1 : index);
+ lua_pushvalue(_pimpl->lua_state, -4);
+ lua_pushvalue(_pimpl->lua_state, -4);
+ if (lua_pcall(_pimpl->lua_state, 3, 0, 0) != 0)
+ throw lutok::api_error::from_stack(*this, "lua_settable");
+ lua_pop(_pimpl->lua_state, 2);
+}
+
+
+/// Wrapper around lua_toboolean.
+///
+/// \param index The second parameter to lua_toboolean.
+///
+/// \return The return value of lua_toboolean.
+bool
+lutok::state::to_boolean(const int index)
+{
+ assert(is_boolean(index));
+ return lua_toboolean(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_tointeger.
+///
+/// \param index The second parameter to lua_tointeger.
+///
+/// \return The return value of lua_tointeger.
+long
+lutok::state::to_integer(const int index)
+{
+ assert(is_number(index));
+ return lua_tointeger(_pimpl->lua_state, index);
+}
+
+
+/// Wrapper around lua_touserdata.
+///
+/// This is internal. The public type-safe interface of this method should be
+/// used instead.
+///
+/// \param index The second parameter to lua_touserdata.
+///
+/// \return The return value of lua_touserdata.
+///
+/// \warning Terminates execution if there is not enough memory.
+void*
+lutok::state::to_userdata_voidp(const int index)
+{
+ return lua_touserdata(_pimpl->lua_state, index);
+}
+
+
+
+/// Wrapper around lua_tostring.
+///
+/// \param index The second parameter to lua_tostring.
+///
+/// \return The return value of lua_tostring.
+///
+/// \warning Terminates execution if there is not enough memory.
+std::string
+lutok::state::to_string(const int index)
+{
+ assert(is_string(index));
+ const char *raw_string = lua_tostring(_pimpl->lua_state, index);
+ // Note that the creation of a string object below (explicit for clarity)
+ // implies that the raw string is duplicated and, henceforth, the string is
+ // safe even if the corresponding element is popped from the Lua stack.
+ return std::string(raw_string);
+}
+
+
+/// Wrapper around lua_upvalueindex.
+///
+/// \param index The first parameter to lua_upvalueindex.
+///
+/// \return The return value of lua_upvalueindex.
+int
+lutok::state::upvalue_index(const int index)
+{
+ return lua_upvalueindex(index);
+}
+
+
+/// Gets the internal lua_State object.
+///
+/// \return The raw Lua state. This is returned as a void pointer to prevent
+/// including the lua.hpp header file from our public interface. The only way
+/// to call this method is by using the c_gate module, and c_gate takes care of
+/// casting this object to the appropriate type.
+void*
+lutok::state::raw_state(void)
+{
+ return _pimpl->lua_state;
+}
diff --git a/state.hpp b/state.hpp
new file mode 100644
index 000000000000..377aa062d81c
--- /dev/null
+++ b/state.hpp
@@ -0,0 +1,145 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file state.hpp
+/// Provides the state wrapper class for the Lua C state.
+
+#if !defined(LUTOK_STATE_HPP)
+#define LUTOK_STATE_HPP
+
+#include <string>
+
+#if defined(_LIBCPP_VERSION) || __cplusplus >= 201103L
+#include <memory>
+#else
+#include <tr1/memory>
+#endif
+
+namespace lutok {
+
+
+class debug;
+class state;
+
+
+/// The type of a C++ function that can be bound into Lua.
+///
+/// Functions of this type are free to raise exceptions. These will not
+/// propagate into the Lua C API. However, any such exceptions will be reported
+/// as a Lua error and their type will be lost.
+typedef int (*cxx_function)(state&);
+
+
+/// Stack index constant pointing to the registry table.
+extern const int registry_index;
+
+
+/// A RAII model for the Lua state.
+///
+/// This class holds the state of the Lua interpreter during its existence and
+/// provides wrappers around several Lua library functions that operate on such
+/// state.
+///
+/// These wrapper functions differ from the C versions in that they use the
+/// implicit state hold by the class, they use C++ types where appropriate and
+/// they use exceptions to report errors.
+///
+/// The wrappers intend to be as lightweight as possible but, in some
+/// situations, they are pretty complex because they need to do extra work to
+/// capture the errors reported by the Lua C API. We prefer having fine-grained
+/// error control rather than efficiency, so this is OK.
+class state {
+ struct impl;
+
+ /// Pointer to the shared internal implementation.
+#if defined(_LIBCPP_VERSION) || __cplusplus >= 201103L
+ std::shared_ptr< impl > _pimpl;
+#else
+ std::tr1::shared_ptr< impl > _pimpl;
+#endif
+
+ void* new_userdata_voidp(const size_t);
+ void* to_userdata_voidp(const int);
+
+ friend class state_c_gate;
+ explicit state(void*);
+ void* raw_state(void);
+
+public:
+ state(void);
+ ~state(void);
+
+ void close(void);
+ void get_global(const std::string&);
+ void get_global_table(void);
+ bool get_metafield(const int, const std::string&);
+ bool get_metatable(const int);
+ void get_table(const int);
+ int get_top(void);
+ void insert(const int);
+ bool is_boolean(const int);
+ bool is_function(const int);
+ bool is_nil(const int);
+ bool is_number(const int);
+ bool is_string(const int);
+ bool is_table(const int);
+ bool is_userdata(const int);
+ void load_file(const std::string&);
+ void load_string(const std::string&);
+ void new_table(void);
+ template< typename Type > Type* new_userdata(void);
+ bool next(const int);
+ void open_all(void);
+ void open_base(void);
+ void open_string(void);
+ void open_table(void);
+ void pcall(const int, const int, const int);
+ void pop(const int);
+ void push_boolean(const bool);
+ void push_cxx_closure(cxx_function, const int);
+ void push_cxx_function(cxx_function);
+ void push_integer(const int);
+ void push_nil(void);
+ void push_string(const std::string&);
+ void push_value(const int);
+ void raw_get(const int);
+ void raw_set(const int);
+ void set_global(const std::string&);
+ void set_metatable(const int);
+ void set_table(const int);
+ bool to_boolean(const int);
+ long to_integer(const int);
+ template< typename Type > Type* to_userdata(const int);
+ std::string to_string(const int);
+ int upvalue_index(const int);
+};
+
+
+} // namespace lutok
+
+#endif // !defined(LUTOK_STATE_HPP)
diff --git a/state.ipp b/state.ipp
new file mode 100644
index 000000000000..76f47a643194
--- /dev/null
+++ b/state.ipp
@@ -0,0 +1,67 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#if !defined(LUTOK_STATE_IPP)
+#define LUTOK_STATE_IPP
+
+#include <lutok/state.hpp>
+
+namespace lutok {
+
+
+/// Wrapper around lua_newuserdata.
+///
+/// This allocates an object as big as the size of the provided Type.
+///
+/// \return The pointer to the allocated userdata object.
+///
+/// \warning Terminates execution if there is not enough memory.
+template< typename Type >
+Type*
+state::new_userdata(void)
+{
+ return static_cast< Type* >(new_userdata_voidp(sizeof(Type)));
+}
+
+
+/// Wrapper around lua_touserdata.
+///
+/// \param index The second parameter to lua_touserdata.
+///
+/// \return The return value of lua_touserdata.
+template< typename Type >
+Type*
+state::to_userdata(const int index)
+{
+ return static_cast< Type* >(to_userdata_voidp(index));
+}
+
+
+} // namespace lutok
+
+#endif // !defined(LUTOK_STATE_IPP)
diff --git a/state_test.cpp b/state_test.cpp
new file mode 100644
index 000000000000..40c70a6d4b38
--- /dev/null
+++ b/state_test.cpp
@@ -0,0 +1,1164 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "state.ipp"
+
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <stdexcept>
+
+#include <atf-c++.hpp>
+#include <lua.hpp>
+
+#include "c_gate.hpp"
+#include "exceptions.hpp"
+#include "test_utils.hpp"
+
+
+// A note about the lutok::state tests.
+//
+// The methods of lutok::state are, in general, thin wrappers around the
+// corresponding Lua C API methods. The tests below are simple unit tests that
+// ensure that these functions just delegate the calls to the Lua library. We
+// do not intend to test the validity of the methods themselves (that's the
+// job of the Lua authors). That said, we test those conditions we rely on,
+// such as the reporting of errors and the default values to the API.
+//
+// Lastly, for every test case that stresses a single lutok::state method, we
+// only call that method directly. All other Lua state manipulation operations
+// are performed by means of direct calls to the Lua C API. This is to ensure
+// that the wrapped methods are really talking to Lua.
+
+
+namespace {
+
+
+/// Checks if a symbol is available.
+///
+/// \param state The Lua state.
+/// \param symbol The symbol to check for.
+///
+/// \return True if the symbol is defined, false otherwise.
+static bool
+is_available(lutok::state& state, const char* symbol)
+{
+ luaL_loadstring(raw(state), (std::string("return ") + symbol).c_str());
+ const bool ok = (lua_pcall(raw(state), 0, 1, 0) == 0 &&
+ !lua_isnil(raw(state), -1));
+ lua_pop(raw(state), 1);
+ std::cout << "Symbol " << symbol << (ok ? " found\n" : " not found\n");
+ return ok;
+}
+
+
+/// Checks that no modules are present or that only one has been loaded.
+///
+/// \post The test case terminates if there is any module present when expected
+/// is empty or if there two modules loaded when expected is defined.
+///
+/// \param state The Lua state.
+/// \param expected The module to expect. Empty if no modules are allowed.
+static void
+check_modules(lutok::state& state, const std::string& expected)
+{
+ std::cout << "Checking loaded modules" <<
+ (expected.empty() ? "" : (" (" + expected + " expected)")) << "\n";
+ ATF_REQUIRE(!((expected == "base") ^ (is_available(state, "assert"))));
+ ATF_REQUIRE(!((expected == "string") ^
+ (is_available(state, "string.byte"))));
+ ATF_REQUIRE(!((expected == "table") ^
+ (is_available(state, "table.concat"))));
+}
+
+
+/// A C closure that returns its two integral upvalues.
+///
+/// \post stack(-2) contains the first upvalue.
+/// \post stack(-1) contains the second upvalue.
+///
+/// \param raw_state The raw Lua state.
+///
+/// \return The number of result values, i.e. 2.
+static int
+c_get_upvalues(lua_State* raw_state)
+{
+ lutok::state state = lutok::state_c_gate::connect(raw_state);
+ const int i1 = lua_tointeger(raw_state, state.upvalue_index(1));
+ const int i2 = lua_tointeger(raw_state, state.upvalue_index(2));
+ lua_pushinteger(raw_state, i1);
+ lua_pushinteger(raw_state, i2);
+ return 2;
+}
+
+
+/// A custom C++ multiply function with one of its factors on its closure.
+///
+/// \pre stack(-1) contains the second factor.
+/// \post stack(-1) contains the product of the two input factors.
+///
+/// \param state The Lua state.
+///
+/// \return The number of result values, i.e. 1.
+static int
+cxx_multiply_closure(lutok::state& state)
+{
+ const int f1 = lua_tointeger(raw(state), lua_upvalueindex(1));
+ const int f2 = lua_tointeger(raw(state), -1);
+ lua_pushinteger(raw(state), f1 * f2);
+ return 1;
+}
+
+
+/// A custom C++ integral division function for Lua.
+///
+/// \pre stack(-2) contains the dividend.
+/// \pre stack(-1) contains the divisor.
+/// \post stack(-2) contains the quotient of the division.
+/// \post stack(-1) contains the remainder of the division.
+///
+/// \param state The Lua state.
+///
+/// \return The number of result values, i.e. 1.
+///
+/// \throw std::runtime_error If the divisor is zero.
+/// \throw std::string If the dividend or the divisor are negative. This is an
+/// exception not derived from std::exception on purpose to ensure that the
+/// C++ wrapping correctly captures any exception regardless of its type.
+static int
+cxx_divide(lutok::state& state)
+{
+ const int dividend = state.to_integer(-2);
+ const int divisor = state.to_integer(-1);
+ if (divisor == 0)
+ throw std::runtime_error("Divisor is 0");
+ if (dividend < 0 || divisor < 0)
+ throw std::string("Cannot divide negative numbers");
+ state.push_integer(dividend / divisor);
+ state.push_integer(dividend % divisor);
+ return 2;
+}
+
+
+/// A Lua function that raises a very long error message.
+///
+/// \pre stack(-1) contains the length of the message to construct.
+///
+/// \param state The Lua state.
+///
+/// \return Never returns.
+///
+/// \throw std::runtime_error Unconditionally, with an error message formed by
+/// the repetition of 'A' as many times as requested.
+static int
+raise_long_error(lutok::state& state)
+{
+ const int length = state.to_integer(-1);
+ throw std::runtime_error(std::string(length, 'A').c_str());
+}
+
+
+} // anonymous namespace
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(close);
+ATF_TEST_CASE_BODY(close)
+{
+ lutok::state state;
+ state.close();
+ // The destructor for state will run now. If it does a second close, we may
+ // crash, so let's see if we don't.
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_global__ok);
+ATF_TEST_CASE_BODY(get_global__ok)
+{
+ lutok::state state;
+ ATF_REQUIRE(luaL_dostring(raw(state), "test_variable = 3") == 0);
+ state.get_global("test_variable");
+ ATF_REQUIRE(lua_isnumber(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_global__undefined);
+ATF_TEST_CASE_BODY(get_global__undefined)
+{
+ lutok::state state;
+ state.get_global("test_variable");
+ ATF_REQUIRE(lua_isnil(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_global_table);
+ATF_TEST_CASE_BODY(get_global_table)
+{
+ lutok::state state;
+ ATF_REQUIRE(luaL_dostring(raw(state), "global_variable = 'hello'") == 0);
+ state.get_global_table();
+ lua_pushstring(raw(state), "global_variable");
+ lua_gettable(raw(state), -2);
+ ATF_REQUIRE(lua_isstring(raw(state), -1));
+ ATF_REQUIRE(std::strcmp("hello", lua_tostring(raw(state), -1)) == 0);
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_metafield__ok);
+ATF_TEST_CASE_BODY(get_metafield__ok)
+{
+ lutok::state state;
+ luaL_openlibs(raw(state));
+ ATF_REQUIRE(luaL_dostring(raw(state), "meta = { foo = 567 }; "
+ "t = {}; setmetatable(t, meta)") == 0);
+ lua_getglobal(raw(state), "t");
+ ATF_REQUIRE(state.get_metafield(-1, "foo"));
+ ATF_REQUIRE(lua_isnumber(raw(state), -1));
+ ATF_REQUIRE_EQ(567, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_metafield__undefined);
+ATF_TEST_CASE_BODY(get_metafield__undefined)
+{
+ lutok::state state;
+ luaL_openlibs(raw(state));
+ ATF_REQUIRE(luaL_dostring(raw(state), "meta = { foo = 567 }; "
+ "t = {}; setmetatable(t, meta)") == 0);
+ lua_getglobal(raw(state), "t");
+ ATF_REQUIRE(!state.get_metafield(-1, "bar"));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_metatable__ok);
+ATF_TEST_CASE_BODY(get_metatable__ok)
+{
+ lutok::state state;
+ luaL_openlibs(raw(state));
+ ATF_REQUIRE(luaL_dostring(raw(state), "meta = { foo = 567 }; "
+ "t = {}; setmetatable(t, meta)") == 0);
+ lua_getglobal(raw(state), "t");
+ lua_pushinteger(raw(state), 5555);
+ ATF_REQUIRE(state.get_metatable(-2));
+ ATF_REQUIRE(lua_istable(raw(state), -1));
+ lua_pushstring(raw(state), "foo");
+ lua_gettable(raw(state), -2);
+ ATF_REQUIRE(lua_isnumber(raw(state), -1));
+ ATF_REQUIRE_EQ(567, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 4);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_metatable__undefined);
+ATF_TEST_CASE_BODY(get_metatable__undefined)
+{
+ lutok::state state;
+ ATF_REQUIRE(luaL_dostring(raw(state), "t = {}") == 0);
+ lua_getglobal(raw(state), "t");
+ ATF_REQUIRE(!state.get_metatable(-1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_table__ok);
+ATF_TEST_CASE_BODY(get_table__ok)
+{
+ lutok::state state;
+ ATF_REQUIRE(luaL_dostring(raw(state), "t = { a = 1, bar = 234 }") == 0);
+ lua_getglobal(raw(state), "t");
+ lua_pushstring(raw(state), "bar");
+ state.get_table(-2);
+ ATF_REQUIRE(lua_isnumber(raw(state), -1));
+ ATF_REQUIRE_EQ(234, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_table__nil);
+ATF_TEST_CASE_BODY(get_table__nil)
+{
+ lutok::state state;
+ lua_pushnil(raw(state));
+ lua_pushinteger(raw(state), 1);
+ REQUIRE_API_ERROR("lua_gettable", state.get_table(-2));
+ ATF_REQUIRE_EQ(2, lua_gettop(raw(state)));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_table__unknown_index);
+ATF_TEST_CASE_BODY(get_table__unknown_index)
+{
+ lutok::state state;
+ ATF_REQUIRE(luaL_dostring(raw(state),
+ "the_table = { foo = 1, bar = 2 }") == 0);
+ lua_getglobal(raw(state), "the_table");
+ lua_pushstring(raw(state), "baz");
+ state.get_table(-2);
+ ATF_REQUIRE(lua_isnil(raw(state), -1));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(get_top);
+ATF_TEST_CASE_BODY(get_top)
+{
+ lutok::state state;
+ ATF_REQUIRE_EQ(0, state.get_top());
+ lua_pushinteger(raw(state), 3);
+ ATF_REQUIRE_EQ(1, state.get_top());
+ lua_pushinteger(raw(state), 3);
+ ATF_REQUIRE_EQ(2, state.get_top());
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(insert);
+ATF_TEST_CASE_BODY(insert)
+{
+ lutok::state state;
+ lua_pushinteger(raw(state), 1);
+ lua_pushinteger(raw(state), 2);
+ lua_pushinteger(raw(state), 3);
+ lua_pushinteger(raw(state), 4);
+ state.insert(-3);
+ ATF_REQUIRE_EQ(3, lua_tointeger(raw(state), -1));
+ ATF_REQUIRE_EQ(2, lua_tointeger(raw(state), -2));
+ ATF_REQUIRE_EQ(4, lua_tointeger(raw(state), -3));
+ ATF_REQUIRE_EQ(1, lua_tointeger(raw(state), -4));
+ lua_pop(raw(state), 4);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_boolean__empty);
+ATF_TEST_CASE_BODY(is_boolean__empty)
+{
+ lutok::state state;
+ ATF_REQUIRE(!state.is_boolean(-1));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_boolean__ok);
+ATF_TEST_CASE_BODY(is_boolean__ok)
+{
+ lutok::state state;
+ lua_pushboolean(raw(state), 1);
+ ATF_REQUIRE(state.is_boolean(-1));
+ lua_pushinteger(raw(state), 5);
+ ATF_REQUIRE(!state.is_boolean(-1));
+ ATF_REQUIRE(state.is_boolean(-2));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_function__empty);
+ATF_TEST_CASE_BODY(is_function__empty)
+{
+ lutok::state state;
+ ATF_REQUIRE(!state.is_function(-1));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_function__ok);
+ATF_TEST_CASE_BODY(is_function__ok)
+{
+ lutok::state state;
+ luaL_dostring(raw(state), "function my_func(a, b) return a + b; end");
+
+ lua_getglobal(raw(state), "my_func");
+ ATF_REQUIRE(state.is_function(-1));
+ lua_pushinteger(raw(state), 5);
+ ATF_REQUIRE(!state.is_function(-1));
+ ATF_REQUIRE(state.is_function(-2));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_nil__empty);
+ATF_TEST_CASE_BODY(is_nil__empty)
+{
+ lutok::state state;
+ ATF_REQUIRE(state.is_nil(-1));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_nil__ok);
+ATF_TEST_CASE_BODY(is_nil__ok)
+{
+ lutok::state state;
+ lua_pushnil(raw(state));
+ ATF_REQUIRE(state.is_nil(-1));
+ lua_pushinteger(raw(state), 5);
+ ATF_REQUIRE(!state.is_nil(-1));
+ ATF_REQUIRE(state.is_nil(-2));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_number__empty);
+ATF_TEST_CASE_BODY(is_number__empty)
+{
+ lutok::state state;
+ ATF_REQUIRE(!state.is_number(-1));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_number__ok);
+ATF_TEST_CASE_BODY(is_number__ok)
+{
+ lutok::state state;
+ lua_pushnil(raw(state));
+ ATF_REQUIRE(!state.is_number(-1));
+ lua_pushinteger(raw(state), 5);
+ ATF_REQUIRE(state.is_number(-1));
+ ATF_REQUIRE(!state.is_number(-2));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_string__empty);
+ATF_TEST_CASE_BODY(is_string__empty)
+{
+ lutok::state state;
+ ATF_REQUIRE(!state.is_string(-1));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_string__ok);
+ATF_TEST_CASE_BODY(is_string__ok)
+{
+ lutok::state state;
+ lua_pushinteger(raw(state), 3);
+ ATF_REQUIRE(state.is_string(-1));
+ lua_pushnil(raw(state));
+ ATF_REQUIRE(!state.is_string(-1));
+ ATF_REQUIRE(state.is_string(-2));
+ lua_pushstring(raw(state), "foo");
+ ATF_REQUIRE(state.is_string(-1));
+ ATF_REQUIRE(!state.is_string(-2));
+ ATF_REQUIRE(state.is_string(-3));
+ lua_pop(raw(state), 3);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_table__empty);
+ATF_TEST_CASE_BODY(is_table__empty)
+{
+ lutok::state state;
+ ATF_REQUIRE(!state.is_table(-1));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_table__ok);
+ATF_TEST_CASE_BODY(is_table__ok)
+{
+ lutok::state state;
+ luaL_dostring(raw(state), "t = {3, 4, 5}");
+
+ lua_pushstring(raw(state), "foo");
+ ATF_REQUIRE(!state.is_table(-1));
+ lua_getglobal(raw(state), "t");
+ ATF_REQUIRE(state.is_table(-1));
+ ATF_REQUIRE(!state.is_table(-2));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_userdata__empty);
+ATF_TEST_CASE_BODY(is_userdata__empty)
+{
+ lutok::state state;
+ ATF_REQUIRE(!state.is_userdata(-1));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_userdata__ok);
+ATF_TEST_CASE_BODY(is_userdata__ok)
+{
+ lutok::state state;
+
+ lua_pushstring(raw(state), "foo");
+ ATF_REQUIRE(!state.is_userdata(-1));
+ lua_newuserdata(raw(state), 543);
+ ATF_REQUIRE(state.is_userdata(-1));
+ ATF_REQUIRE(!state.is_userdata(-2));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(load_file__ok);
+ATF_TEST_CASE_BODY(load_file__ok)
+{
+ std::ofstream output("test.lua");
+ output << "in_the_file = \"oh yes\"\n";
+ output.close();
+
+ lutok::state state;
+ state.load_file("test.lua");
+ ATF_REQUIRE(lua_pcall(raw(state), 0, 0, 0) == 0);
+ lua_getglobal(raw(state), "in_the_file");
+ ATF_REQUIRE(std::strcmp("oh yes", lua_tostring(raw(state), -1)) == 0);
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(load_file__api_error);
+ATF_TEST_CASE_BODY(load_file__api_error)
+{
+ std::ofstream output("test.lua");
+ output << "I have a bad syntax! Wohoo!\n";
+ output.close();
+
+ lutok::state state;
+ REQUIRE_API_ERROR("luaL_loadfile", state.load_file("test.lua"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(load_file__file_not_found_error);
+ATF_TEST_CASE_BODY(load_file__file_not_found_error)
+{
+ lutok::state state;
+ ATF_REQUIRE_THROW_RE(lutok::file_not_found_error, "missing.lua",
+ state.load_file("missing.lua"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(load_string__ok);
+ATF_TEST_CASE_BODY(load_string__ok)
+{
+ lutok::state state;
+ state.load_string("return 2 + 3");
+ ATF_REQUIRE(lua_pcall(raw(state), 0, 1, 0) == 0);
+ ATF_REQUIRE_EQ(5, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(load_string__fail);
+ATF_TEST_CASE_BODY(load_string__fail)
+{
+ lutok::state state;
+ REQUIRE_API_ERROR("luaL_loadstring", state.load_string("-"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(new_table);
+ATF_TEST_CASE_BODY(new_table)
+{
+ lutok::state state;
+ state.new_table();
+ ATF_REQUIRE_EQ(1, lua_gettop(raw(state)));
+ ATF_REQUIRE(lua_istable(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(new_userdata);
+ATF_TEST_CASE_BODY(new_userdata)
+{
+ lutok::state state;
+ int* pointer = state.new_userdata< int >();
+ *pointer = 1234;
+ ATF_REQUIRE_EQ(1, lua_gettop(raw(state)));
+ ATF_REQUIRE(lua_isuserdata(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(next__empty);
+ATF_TEST_CASE_BODY(next__empty)
+{
+ lutok::state state;
+ luaL_dostring(raw(state), "t = {}");
+
+ lua_getglobal(raw(state), "t");
+ lua_pushstring(raw(state), "this is a dummy value");
+ lua_pushnil(raw(state));
+ ATF_REQUIRE(!state.next(-3));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(next__many);
+ATF_TEST_CASE_BODY(next__many)
+{
+ lutok::state state;
+ luaL_dostring(raw(state), "t = {}; t[1] = 100; t[2] = 200");
+
+ lua_getglobal(raw(state), "t");
+ lua_pushnil(raw(state));
+
+ ATF_REQUIRE(state.next(-2));
+ ATF_REQUIRE_EQ(3, lua_gettop(raw(state)));
+ ATF_REQUIRE(lua_isnumber(raw(state), -2));
+ ATF_REQUIRE_EQ(1, lua_tointeger(raw(state), -2));
+ ATF_REQUIRE(lua_isnumber(raw(state), -1));
+ ATF_REQUIRE_EQ(100, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 1);
+
+ ATF_REQUIRE(state.next(-2));
+ ATF_REQUIRE_EQ(3, lua_gettop(raw(state)));
+ ATF_REQUIRE(lua_isnumber(raw(state), -2));
+ ATF_REQUIRE_EQ(2, lua_tointeger(raw(state), -2));
+ ATF_REQUIRE(lua_isnumber(raw(state), -1));
+ ATF_REQUIRE_EQ(200, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 1);
+
+ ATF_REQUIRE(!state.next(-2));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(open_base);
+ATF_TEST_CASE_BODY(open_base)
+{
+ lutok::state state;
+ check_modules(state, "");
+ state.open_base();
+ check_modules(state, "base");
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(open_all);
+ATF_TEST_CASE_BODY(open_all)
+{
+ lutok::state state;
+ check_modules(state, "");
+ state.open_all();
+ // Best-effort attempt at looking for a bunch of possible modules.
+ ATF_REQUIRE(is_available(state, "assert"));
+ ATF_REQUIRE(is_available(state, "debug.getinfo"));
+ ATF_REQUIRE(is_available(state, "package.path"));
+ ATF_REQUIRE(is_available(state, "string.byte"));
+ ATF_REQUIRE(is_available(state, "table.concat"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(open_string);
+ATF_TEST_CASE_BODY(open_string)
+{
+ lutok::state state;
+ check_modules(state, "");
+ state.open_string();
+ check_modules(state, "string");
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(open_table);
+ATF_TEST_CASE_BODY(open_table)
+{
+ lutok::state state;
+ check_modules(state, "");
+ state.open_table();
+ check_modules(state, "table");
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(pcall__ok);
+ATF_TEST_CASE_BODY(pcall__ok)
+{
+ lutok::state state;
+ luaL_loadstring(raw(state), "function mul(a, b) return a * b; end");
+ state.pcall(0, 0, 0);
+ state.get_global_table();
+ lua_pushstring(raw(state), "mul");
+ lua_gettable(raw(state), -2);
+ lua_pushinteger(raw(state), 3);
+ lua_pushinteger(raw(state), 5);
+ state.pcall(2, 1, 0);
+ ATF_REQUIRE_EQ(15, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(pcall__fail);
+ATF_TEST_CASE_BODY(pcall__fail)
+{
+ lutok::state state;
+ lua_pushnil(raw(state));
+ REQUIRE_API_ERROR("lua_pcall", state.pcall(0, 0, 0));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(pop__one);
+ATF_TEST_CASE_BODY(pop__one)
+{
+ lutok::state state;
+ lua_pushinteger(raw(state), 10);
+ lua_pushinteger(raw(state), 20);
+ lua_pushinteger(raw(state), 30);
+ state.pop(1);
+ ATF_REQUIRE_EQ(2, lua_gettop(raw(state)));
+ ATF_REQUIRE_EQ(20, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(pop__many);
+ATF_TEST_CASE_BODY(pop__many)
+{
+ lutok::state state;
+ lua_pushinteger(raw(state), 10);
+ lua_pushinteger(raw(state), 20);
+ lua_pushinteger(raw(state), 30);
+ state.pop(2);
+ ATF_REQUIRE_EQ(1, lua_gettop(raw(state)));
+ ATF_REQUIRE_EQ(10, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(push_boolean);
+ATF_TEST_CASE_BODY(push_boolean)
+{
+ lutok::state state;
+ state.push_boolean(true);
+ ATF_REQUIRE_EQ(1, lua_gettop(raw(state)));
+ ATF_REQUIRE(lua_toboolean(raw(state), -1));
+ state.push_boolean(false);
+ ATF_REQUIRE_EQ(2, lua_gettop(raw(state)));
+ ATF_REQUIRE(!lua_toboolean(raw(state), -1));
+ ATF_REQUIRE(lua_toboolean(raw(state), -2));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(push_cxx_closure);
+ATF_TEST_CASE_BODY(push_cxx_closure)
+{
+ lutok::state state;
+ state.push_integer(15);
+ state.push_cxx_closure(cxx_multiply_closure, 1);
+ lua_setglobal(raw(state), "cxx_multiply_closure");
+
+ ATF_REQUIRE(luaL_dostring(raw(state),
+ "return cxx_multiply_closure(10)") == 0);
+ ATF_REQUIRE_EQ(150, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(push_cxx_function__ok);
+ATF_TEST_CASE_BODY(push_cxx_function__ok)
+{
+ lutok::state state;
+ state.push_cxx_function(cxx_divide);
+ lua_setglobal(raw(state), "cxx_divide");
+
+ ATF_REQUIRE(luaL_dostring(raw(state), "return cxx_divide(17, 3)") == 0);
+ ATF_REQUIRE_EQ(5, lua_tointeger(raw(state), -2));
+ ATF_REQUIRE_EQ(2, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(push_cxx_function__fail_exception);
+ATF_TEST_CASE_BODY(push_cxx_function__fail_exception)
+{
+ lutok::state state;
+ state.push_cxx_function(cxx_divide);
+ lua_setglobal(raw(state), "cxx_divide");
+
+ ATF_REQUIRE(luaL_dostring(raw(state), "return cxx_divide(15, 0)") != 0);
+ ATF_REQUIRE_MATCH("Divisor is 0", lua_tostring(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(push_cxx_function__fail_anything);
+ATF_TEST_CASE_BODY(push_cxx_function__fail_anything)
+{
+ lutok::state state;
+ state.push_cxx_function(cxx_divide);
+ lua_setglobal(raw(state), "cxx_divide");
+
+ ATF_REQUIRE(luaL_dostring(raw(state), "return cxx_divide(-3, -1)") != 0);
+ ATF_REQUIRE_MATCH("Unhandled exception", lua_tostring(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(push_cxx_function__fail_overflow);
+ATF_TEST_CASE_BODY(push_cxx_function__fail_overflow)
+{
+ lutok::state state;
+ state.push_cxx_function(raise_long_error);
+ lua_setglobal(raw(state), "fail");
+
+ ATF_REQUIRE(luaL_dostring(raw(state), "return fail(900)") != 0);
+ ATF_REQUIRE_MATCH(std::string(900, 'A'), lua_tostring(raw(state), -1));
+ lua_pop(raw(state), 1);
+
+ ATF_REQUIRE(luaL_dostring(raw(state), "return fail(8192)") != 0);
+ ATF_REQUIRE_MATCH(std::string(900, 'A'), lua_tostring(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(push_integer);
+ATF_TEST_CASE_BODY(push_integer)
+{
+ lutok::state state;
+ state.push_integer(12);
+ ATF_REQUIRE_EQ(1, lua_gettop(raw(state)));
+ ATF_REQUIRE_EQ(12, lua_tointeger(raw(state), -1));
+ state.push_integer(34);
+ ATF_REQUIRE_EQ(2, lua_gettop(raw(state)));
+ ATF_REQUIRE_EQ(34, lua_tointeger(raw(state), -1));
+ ATF_REQUIRE_EQ(12, lua_tointeger(raw(state), -2));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(push_nil);
+ATF_TEST_CASE_BODY(push_nil)
+{
+ lutok::state state;
+ state.push_nil();
+ ATF_REQUIRE_EQ(1, lua_gettop(raw(state)));
+ ATF_REQUIRE(lua_isnil(raw(state), -1));
+ state.push_integer(34);
+ ATF_REQUIRE_EQ(2, lua_gettop(raw(state)));
+ ATF_REQUIRE(!lua_isnil(raw(state), -1));
+ ATF_REQUIRE(lua_isnil(raw(state), -2));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(push_string);
+ATF_TEST_CASE_BODY(push_string)
+{
+ lutok::state state;
+
+ {
+ std::string str = "first";
+ state.push_string(str);
+ ATF_REQUIRE_EQ(1, lua_gettop(raw(state)));
+ ATF_REQUIRE_EQ(std::string("first"), lua_tostring(raw(state), -1));
+ str = "second";
+ state.push_string(str);
+ }
+ ATF_REQUIRE_EQ(2, lua_gettop(raw(state)));
+ ATF_REQUIRE_EQ(std::string("second"), lua_tostring(raw(state), -1));
+ ATF_REQUIRE_EQ(std::string("first"), lua_tostring(raw(state), -2));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(push_value);
+ATF_TEST_CASE_BODY(push_value)
+{
+ lutok::state state;
+
+ lua_pushinteger(raw(state), 10);
+ lua_pushinteger(raw(state), 20);
+ state.push_value(-2);
+ ATF_REQUIRE_EQ(3, lua_gettop(raw(state)));
+ ATF_REQUIRE_EQ(10, lua_tointeger(raw(state), -1));
+ ATF_REQUIRE_EQ(20, lua_tointeger(raw(state), -2));
+ ATF_REQUIRE_EQ(10, lua_tointeger(raw(state), -3));
+ lua_pop(raw(state), 3);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(raw_get);
+ATF_TEST_CASE_BODY(raw_get)
+{
+ lutok::state state;
+
+ luaL_openlibs(raw(state));
+ ATF_REQUIRE(luaL_dostring(
+ raw(state), "t = {foo=123} ; setmetatable(t, {__index=1})") == 0);
+ lua_getglobal(raw(state), "t");
+ lua_pushinteger(raw(state), 9876);
+ lua_pushstring(raw(state), "foo");
+ state.raw_get(-3);
+ ATF_REQUIRE(lua_isnumber(raw(state), -1));
+ ATF_REQUIRE_EQ(123, lua_tointeger(raw(state), -1));
+ ATF_REQUIRE_EQ(9876, lua_tointeger(raw(state), -2));
+ lua_pop(raw(state), 3);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(raw_set);
+ATF_TEST_CASE_BODY(raw_set)
+{
+ lutok::state state;
+
+ luaL_openlibs(raw(state));
+ ATF_REQUIRE(luaL_dostring(
+ raw(state), "t = {} ; setmetatable(t, {__newindex=1})") == 0);
+ lua_getglobal(raw(state), "t");
+ lua_pushinteger(raw(state), 876);
+ lua_pushstring(raw(state), "foo");
+ lua_pushinteger(raw(state), 345);
+ state.raw_set(-4);
+ ATF_REQUIRE(luaL_dostring(raw(state), "return t.foo") == 0);
+ ATF_REQUIRE(lua_isnumber(raw(state), -1));
+ ATF_REQUIRE_EQ(345, lua_tointeger(raw(state), -1));
+ ATF_REQUIRE_EQ(876, lua_tointeger(raw(state), -2));
+ lua_pop(raw(state), 3);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(registry_index);
+ATF_TEST_CASE_BODY(registry_index)
+{
+ lutok::state state;
+ lua_pushvalue(raw(state), lutok::registry_index);
+ lua_pushstring(raw(state), "custom_variable");
+ lua_pushstring(raw(state), "custom value");
+ lua_settable(raw(state), -3);
+ lua_pop(raw(state), 1);
+ ATF_REQUIRE(luaL_dostring(raw(state),
+ "return custom_variable == nil") == 0);
+ ATF_REQUIRE(lua_isboolean(raw(state), -1));
+ ATF_REQUIRE(lua_toboolean(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set_global);
+ATF_TEST_CASE_BODY(set_global)
+{
+ lutok::state state;
+ lua_pushinteger(raw(state), 3);
+ state.set_global("test_variable");
+ ATF_REQUIRE(luaL_dostring(raw(state), "return test_variable + 1") == 0);
+ ATF_REQUIRE(lua_isnumber(raw(state), -1));
+ ATF_REQUIRE_EQ(4, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set_metatable);
+ATF_TEST_CASE_BODY(set_metatable)
+{
+ lutok::state state;
+ ATF_REQUIRE(luaL_dostring(
+ raw(state),
+ "mt = {}\n"
+ "mt.__add = function(a, b) return a[1] + b end\n"
+ "numbers = {}\n"
+ "numbers[1] = 5\n") == 0);
+
+ lua_getglobal(raw(state), "numbers");
+ lua_pushinteger(raw(state), 1234);
+ lua_getglobal(raw(state), "mt");
+ state.set_metatable(-3);
+ lua_pop(raw(state), 2);
+
+ ATF_REQUIRE(luaL_dostring(raw(state), "return numbers + 2") == 0);
+ ATF_REQUIRE(lua_isnumber(raw(state), -1));
+ ATF_REQUIRE_EQ(7, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set_table__ok);
+ATF_TEST_CASE_BODY(set_table__ok)
+{
+ lutok::state state;
+ ATF_REQUIRE(luaL_dostring(raw(state), "t = { a = 1, bar = 234 }") == 0);
+ lua_getglobal(raw(state), "t");
+
+ lua_pushstring(raw(state), "bar");
+ lua_pushstring(raw(state), "baz");
+ state.set_table(-3);
+ ATF_REQUIRE_EQ(1, lua_gettop(raw(state)));
+
+ lua_pushstring(raw(state), "a");
+ lua_gettable(raw(state), -2);
+ ATF_REQUIRE(lua_isnumber(raw(state), -1));
+ ATF_REQUIRE_EQ(1, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 1);
+
+ lua_pushstring(raw(state), "bar");
+ lua_gettable(raw(state), -2);
+ ATF_REQUIRE(lua_isstring(raw(state), -1));
+ ATF_REQUIRE_EQ(std::string("baz"), lua_tostring(raw(state), -1));
+ lua_pop(raw(state), 1);
+
+ lua_pop(raw(state), 1);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set_table__nil);
+ATF_TEST_CASE_BODY(set_table__nil)
+{
+ lutok::state state;
+ lua_pushnil(raw(state));
+ lua_pushinteger(raw(state), 1);
+ lua_pushinteger(raw(state), 2);
+ REQUIRE_API_ERROR("lua_settable", state.set_table(-3));
+ lua_pop(raw(state), 3);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(to_boolean);
+ATF_TEST_CASE_BODY(to_boolean)
+{
+ lutok::state state;
+ lua_pushboolean(raw(state), 0);
+ lua_pushboolean(raw(state), 1);
+ ATF_REQUIRE(!state.to_boolean(-2));
+ ATF_REQUIRE(state.to_boolean(-1));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(to_integer);
+ATF_TEST_CASE_BODY(to_integer)
+{
+ lutok::state state;
+ lua_pushinteger(raw(state), 12);
+ lua_pushstring(raw(state), "foobar");
+ ATF_REQUIRE_EQ(12, state.to_integer(-2));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(to_string);
+ATF_TEST_CASE_BODY(to_string)
+{
+ lutok::state state;
+ lua_pushstring(raw(state), "foobar");
+ lua_pushinteger(raw(state), 12);
+ ATF_REQUIRE_EQ("foobar", state.to_string(-2));
+ ATF_REQUIRE_EQ("12", state.to_string(-1));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(to_userdata);
+ATF_TEST_CASE_BODY(to_userdata)
+{
+ lutok::state state;
+ {
+ int* pointer = static_cast< int* >(
+ lua_newuserdata(raw(state), sizeof(int)));
+ *pointer = 987;
+ }
+
+ lua_pushinteger(raw(state), 3);
+ int* pointer = state.to_userdata< int >(-2);
+ ATF_REQUIRE_EQ(987, *pointer);
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(upvalue_index);
+ATF_TEST_CASE_BODY(upvalue_index)
+{
+ lutok::state state;
+ lua_pushinteger(raw(state), 25);
+ lua_pushinteger(raw(state), 30);
+ lua_pushcclosure(raw(state), c_get_upvalues, 2);
+ lua_setglobal(raw(state), "c_get_upvalues");
+
+ ATF_REQUIRE(luaL_dostring(raw(state),
+ "return c_get_upvalues()") == 0);
+ ATF_REQUIRE_EQ(25, lua_tointeger(raw(state), -2));
+ ATF_REQUIRE_EQ(30, lua_tointeger(raw(state), -1));
+ lua_pop(raw(state), 2);
+}
+
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, close);
+ ATF_ADD_TEST_CASE(tcs, get_global__ok);
+ ATF_ADD_TEST_CASE(tcs, get_global__undefined);
+ ATF_ADD_TEST_CASE(tcs, get_global_table);
+ ATF_ADD_TEST_CASE(tcs, get_metafield__ok);
+ ATF_ADD_TEST_CASE(tcs, get_metafield__undefined);
+ ATF_ADD_TEST_CASE(tcs, get_metatable__ok);
+ ATF_ADD_TEST_CASE(tcs, get_metatable__undefined);
+ ATF_ADD_TEST_CASE(tcs, get_table__ok);
+ ATF_ADD_TEST_CASE(tcs, get_table__nil);
+ ATF_ADD_TEST_CASE(tcs, get_table__unknown_index);
+ ATF_ADD_TEST_CASE(tcs, get_top);
+ ATF_ADD_TEST_CASE(tcs, insert);
+ ATF_ADD_TEST_CASE(tcs, is_boolean__empty);
+ ATF_ADD_TEST_CASE(tcs, is_boolean__ok);
+ ATF_ADD_TEST_CASE(tcs, is_function__empty);
+ ATF_ADD_TEST_CASE(tcs, is_function__ok);
+ ATF_ADD_TEST_CASE(tcs, is_nil__empty);
+ ATF_ADD_TEST_CASE(tcs, is_nil__ok);
+ ATF_ADD_TEST_CASE(tcs, is_number__empty);
+ ATF_ADD_TEST_CASE(tcs, is_number__ok);
+ ATF_ADD_TEST_CASE(tcs, is_string__empty);
+ ATF_ADD_TEST_CASE(tcs, is_string__ok);
+ ATF_ADD_TEST_CASE(tcs, is_table__empty);
+ ATF_ADD_TEST_CASE(tcs, is_table__ok);
+ ATF_ADD_TEST_CASE(tcs, is_userdata__empty);
+ ATF_ADD_TEST_CASE(tcs, is_userdata__ok);
+ ATF_ADD_TEST_CASE(tcs, load_file__ok);
+ ATF_ADD_TEST_CASE(tcs, load_file__api_error);
+ ATF_ADD_TEST_CASE(tcs, load_file__file_not_found_error);
+ ATF_ADD_TEST_CASE(tcs, load_string__ok);
+ ATF_ADD_TEST_CASE(tcs, load_string__fail);
+ ATF_ADD_TEST_CASE(tcs, new_table);
+ ATF_ADD_TEST_CASE(tcs, new_userdata);
+ ATF_ADD_TEST_CASE(tcs, next__empty);
+ ATF_ADD_TEST_CASE(tcs, next__many);
+ ATF_ADD_TEST_CASE(tcs, open_all);
+ ATF_ADD_TEST_CASE(tcs, open_base);
+ ATF_ADD_TEST_CASE(tcs, open_string);
+ ATF_ADD_TEST_CASE(tcs, open_table);
+ ATF_ADD_TEST_CASE(tcs, pcall__ok);
+ ATF_ADD_TEST_CASE(tcs, pcall__fail);
+ ATF_ADD_TEST_CASE(tcs, pop__one);
+ ATF_ADD_TEST_CASE(tcs, pop__many);
+ ATF_ADD_TEST_CASE(tcs, push_boolean);
+ ATF_ADD_TEST_CASE(tcs, push_cxx_closure);
+ ATF_ADD_TEST_CASE(tcs, push_cxx_function__ok);
+ ATF_ADD_TEST_CASE(tcs, push_cxx_function__fail_exception);
+ ATF_ADD_TEST_CASE(tcs, push_cxx_function__fail_anything);
+ ATF_ADD_TEST_CASE(tcs, push_cxx_function__fail_overflow);
+ ATF_ADD_TEST_CASE(tcs, push_integer);
+ ATF_ADD_TEST_CASE(tcs, push_nil);
+ ATF_ADD_TEST_CASE(tcs, push_string);
+ ATF_ADD_TEST_CASE(tcs, push_value);
+ ATF_ADD_TEST_CASE(tcs, raw_get);
+ ATF_ADD_TEST_CASE(tcs, raw_set);
+ ATF_ADD_TEST_CASE(tcs, registry_index);
+ ATF_ADD_TEST_CASE(tcs, set_global);
+ ATF_ADD_TEST_CASE(tcs, set_metatable);
+ ATF_ADD_TEST_CASE(tcs, set_table__ok);
+ ATF_ADD_TEST_CASE(tcs, set_table__nil);
+ ATF_ADD_TEST_CASE(tcs, to_boolean);
+ ATF_ADD_TEST_CASE(tcs, to_integer);
+ ATF_ADD_TEST_CASE(tcs, to_string);
+ ATF_ADD_TEST_CASE(tcs, to_userdata);
+ ATF_ADD_TEST_CASE(tcs, upvalue_index);
+}
diff --git a/test_utils.hpp b/test_utils.hpp
new file mode 100644
index 000000000000..9cbb8edee729
--- /dev/null
+++ b/test_utils.hpp
@@ -0,0 +1,141 @@
+// Copyright 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file test_utils.hpp
+/// Utilities for tests of the lua modules.
+///
+/// This file is intended to be included once, and only once, for every test
+/// program that needs it. All the code is herein contained to simplify the
+/// dependency chain in the build rules.
+
+#if !defined(LUTOK_TEST_UTILS_HPP)
+# define LUTOK_TEST_UTILS_HPP
+#else
+# error "test_utils.hpp can only be included once"
+#endif
+
+#include <atf-c++.hpp>
+
+#include "c_gate.hpp"
+#include "exceptions.hpp"
+#include "state.hpp"
+
+
+namespace {
+
+
+/// Checks that a given expression raises a particular lutok::api_error.
+///
+/// We cannot make any assumptions regarding the error text provided by Lua, so
+/// we resort to checking only which API function raised the error (because our
+/// code is the one hardcoding these strings).
+///
+/// \param exp_api_function The name of the Lua C API function that causes the
+/// error.
+/// \param statement The statement to execute.
+#define REQUIRE_API_ERROR(exp_api_function, statement) \
+ do { \
+ try { \
+ statement; \
+ ATF_FAIL("api_error not raised by " #statement); \
+ } catch (const lutok::api_error& api_error) { \
+ ATF_REQUIRE_EQ(exp_api_function, api_error.api_function()); \
+ } \
+ } while (0)
+
+
+/// Gets the pointer to the internal lua_State of a state object.
+///
+/// This is pure syntactic sugar to simplify typing in the test cases.
+///
+/// \param state The Lua state.
+///
+/// \return The internal lua_State of the input Lua state.
+static inline lua_State*
+raw(lutok::state& state)
+{
+ return lutok::state_c_gate(state).c_state();
+}
+
+
+/// Ensures that the Lua stack maintains its original height upon exit.
+///
+/// Use an instance of this class to check that a piece of code does not have
+/// side-effects on the Lua stack.
+///
+/// To be used within a test case only.
+class stack_balance_checker {
+ /// The Lua state.
+ lutok::state& _state;
+
+ /// Whether to install a sentinel on the stack for balance enforcement.
+ bool _with_sentinel;
+
+ /// The height of the stack on creation.
+ unsigned int _old_count;
+
+public:
+ /// Constructs a new stack balance checker.
+ ///
+ /// \param state_ The Lua state to validate.
+ /// \param with_sentinel_ If true, insert a sentinel item into the stack and
+ /// validate upon exit that the item is still there. This is an attempt
+ /// to ensure that already-existing items are not removed from the stack
+ /// by the code under test.
+ stack_balance_checker(lutok::state& state_,
+ const bool with_sentinel_ = true) :
+ _state(state_),
+ _with_sentinel(with_sentinel_),
+ _old_count(_state.get_top())
+ {
+ if (_with_sentinel)
+ _state.push_integer(987654321);
+ }
+
+ /// Destructor for the object.
+ ///
+ /// If the stack height does not match the height when the instance was
+ /// created, this fails the test case.
+ ~stack_balance_checker(void)
+ {
+ if (_with_sentinel) {
+ if (!_state.is_number(-1) || _state.to_integer(-1) != 987654321)
+ ATF_FAIL("Stack corrupted: sentinel not found");
+ _state.pop(1);
+ }
+
+ unsigned int new_count = _state.get_top();
+ if (_old_count != new_count)
+ //ATF_FAIL(F("Stack not balanced: before %d, after %d") %
+ // _old_count % new_count);
+ ATF_FAIL("Stack not balanced");
+ }
+};
+
+
+} // anonymous namespace