TS-4099: add core library infrastructure for Lua bindings

This adds a libbindings.la library with a convenience wrapper class
for dealing with a Lua state and binding APIs into it. We also add
some low-level Lua convenience APIs that end up being useful.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/5942c420
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/5942c420
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/5942c420

Branch: refs/heads/master
Commit: 5942c42020fde09e348c418ae0aec41cd8e4cf91
Parents: 4e2f0b8
Author: James Peach <[email protected]>
Authored: Tue Nov 24 15:59:54 2015 -0800
Committer: James Peach <[email protected]>
Committed: Thu Jan 21 18:52:18 2016 -0800

----------------------------------------------------------------------
 configure.ac             |   1 +
 lib/Makefile.am          |   2 +-
 lib/bindings/Makefile.am |  40 ++++++
 lib/bindings/bindings.cc | 275 ++++++++++++++++++++++++++++++++++++++++++
 lib/bindings/bindings.h  |  72 +++++++++++
 lib/bindings/lua.cc      |  63 ++++++++++
 lib/bindings/lua.h       |  68 +++++++++++
 lib/bindings/repl.cc     |  60 +++++++++
 lib/bindings/repl.h      |  30 +++++
 9 files changed, 610 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5942c420/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 5712cf8..56793a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1846,6 +1846,7 @@ AC_CONFIG_FILES([
   iocore/net/Makefile
   iocore/utils/Makefile
   lib/Makefile
+  lib/bindings/Makefile
   lib/perl/Makefile
   lib/perl/lib/Apache/TS.pm
   lib/records/Makefile

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5942c420/lib/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 3306483..685b6be 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -16,7 +16,7 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
-SUBDIRS = ts records tsconfig
+SUBDIRS = ts records tsconfig bindings
 
 if BUILD_PERL_LIB
 SUBDIRS += perl

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5942c420/lib/bindings/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/bindings/Makefile.am b/lib/bindings/Makefile.am
new file mode 100644
index 0000000..661411c
--- /dev/null
+++ b/lib/bindings/Makefile.am
@@ -0,0 +1,40 @@
+#  Makefile.am
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+AM_CPPFLAGS = \
+  $(LUAJIT_CPPFLAGS) \
+  -I$(top_srcdir)/lib \
+  -I$(top_srcdir)/lib/records
+
+AM_CXXFLAGS = \
+  $(LUAJIT_CFLAGS)
+
+if BUILD_LUAJIT
+
+noinst_LTLIBRARIES = libbindings.la
+
+libbindings_la_SOURCES = \
+  bindings.cc \
+  bindings.h \
+  lua.cc \
+  lua.h \
+  repl.cc \
+  repl.h
+
+endif
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5942c420/lib/bindings/bindings.cc
----------------------------------------------------------------------
diff --git a/lib/bindings/bindings.cc b/lib/bindings/bindings.cc
new file mode 100644
index 0000000..9cd01b8
--- /dev/null
+++ b/lib/bindings/bindings.cc
@@ -0,0 +1,275 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @section license License
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "bindings.h"
+#include "ts/Diags.h"
+
+static const char selfkey[] = "bb3ecc8d-de6b-4f48-9aca-b3a3f14bdbad";
+
+static bool
+is_indexable(lua_State *L, int index)
+{
+  return lua_istable(L, index) || lua_isuserdata(L, index);
+}
+
+BindingInstance::BindingInstance() : lua(NULL)
+{
+}
+
+BindingInstance::~BindingInstance()
+{
+  if (this->lua) {
+    lua_close(this->lua);
+  }
+}
+
+void
+BindingInstance::attach_ptr(const char *name, void *ptr)
+{
+  this->attachments[name] = ptr;
+}
+
+void *
+BindingInstance::retrieve_ptr(const char *name)
+{
+  auto ptr = this->attachments.find(name);
+  return (ptr == this->attachments.end()) ? NULL : ptr->second;
+}
+
+void
+BindingInstance::bind_constant(const char *name, lua_Integer value)
+{
+  lua_pushinteger(this->lua, value);
+  this->bind_value(name, -1);
+  lua_pop(this->lua, 1);
+}
+
+void
+BindingInstance::bind_constant(const char *name, const char *value)
+{
+  lua_pushlstring(this->lua, value, strlen(value));
+  this->bind_value(name, -1);
+  lua_pop(this->lua, 1);
+}
+
+void
+BindingInstance::bind_function(const char *name, int (*value)(lua_State *))
+{
+  lua_pushcfunction(this->lua, value);
+  this->bind_value(name, -1);
+  lua_pop(this->lua, 1);
+}
+
+// Bind an arbitrary Lua value from the give stack position.
+void
+BindingInstance::bind_value(const char *name, int value)
+{
+  const char * start = name;
+  const char * end = name;
+
+  int depth = 0;
+
+  // Make the value an absolute stack inde because we are going to
+  // invalidate relative indices.
+  value = lua_absolute_index(this->lua, value);
+
+  // XXX extract this code so that we can using it for binding constants
+  // into an arbitrary table path ...
+  Debug("lua", "binding %s value at %d to %s\n", luaL_typename(this->lua, 
value), value, name);
+
+  for (; (end = ::strchr(start, '.')); start = end + 1) {
+    std::string name(start, end);
+
+    Debug("lua", "checking for table '%s'\n", name.c_str());
+    if (depth == 0) {
+      lua_getglobal(this->lua, name.c_str());
+      if (lua_isnil(this->lua, -1)) {
+        // No table with this name, construct one.
+        Debug("lua", "creating global table '%s'\n", name.c_str());
+
+        lua_pop(this->lua, 1); // Pop the nil.
+        lua_newtable(this->lua);
+        lua_setglobal(this->lua, name.c_str());
+        lua_getglobal(this->lua, name.c_str());
+
+        // Top of stack MUST be a table now.
+        ink_assert(lua_istable(this->lua, -1));
+      }
+
+      ink_assert(is_indexable(this->lua, -1));
+    } else {
+      ink_assert(is_indexable(this->lua, -1));
+
+      Debug("lua", "checking for table key '%s'\n", name.c_str());
+
+      // Push the string key.
+      lua_pushlstring(this->lua, &name[0], name.size());
+      // Get the table entry (now on top of the stack).
+      lua_gettable(this->lua, -2);
+
+      if (lua_isnil(this->lua, -1)) {
+        Debug("lua", "creating table key '%s'\n", name.c_str());
+
+        lua_pop(this->lua, 1); // Pop the nil.
+        lua_pushlstring(this->lua, &name[0], name.size());
+        lua_newtable(this->lua);
+
+        // Set the table entry. The stack now looks like:
+        //  -1  value (the new table)
+        //  -2  index (string)
+        //  -3  target (the table to add the index to)
+        lua_settable(this->lua, -3);
+
+        // Get the table entry we just created.
+        lua_pushlstring(this->lua, &name[0], name.size());
+        lua_gettable(this->lua, -2);
+
+        // Top of stack MUST be a table now.
+        ink_assert(lua_istable(this->lua, -1));
+      }
+
+      // The new entry is on top of the stack.
+      ink_assert(is_indexable(this->lua, -1));
+    }
+
+    ++depth;
+  }
+
+  Debug("lua", "stack depth is %d (expected %d)\n", lua_gettop(this->lua), 
depth);
+  Debug("lua", "last name token is '%s'\n", start);
+
+  // If we pushed a series of tables onto the stack, bind the name to a table
+  // entry. otherwise bind it as a global name.
+  if (depth) {
+    lua_pushstring(this->lua, start);
+    lua_pushvalue(this->lua, value);
+
+    ink_assert(is_indexable(this->lua, -3));
+
+    lua_settable(this->lua, -3);
+    Debug("lua", "stack depth is %d (expected %d)\n", lua_gettop(this->lua), 
depth);
+    lua_pop(this->lua, depth);
+  } else {
+    lua_pushvalue(this->lua, value);
+    lua_setglobal(this->lua, start);
+  }
+}
+
+bool
+BindingInstance::construct()
+{
+  ink_release_assert(this->lua == NULL);
+
+  if ((this->lua = luaL_newstate())) {
+    luaL_openlibs(this->lua);
+
+    // Push a pointer to ourself into the well-known registry key.
+    lua_pushlightuserdata(this->lua, this);
+    lua_setfield(this->lua, LUA_REGISTRYINDEX, selfkey);
+
+    ink_release_assert(BindingInstance::self(this->lua) == this);
+  }
+
+  return this->lua;
+}
+
+bool
+BindingInstance::require(const char *path)
+{
+  ink_release_assert(this->lua != NULL);
+
+  if (luaL_dofile(this->lua, path) != 0) {
+    Warning("%s", lua_tostring(this->lua, -1));
+    lua_pop(this->lua, 1);
+    return false;
+  }
+
+  return true;
+}
+
+BindingInstance *
+BindingInstance::self(lua_State *lua)
+{
+  BindingInstance *binding;
+
+  lua_getfield(lua, LUA_REGISTRYINDEX, selfkey);
+  binding = (BindingInstance *)lua_touserdata(lua, -1);
+
+  ink_release_assert(binding != NULL);
+  ink_release_assert(binding->lua == lua);
+
+  lua_pop(lua, 1);
+  return binding;
+}
+
+void
+BindingInstance::typecheck(lua_State *lua, const char *name, ...)
+{
+  int nargs = lua_gettop(lua);
+  int seen = 0;
+  va_list ap;
+
+  va_start(ap, name);
+
+  for (; seen < nargs; ++seen) {
+    int expected = va_arg(ap, int);
+
+    if (expected == LUA_TNONE) {
+      va_end(ap);
+      luaL_error(lua, "too many arguments to '%s'", name);
+      return;
+    }
+
+    if (lua_type(lua, seen + 1) != expected) {
+      va_end(ap);
+      luaL_error(lua, "bad argument #%d to '%s' (expected %s, received %s)", 
seen + 1, name, lua_typename(lua, expected),
+                 lua_typename(lua, lua_type(lua, seen + 1)));
+      return;
+    }
+  }
+
+  va_end(ap);
+
+  if (seen != nargs) {
+    luaL_error(lua, "too few arguments to '%s' (seen %d, nargs %d)", name, 
seen, nargs);
+  }
+}
+
+void
+BindingInstance::register_metatable(lua_State *lua, const char *name, const 
luaL_reg *metatable)
+{
+  // Create a metatable, adding it to the Lua registry.
+  luaL_newmetatable(lua, name);
+  // Dup the metatable.
+  lua_pushvalue(lua, -1);
+  // Pop one of those copies and assign it to __index field on the 1st 
metatable
+  lua_setfield(lua, -2, "__index");
+  // register functions in the metatable
+  luaL_register(lua, NULL, metatable);
+
+  lua_pop(lua, 1); /* drop metatable */
+
+  ink_assert(lua_gettop(lua) == 0);
+}
+
+/* vim: set sw=4 ts=4 tw=79 et: */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5942c420/lib/bindings/bindings.h
----------------------------------------------------------------------
diff --git a/lib/bindings/bindings.h b/lib/bindings/bindings.h
new file mode 100644
index 0000000..525cb60
--- /dev/null
+++ b/lib/bindings/bindings.h
@@ -0,0 +1,72 @@
+/** @file
+ *
+ *  Lua bindings object.
+ *
+ *  @section license License
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef BINDINGS_H_02DF784C_94BD_4A5C_B57A_F986F5493C6A
+#define BINDINGS_H_02DF784C_94BD_4A5C_B57A_F986F5493C6A
+
+#include <string>
+#include <map>
+#include "lua.h"
+
+struct BindingInstance {
+  BindingInstance();
+  ~BindingInstance();
+
+  // Construct this Lua bindings instance.
+  bool construct();
+
+  // Import a Lua file.
+  bool require(const char *path);
+
+  // Bind values to the specified global name. If the name contains '.'
+  // separators, intermediate tables are constucted and the value is bound
+  // to the final path component.
+  void bind_constant(const char *name, lua_Integer value);
+  void bind_constant(const char *name, const char *value);
+  void bind_function(const char *name, int (*value)(lua_State *));
+  void bind_value(const char *name, int value);
+
+  // Attach a named pointer that we can later fish out from a Lua state.
+  void attach_ptr(const char *, void *);
+  void *retrieve_ptr(const char *);
+
+  // Generic typecheck helper for Lua APIs. Pass in a list of Lua type IDs
+  // (ie. LUA_Txxx) terminated by LUA_TNONE. Throws a Lua error string on
+  // failure.
+  static void typecheck(lua_State *, const char *name, ...);
+
+  // Given a Lua state, return the binding instance that owns it.
+  static BindingInstance *self(lua_State *);
+
+  // Register a Lua metatable for a custom type.
+  static void register_metatable(lua_State *, const char *, const luaL_reg *);
+
+  lua_State *lua;
+
+private:
+  std::map<std::string, void *> attachments;
+  BindingInstance(const BindingInstance &);            // noncopyable
+  BindingInstance &operator=(const BindingInstance &); // noncopyable
+};
+
+#endif /* BINDINGS_H_02DF784C_94BD_4A5C_B57A_F986F5493C6A */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5942c420/lib/bindings/lua.cc
----------------------------------------------------------------------
diff --git a/lib/bindings/lua.cc b/lib/bindings/lua.cc
new file mode 100644
index 0000000..57be614
--- /dev/null
+++ b/lib/bindings/lua.cc
@@ -0,0 +1,63 @@
+/** @file
+ *
+ *  Lua utilities and extensions.
+ *
+ *  @section license License
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "lua.h"
+
+int
+lua_absolute_index(lua_State *L, int relative)
+{
+  return (relative > 0 || relative <= LUA_REGISTRYINDEX) ? relative : 
lua_gettop(L) + relative + 1;
+}
+
+// Check the type at the given index. Error if it is not the expected type.
+void
+lua_checktype(lua_State *L, int index, int ltype)
+{
+  if (lua_type(L, index) != ltype) {
+    luaL_error(L, "bad type, expected '%s' but found '%s'", lua_typename(L, 
ltype), lua_typename(L, lua_type(L, index)));
+  }
+}
+
+// luaL_checkudata() throws an exception if it fails, so to accept variadic
+// user types, we need to non-destructively test whether a userdata is an
+// instance of the type we want.
+bool
+lua_is_userdata(lua_State *L, int index, const char *metatype)
+{
+  int target = lua_absolute_index(L, index);
+  bool result = false;
+
+  // Get the metatable of the target.
+  if (lua_getmetatable(L, target) != 0) {
+    // If there was one, get the metatable of the target type.
+    luaL_getmetatable(L, metatype);
+
+    // Compare them.
+    result = lua_equal(L, -1, -2) == 1;
+
+    // Pop the 2 metatables.
+    lua_pop(L, 2);
+  }
+
+  return result;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5942c420/lib/bindings/lua.h
----------------------------------------------------------------------
diff --git a/lib/bindings/lua.h b/lib/bindings/lua.h
new file mode 100644
index 0000000..8314df6
--- /dev/null
+++ b/lib/bindings/lua.h
@@ -0,0 +1,68 @@
+/** @file
+ *
+ *  Lua utilities and extensions.
+ *
+ *  @section license License
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef LUA_H_7A9F5CCE_01C6_45C3_987A_FDCC1F437AA2
+#define LUA_H_7A9F5CCE_01C6_45C3_987A_FDCC1F437AA2
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+
+// Redeclare luaL_error with format string checking.
+LUALIB_API int(luaL_error)(lua_State *L, const char *fmt, ...) 
__attribute__((format(printf, 2, 3)));
+
+#ifdef __cplusplus
+}
+#endif
+
+// Our version of abs_index() from lauxlib.c. Converts a absolute or relative
+// stack index into an absolute index. This is helpful for functions that
+// accepts an index but are going to do stack manipulation themselves.
+int lua_absolute_index(lua_State *L, int relative);
+
+// Check the type at the given index. Error if it is not the expected type.
+void lua_checktype(lua_State *L, int index, int ltype);
+
+// luaL_checkudata() throws an exception if it fails, so to accept variadic
+// user types, we need to non-destructively test whether a userdata is an
+// instance of the type we want.
+bool lua_is_userdata(lua_State *L, int index, const char *metatype);
+
+// Like lua_newuserdata() but for C++ objects.
+template <typename T>
+T *
+lua_newuserobject(lua_State *L)
+{
+  T *ptr = (T *)lua_newuserdata(L, sizeof(T));
+  if (ptr) {
+    return new (ptr) T();
+  }
+
+  return nullptr;
+}
+
+#endif /* LUA_H_7A9F5CCE_01C6_45C3_987A_FDCC1F437AA2 */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5942c420/lib/bindings/repl.cc
----------------------------------------------------------------------
diff --git a/lib/bindings/repl.cc b/lib/bindings/repl.cc
new file mode 100644
index 0000000..2931db8
--- /dev/null
+++ b/lib/bindings/repl.cc
@@ -0,0 +1,60 @@
+/** @file
+ *
+ *  Lua bindings REPL.
+ *
+ *  @section license License
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "ink_autoconf.h"
+#include "bindings.h"
+#include <stdlib.h>
+
+#if HAVE_READLINE_H
+#include <readline.h>
+#endif
+
+void
+repl(BindingInstance &binding)
+{
+#if HAVE_READLINE_H
+  for (;;) {
+    char *line;
+
+    line = readline("> ");
+    if (line == NULL) {
+      exit(0);
+    }
+
+    if (*line) {
+      ::add_history(line);
+
+      if (luaL_loadbuffer(this->lua, line, ::strlen(line), "@stdin" /* source 
*/) != 0 ||
+          lua_pcall(this->lua, 0, LUA_MULTRET, 0) != 0) {
+        // Pop the error message off the top of the stack and show it ...
+        error("%s\n", lua_tostring(this->lua, -1));
+        lua_pop(this->lua, 1);
+      }
+    }
+
+    ::free(line);
+  }
+
+#endif /*  HAVE_READLINE_H */
+  ::exit(0);
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5942c420/lib/bindings/repl.h
----------------------------------------------------------------------
diff --git a/lib/bindings/repl.h b/lib/bindings/repl.h
new file mode 100644
index 0000000..944a344
--- /dev/null
+++ b/lib/bindings/repl.h
@@ -0,0 +1,30 @@
+/** @file
+ *
+ *  Lua bindings REPL.
+ *
+ *  @section license License
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef REPL_H_DB690467_F8FE_4797_8B08_7FD1F090DBA2
+#define REPL_H_DB690467_F8FE_4797_8B08_7FD1F090DBA2
+
+// Drop this binding instance into a Lua REPL (and never come out).
+void repl(BindingInstance &binding) TS_NORETURN;
+
+#endif /* REPL_H_DB690467_F8FE_4797_8B08_7FD1F090DBA2 */

Reply via email to