summaryrefslogtreecommitdiff
path: root/upb/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'upb/bindings')
-rw-r--r--upb/bindings/googlepb/bridge.cc114
-rw-r--r--upb/bindings/googlepb/bridge.h65
-rw-r--r--upb/bindings/googlepb/proto1.cc27
-rw-r--r--upb/bindings/googlepb/proto1.int.h (renamed from upb/bindings/googlepb/proto1.h)16
-rw-r--r--upb/bindings/googlepb/proto2.cc271
-rw-r--r--upb/bindings/googlepb/proto2.int.h (renamed from upb/bindings/googlepb/proto2.h)18
-rw-r--r--upb/bindings/lua/table.c13
-rw-r--r--upb/bindings/lua/upb.c1736
-rw-r--r--upb/bindings/lua/upb.h106
-rw-r--r--upb/bindings/lua/upb.lua146
-rw-r--r--upb/bindings/lua/upb.pb.c106
-rw-r--r--upb/bindings/lua/upb/descriptor.c20
-rw-r--r--upb/bindings/stdc++/string.h2
13 files changed, 1969 insertions, 671 deletions
diff --git a/upb/bindings/googlepb/bridge.cc b/upb/bindings/googlepb/bridge.cc
index c8e3474..a666ff6 100644
--- a/upb/bindings/googlepb/bridge.cc
+++ b/upb/bindings/googlepb/bridge.cc
@@ -16,8 +16,8 @@
#include <map>
#include <string>
#include "upb/def.h"
-#include "upb/bindings/googlepb/proto1.h"
-#include "upb/bindings/googlepb/proto2.h"
+#include "upb/bindings/googlepb/proto1.int.h"
+#include "upb/bindings/googlepb/proto2.int.h"
#include "upb/handlers.h"
#define ASSERT_STATUS(status) do { \
@@ -39,31 +39,24 @@ namespace goog = ::proto2;
namespace goog = ::google::protobuf;
#endif
-namespace {
-
-const goog::Message* GetPrototype(const goog::Message& m,
- const goog::FieldDescriptor* f) {
- const goog::Message* ret = NULL;
-#ifdef UPB_GOOGLE3
- ret = upb::google::GetProto1WeakPrototype(m, f);
- if (ret) return ret;
-#endif
+namespace upb {
+namespace googlepb {
- if (f->cpp_type() == goog::FieldDescriptor::CPPTYPE_MESSAGE) {
- ret = upb::google::GetFieldPrototype(m, f);
+const goog::Message* TryGetFieldPrototype(const goog::Message& m,
+ const goog::FieldDescriptor* f) {
+ const goog::Message* ret = upb::googlepb::GetProto2FieldPrototype(m, f);
#ifdef UPB_GOOGLE3
- if (!ret) ret = upb::google::GetProto1FieldPrototype(m, f);
+ if (!ret) ret = upb::googlepb::GetProto1FieldPrototype(m, f);
#endif
- assert(ret);
- }
return ret;
}
-} // namespace
-
-namespace upb {
-namespace googlepb {
-
+const goog::Message* GetFieldPrototype(const goog::Message& m,
+ const goog::FieldDescriptor* f) {
+ const goog::Message* ret = TryGetFieldPrototype(m, f);
+ assert(ret);
+ return ret;
+}
/* DefBuilder ****************************************************************/
@@ -117,20 +110,11 @@ const MessageDef* DefBuilder::GetMaybeUnfrozenMessageDef(
reffed_ptr<FieldDef> DefBuilder::NewFieldDef(const goog::FieldDescriptor* f,
const goog::Message* m) {
- const goog::Message* subm = NULL;
- const goog::Message* weak_prototype = NULL;
-
- if (m) {
-#ifdef UPB_GOOGLE3
- weak_prototype = upb::google::GetProto1WeakPrototype(*m, f);
-#endif
- subm = GetPrototype(*m, f);
- }
-
reffed_ptr<FieldDef> upb_f(FieldDef::New());
Status status;
upb_f->set_number(f->number(), &status);
upb_f->set_label(FieldDef::ConvertLabel(f->label()));
+ upb_f->set_descriptor_type(FieldDef::ConvertDescriptorType(f->type()));
#ifdef UPB_GOOGLE3
upb_f->set_lazy(f->options().lazy());
#endif
@@ -142,11 +126,19 @@ reffed_ptr<FieldDef> DefBuilder::NewFieldDef(const goog::FieldDescriptor* f,
upb_f->set_name(f->name(), &status);
}
- // For weak fields, weak_prototype will be non-NULL even though the proto2
- // descriptor does not indicate a submessage field.
- upb_f->set_descriptor_type(weak_prototype
- ? UPB_DESCRIPTOR_TYPE_MESSAGE
- : FieldDef::ConvertDescriptorType(f->type()));
+ const goog::Message* subm = NULL;
+
+ if (m) {
+ subm = TryGetFieldPrototype(*m, f);
+
+ if (upb_f->type() == UPB_TYPE_MESSAGE) {
+ assert(subm);
+ } else if (subm) {
+ // Weak field: subm will be weak prototype even though the proto2
+ // descriptor does not indicate a submessage field.
+ upb_f->set_descriptor_type(UPB_DESCRIPTOR_TYPE_MESSAGE);
+ }
+ }
switch (upb_f->type()) {
case UPB_TYPE_INT32:
@@ -213,6 +205,35 @@ const MessageDef* DefBuilder::GetMessageDefExpandWeak(
}
+/* WriteHandlers *************************************************************/
+
+// static
+bool WriteHandlers::AddFieldHandler(const goog::Message& m,
+ const goog::FieldDescriptor* f,
+ upb::Handlers* h) {
+ const FieldDef* upb_f = h->message_def()->FindFieldByNumber(f->number());
+ if (!upb_f) return false;
+ if (upb::googlepb::TrySetWriteHandlers(f, m, upb_f, h)) return true;
+#ifdef UPB_GOOGLE3
+ if (upb::googlepb::TrySetProto1WriteHandlers(f, m, upb_f, h)) return true;
+#endif
+
+ // Unsupported reflection class.
+ //
+ // Should we fall back to using the public Reflection interface in this
+ // case? It's unclear whether it's supported behavior for users to
+ // create their own Reflection classes.
+ return false;
+}
+
+// static
+upb::reffed_ptr<const upb::Handlers> WriteHandlers::New(
+ const goog::Message& m) {
+ CodeCache cache;
+ return upb::reffed_ptr<const upb::Handlers>(cache.GetWriteHandlers(m));
+}
+
+
/* CodeCache *****************************************************************/
const Handlers* CodeCache::GetMaybeUnfrozenWriteHandlers(
@@ -234,21 +255,11 @@ const Handlers* CodeCache::GetMaybeUnfrozenWriteHandlers(
}
assert(proto2_f);
- if (!upb::google::TrySetWriteHandlers(proto2_f, m, upb_f, h)
-#ifdef UPB_GOOGLE3
- && !upb::google::TrySetProto1WriteHandlers(proto2_f, m, upb_f, h)
-#endif
- ) {
- // Unsupported reflection class.
- //
- // Should we fall back to using the public Reflection interface in this
- // case? It's unclear whether it's supported behavior for users to
- // create their own Reflection classes.
- assert(false);
- }
+ bool ok = WriteHandlers::AddFieldHandler(m, proto2_f, h);
+ UPB_ASSERT_VAR(ok, ok);
if (upb_f->type() == UPB_TYPE_MESSAGE) {
- const goog::Message* prototype = GetPrototype(m, proto2_f);
+ const goog::Message* prototype = GetFieldPrototype(m, proto2_f);
assert(prototype);
const upb::Handlers* sub_handlers =
GetMaybeUnfrozenWriteHandlers(upb_f->message_subdef(), *prototype);
@@ -269,10 +280,5 @@ const Handlers* CodeCache::GetWriteHandlers(const goog::Message& m) {
return ret;
}
-upb::reffed_ptr<const upb::Handlers> NewWriteHandlers(const goog::Message& m) {
- CodeCache cache;
- return upb::reffed_ptr<const upb::Handlers>(cache.GetWriteHandlers(m));
-}
-
} // namespace googlepb
} // namespace upb
diff --git a/upb/bindings/googlepb/bridge.h b/upb/bindings/googlepb/bridge.h
index 9eed51b..9878247 100644
--- a/upb/bindings/googlepb/bridge.h
+++ b/upb/bindings/googlepb/bridge.h
@@ -63,13 +63,6 @@ namespace upb {
namespace googlepb {
-// Returns a upb::Handlers object that can be used to populate a proto2::Message
-// object of the same type as "m." For more control over handler caching and
-// reuse, instantiate a CodeCache object below.
-upb::reffed_ptr<const upb::Handlers> NewWriteHandlers(const proto2::Message& m);
-upb::reffed_ptr<const upb::Handlers> NewWriteHandlers(
- const ::google::protobuf::Message& m);
-
// Builds upb::Defs from proto2::Descriptors, and caches all built Defs for
// reuse. CodeCache (below) uses this internally; there is no need to use this
// class directly unless you only want Defs without corresponding Handlers.
@@ -159,6 +152,41 @@ class DefBuilder {
std::vector<Def*> to_freeze_;
};
+// Handlers to populate a proto2::Message with incoming data.
+class WriteHandlers {
+ public:
+ // Returns a upb::Handlers object that can be used to populate a
+ // proto2::Message object of the same type as "m." For more control over
+ // handler caching and reuse, instantiate a CodeCache object below.
+ static upb::reffed_ptr<const upb::Handlers> New(const proto2::Message& m);
+ static upb::reffed_ptr<const upb::Handlers> New(
+ const ::google::protobuf::Message& m);
+
+ // TODO(haberman): add an interface that takes a list of field paths,
+ // something like:
+ //
+ // // Returns a Handlers instance that will populate the given field paths
+ // // only, dropping data for all other field paths on the floor.
+ // static upb::reffed_ptr<const upb::Handlers> New(
+ // const proto2::Message& m,
+ // const std::vector<std::string>& paths);
+
+ // A lower-level interface with field granularity.
+ //
+ // Adds a handler to the given upb::Handlers for parsing the given field. If
+ // you only want to write certain fields into the proto2 message at parse
+ // time, call these methods ONLY for the fields you want to parse.
+ //
+ // The given field can be either a regular field or an extension, as long as
+ // its containing_type() matches this message.
+ static bool AddFieldHandler(const proto2::Message& m,
+ const proto2::FieldDescriptor* f,
+ upb::Handlers* h);
+ static bool AddFieldHandler(const ::google::protobuf::Message& m,
+ const ::google::protobuf::FieldDescriptor* f,
+ upb::Handlers* h);
+};
+
// Builds and caches upb::Handlers for populating proto2 generated classes.
//
// This class is NOT thread-safe.
@@ -199,6 +227,29 @@ class CodeCache {
std::vector<Handlers*> to_freeze_;
};
+// Functions for getting prototypes; these are only necessary if you are
+// building handlers manually, field by field.
+
+// Given a message and a field descriptor for that message, returns a prototype
+// for the submessage. Requires that this is a submessage field or a weak
+// field.
+const proto2::Message* GetFieldPrototype(const proto2::Message& m,
+ const proto2::FieldDescriptor* f);
+const ::google::protobuf::Message* GetFieldPrototype(
+ const ::google::protobuf::Message& m,
+ const ::google::protobuf::FieldDescriptor* f);
+
+// Given a message and a field descriptor for that message, returns a prototype
+// for the submessage, or NULL if this is not a submessage field or a weak
+// field. If this returns non-NULL even though the descriptor's type is not a
+// submessage, then this is a weak field. If you don't know what a weak field
+// is, you are probably not using one.
+const proto2::Message* TryGetFieldPrototype(const proto2::Message& m,
+ const proto2::FieldDescriptor* f);
+const ::google::protobuf::Message* TryGetFieldPrototype(
+ const ::google::protobuf::Message& m,
+ const ::google::protobuf::FieldDescriptor* f);
+
} // namespace googlepb
} // namespace upb
diff --git a/upb/bindings/googlepb/proto1.cc b/upb/bindings/googlepb/proto1.cc
index c317cdf..0b46fed 100644
--- a/upb/bindings/googlepb/proto1.cc
+++ b/upb/bindings/googlepb/proto1.cc
@@ -16,13 +16,20 @@
// dynamic_cast<> in this file:
// https://groups.google.com/a/google.com/d/msg/c-style/7Zp_XCX0e7s/I6dpzno4l-MJ
-#include "upb/bindings/googlepb/proto1.h"
+#include "upb/bindings/googlepb/proto1.int.h"
#include <memory>
+// TEMPORARY measure until we update the friend declarations in proto1.
+// Can't do in a single CL because of components.
+#define private public
+#define protected public
#include "net/proto2/public/repeated_field.h"
#include "net/proto/internal_layout.h"
#include "net/proto/proto2_reflection.h"
+#undef private
+#undef protected
+
#include "upb/def.h"
#include "upb/handlers.h"
#include "upb/shim/shim.h"
@@ -36,7 +43,7 @@ template <class T> static T* GetPointer(void* message, size_t offset) {
}
namespace upb {
-namespace google {
+namespace googlepb {
class P2R_Handlers {
public:
@@ -466,18 +473,18 @@ class P2R_Handlers {
bool TrySetProto1WriteHandlers(const proto2::FieldDescriptor* proto2_f,
const proto2::Message& m,
const upb::FieldDef* upb_f, upb::Handlers* h) {
- return P2R_Handlers::TrySet(proto2_f, m, upb_f, h);
-}
-
-const proto2::Message* GetProto1WeakPrototype(
- const proto2::Message& m, const proto2::FieldDescriptor* f) {
- return P2R_Handlers::GetWeakPrototype(m, f);
+ return googlepb::P2R_Handlers::TrySet(proto2_f, m, upb_f, h);
}
const proto2::Message* GetProto1FieldPrototype(
const proto2::Message& m, const proto2::FieldDescriptor* f) {
- return P2R_Handlers::GetFieldPrototype(m, f);
+ const proto2::Message *weak = googlepb::P2R_Handlers::GetWeakPrototype(m, f);
+ if (weak) return weak;
+ if (f->cpp_type() != proto2::FieldDescriptor::CPPTYPE_MESSAGE) {
+ return NULL;
+ }
+ return googlepb::P2R_Handlers::GetFieldPrototype(m, f);
}
-} // namespace google
+} // namespace googlepb
} // namespace upb
diff --git a/upb/bindings/googlepb/proto1.h b/upb/bindings/googlepb/proto1.int.h
index eb550ac..64ebb2f 100644
--- a/upb/bindings/googlepb/proto1.h
+++ b/upb/bindings/googlepb/proto1.int.h
@@ -7,8 +7,7 @@
// Support for registering field handlers that can write into a legacy proto1
// message. This functionality is only needed inside Google.
//
-// This is a low-level interface; the high-level interface in google.h is
-// more user-friendly.
+// This is an internal-only interface.
#ifndef UPB_GOOGLE_PROTO1_H_
#define UPB_GOOGLE_PROTO1_H_
@@ -24,7 +23,7 @@ class Handlers;
}
namespace upb {
-namespace google {
+namespace googlepb {
// Sets field handlers in the given Handlers object for writing to a single
// field (as described by "proto2_f" and "upb_f") into a message constructed
@@ -35,17 +34,12 @@ bool TrySetProto1WriteHandlers(const proto2::FieldDescriptor* proto2_f,
const proto2::Message& prototype,
const upb::FieldDef* upb_f, upb::Handlers* h);
-// Returns a prototype for the given field in "m", if it is weak. The returned
-// message could be the linked-in message type or OpaqueMessage, if the weak
-// message is *not* linked in. Otherwise returns NULL.
-const proto2::Message* GetProto1WeakPrototype(const proto2::Message& m,
- const proto2::FieldDescriptor* f);
-
-// Returns a prototype for the given non-weak field in "m".
+// Returns a prototype for the given this (possibly-weak) field. Returns NULL
+// if this is not a submessage field of any kind (weak or no).
const proto2::Message* GetProto1FieldPrototype(
const proto2::Message& m, const proto2::FieldDescriptor* f);
-} // namespace google
+} // namespace googlepb
} // namespace upb
#endif // UPB_GOOGLE_PROTO1_H_
diff --git a/upb/bindings/googlepb/proto2.cc b/upb/bindings/googlepb/proto2.cc
index 04e504b..224e2b9 100644
--- a/upb/bindings/googlepb/proto2.cc
+++ b/upb/bindings/googlepb/proto2.cc
@@ -13,10 +13,12 @@
// and protobuf opensource both in a single binary without the two conflicting.
// However we must be careful not to violate the ODR.
-#include "upb/bindings/googlepb/proto2.h"
+#include "upb/bindings/googlepb/proto2.int.h"
+
+#include <map>
#include "upb/def.h"
-#include "upb/bindings/googlepb/proto1.h"
+#include "upb/bindings/googlepb/proto1.int.h"
#include "upb/handlers.h"
#include "upb/shim/shim.h"
#include "upb/sink.h"
@@ -237,10 +239,26 @@ case goog::FieldDescriptor::cpptype: \
return (r->has_bits_offset_ * 8) + f->index();
}
+#ifdef GOOGLE_PROTOBUF_HAS_ONEOF
+ static size_t GetOneofDiscriminantOffset(
+ const goog::FieldDescriptor* f,
+ const goog::internal::GeneratedMessageReflection* r) {
+ assert(f->containing_oneof());
+ return r->oneof_case_offset_ + f->containing_oneof()->index();
+ }
+#endif
+
static uint16_t GetOffset(
const goog::FieldDescriptor* f,
const goog::internal::GeneratedMessageReflection* r) {
- return r->offsets_[f->index()];
+ int index = f->index();
+#ifdef GOOGLE_PROTOBUF_HAS_ONEOF
+ if (f->containing_oneof()) {
+ index =
+ f->containing_type()->field_count() + f->containing_oneof()->index();
+ }
+#endif
+ return r->offsets_[index];
}
class FieldOffset {
@@ -274,6 +292,154 @@ case goog::FieldDescriptor::cpptype: \
int8_t mask_;
};
+#ifdef GOOGLE_PROTOBUF_HAS_ONEOF
+ class OneofFieldData {
+ public:
+ OneofFieldData(const goog::FieldDescriptor* f,
+ const goog::internal::GeneratedMessageReflection* r)
+ : field_number_offset_(GetOneofDiscriminantOffset(f, r)),
+ field_number_(f->number()) {
+ const goog::OneofDescriptor* oneof = f->containing_oneof();
+
+ // Determine the type of each discriminant value, so we know what kind of
+ // value to delete if we are changing the type.
+ //
+ // For example, we may find that the oneof has three possible values: an
+ // int32, a message, and a string. For the int32 there is nothing to
+ // delete, but the message and the string need to be deleted when we
+ // switch to another oneof type, to avoid leaking it.
+ //
+ // TODO(haberman): share this map of types between all fields in the
+ // oneof. Right now we duplicate it for each one, which is wasteful.
+ for (int i = 0; i < oneof->field_count(); i++) {
+ const goog::FieldDescriptor* oneof_f = oneof->field(i);
+ OneofType& type = types_[oneof_f->number()];
+
+ switch (oneof_f->cpp_type()) {
+ case goog::FieldDescriptor::CPPTYPE_STRING:
+ type = GetTypeForString(oneof_f);
+ break;
+ case goog::FieldDescriptor::CPPTYPE_MESSAGE:
+#ifdef UPB_GOOGLE3
+ if (oneof_f->options().lazy()) {
+ type = ONEOF_TYPE_LAZYFIELD;
+ break;
+ }
+#endif
+ type = ONEOF_TYPE_MESSAGE;
+ break;
+
+ default:
+ type = ONEOF_TYPE_NONE;
+ break;
+ }
+ }
+
+ // "0" indicates that the field is not set.
+ types_[0] = ONEOF_TYPE_NONE;
+ }
+
+ int32_t* GetFieldPointer(goog::Message* message) const {
+ return GetPointer<int32_t>(message, field_number_offset_);
+ }
+
+ // Returns whether this is different than the previous value of the
+ // field_number; this implies that the current value was freed (if
+ // necessary) and the caller should allocate a new instance.
+ bool SetOneofHas(goog::Message* m, const FieldOffset* ofs) const {
+ int32_t *field_number = GetFieldPointer(m);
+ if (*field_number == field_number_) {
+ return false;
+ } else {
+ switch (types_.at(*field_number)) {
+ case ONEOF_TYPE_NONE:
+ break;
+ case ONEOF_TYPE_STRING:
+ delete *ofs->GetFieldPointer<std::string*>(m);
+ break;
+ case ONEOF_TYPE_MESSAGE:
+ delete *ofs->GetFieldPointer<goog::Message*>(m);
+ break;
+#ifdef UPB_GOOGLE3
+ case ONEOF_TYPE_GLOBALSTRING:
+ delete *ofs->GetFieldPointer<string*>(m);
+ break;
+ case ONEOF_TYPE_CORD:
+ delete *ofs->GetFieldPointer<Cord*>(m);
+ break;
+ case ONEOF_TYPE_STRINGPIECE:
+ delete *ofs->GetFieldPointer<goog::internal::StringPieceField*>(m);
+ break;
+ case ONEOF_TYPE_LAZYFIELD:
+ delete *ofs->GetFieldPointer<goog::internal::LazyField*>(m);
+ break;
+#endif
+ }
+ *field_number = field_number_;
+ return true;
+ }
+ }
+
+ private:
+ enum OneofType {
+ ONEOF_TYPE_NONE,
+ ONEOF_TYPE_STRING,
+ ONEOF_TYPE_MESSAGE,
+#ifdef UPB_GOOGLE3
+ ONEOF_TYPE_GLOBALSTRING,
+ ONEOF_TYPE_CORD,
+ ONEOF_TYPE_STRINGPIECE,
+ ONEOF_TYPE_LAZYFIELD,
+#endif
+ };
+
+ OneofType GetTypeForString(const goog::FieldDescriptor* f) {
+ switch (f->options().ctype()) {
+ case goog::FieldOptions::STRING:
+#ifdef UPB_GOOGLE3
+ return ONEOF_TYPE_GLOBALSTRING;
+#else
+ return ONEOF_TYPE_STRING;
+#endif
+
+#ifdef UPB_GOOGLE3
+ case goog::FieldOptions::CORD:
+ return ONEOF_TYPE_CORD;
+ case goog::FieldOptions::STRING_PIECE:
+ return ONEOF_TYPE_STRINGPIECE;
+#endif
+ default:
+ assert(false);
+ return ONEOF_TYPE_NONE;
+ }
+ }
+
+ // Offset of the uint32 that specifies which field is set.
+ size_t field_number_offset_;
+
+ // Field number for this field.
+ int32_t field_number_;
+
+ // The types of the oneof fields, indexed by field_number_.
+ std::map<int32_t, OneofType> types_;
+ };
+
+ class OneofFieldHandlerData : public FieldOffset {
+ public:
+ OneofFieldHandlerData(const goog::FieldDescriptor* f,
+ const goog::internal::GeneratedMessageReflection* r)
+ : FieldOffset(f, r),
+ oneof_data_(f, r) {}
+
+ bool SetOneofHas(goog::Message* message) const {
+ return oneof_data_.SetOneofHas(message, this);
+ }
+
+ public:
+ OneofFieldData oneof_data_;
+ };
+#endif // GOOGLE_PROTOBUF_HAS_ONEOF
+
class ExtensionFieldData {
public:
ExtensionFieldData(
@@ -352,7 +518,16 @@ case goog::FieldDescriptor::cpptype: \
CHKRET(h->SetValueHandler<T>(
f, UpbBindT(SetPrimitiveExtension<T>, data.release())));
}
- } else {
+ }
+#ifdef GOOGLE_PROTOBUF_HAS_ONEOF
+ else if (proto2_f->containing_oneof()) {
+ assert(!proto2_f->is_repeated());
+ CHKRET(h->SetValueHandler<T>(
+ f, UpbBindT(SetOneofPrimitive<T>,
+ new OneofFieldHandlerData(proto2_f, r))));
+ }
+#endif
+ else {
if (f->IsSequence()) {
SetStartRepeatedField<T>(proto2_f, r, f, h);
CHKRET(h->SetValueHandler<T>(f, UpbMakeHandlerT(AppendPrimitive<T>)));
@@ -383,6 +558,17 @@ case goog::FieldDescriptor::cpptype: \
val, set);
}
+#ifdef GOOGLE_PROTOBUF_HAS_ONEOF
+ template <typename T>
+ static void SetOneofPrimitive(goog::Message* m,
+ const OneofFieldHandlerData* data, T val) {
+ data->SetOneofHas(m);
+ const FieldOffset* ofs = data;
+ T* ptr = ofs->GetFieldPointer<T>(m);
+ *ptr = val;
+ }
+#endif
+
// Enum //////////////////////////////////////////////////////////////////////
class EnumHandlerData : public FieldOffset {
@@ -506,7 +692,15 @@ case goog::FieldDescriptor::cpptype: \
upb::Handlers* h) {
assert(!proto2_f->is_extension());
CHKRET(h->SetStringHandler(f, UpbMakeHandlerT(&OnStringBuf<T>)));
- if (f->IsSequence()) {
+#ifdef GOOGLE_PROTOBUF_HAS_ONEOF
+ if (proto2_f->containing_oneof()) {
+ assert(!f->IsSequence());
+ CHKRET(h->SetStartStringHandler(
+ f, UpbBindT(&StartOneofString<T>,
+ new OneofFieldHandlerData(proto2_f, r))));
+ } else
+#endif
+ if (f->IsSequence()) {
SetStartRepeatedPtrField<T>(proto2_f, r, f, h);
CHKRET(
h->SetStartStringHandler(f, UpbMakeHandlerT(StartRepeatedString<T>)));
@@ -545,6 +739,22 @@ case goog::FieldDescriptor::cpptype: \
return str;
}
+#ifdef GOOGLE_PROTOBUF_HAS_ONEOF
+ template <typename T>
+ static T* StartOneofString(goog::Message* m,
+ const OneofFieldHandlerData* data,
+ size_t size_hint) {
+ const FieldOffset* ofs = data;
+ T** str = ofs->GetFieldPointer<T*>(m);
+ if (data->SetOneofHas(m)) {
+ *str = new T();
+ } else {
+ (*str)->clear();
+ }
+ return *str;
+ }
+#endif
+
// StringExtension ///////////////////////////////////////////////////////////
template <typename T>
@@ -598,6 +808,24 @@ case goog::FieldDescriptor::cpptype: \
const goog::Message* const prototype_;
};
+#ifdef GOOGLE_PROTOBUF_HAS_ONEOF
+ class OneofSubMessageHandlerData : public SubMessageHandlerData {
+ public:
+ OneofSubMessageHandlerData(const goog::FieldDescriptor* f,
+ const goog::internal::GeneratedMessageReflection* r,
+ const goog::Message* prototype)
+ : SubMessageHandlerData(f, r, prototype),
+ oneof_data_(f, r) {}
+
+ bool SetOneofHas(goog::Message* m) const {
+ return oneof_data_.SetOneofHas(m, this);
+ }
+
+ private:
+ OneofFieldData oneof_data_;
+ };
+#endif
+
static void SetSubMessageHandlers(
const goog::FieldDescriptor* proto2_f, const goog::Message& m,
const goog::internal::GeneratedMessageReflection* r,
@@ -605,7 +833,15 @@ case goog::FieldDescriptor::cpptype: \
const goog::Message* field_prototype = GetFieldPrototype(m, proto2_f);
scoped_ptr<SubMessageHandlerData> data(
new SubMessageHandlerData(proto2_f, r, field_prototype));
- if (f->IsSequence()) {
+#ifdef GOOGLE_PROTOBUF_HAS_ONEOF
+ if (proto2_f->containing_oneof()) {
+ assert(!f->IsSequence());
+ CHKRET(h->SetStartSubMessageHandler(
+ f, UpbBind(StartOneofSubMessage, new OneofSubMessageHandlerData(
+ proto2_f, r, field_prototype))));
+ } else
+#endif
+ if (f->IsSequence()) {
SetStartRepeatedSubmessageField(proto2_f, r, f, h);
CHKRET(h->SetStartSubMessageHandler(
f, UpbBind(StartRepeatedSubMessage, data.release())));
@@ -649,6 +885,18 @@ case goog::FieldDescriptor::cpptype: \
return submsg;
}
+#ifdef GOOGLE_PROTOBUF_HAS_ONEOF
+ static goog::Message* StartOneofSubMessage(
+ goog::Message* m, const OneofSubMessageHandlerData* data) {
+ const FieldOffset* ofs = data;
+ goog::Message** subm = ofs->GetFieldPointer<goog::Message*>(m);
+ if (data->SetOneofHas(m)) {
+ *subm = data->prototype()->New();
+ }
+ return *subm;
+ }
+#endif
+
// SubMessageExtension ///////////////////////////////////////////////////////
class SubMessageExtensionHandlerData : public ExtensionFieldData {
@@ -979,7 +1227,7 @@ case goog::FieldDescriptor::cpptype: \
};
namespace upb {
-namespace google {
+namespace googlepb {
bool TrySetWriteHandlers(const goog::FieldDescriptor* proto2_f,
const goog::Message& prototype,
@@ -987,10 +1235,13 @@ bool TrySetWriteHandlers(const goog::FieldDescriptor* proto2_f,
return me::GMR_Handlers::TrySet(proto2_f, prototype, upb_f, h);
}
-const goog::Message* GetFieldPrototype(const goog::Message& m,
- const goog::FieldDescriptor* f) {
+const goog::Message* GetProto2FieldPrototype(const goog::Message& m,
+ const goog::FieldDescriptor* f) {
+ if (f->cpp_type() != goog::FieldDescriptor::CPPTYPE_MESSAGE) {
+ return NULL;
+ }
return me::GMR_Handlers::GetFieldPrototype(m, f);
}
-} // namespace google
+} // namespace googlepb
} // namespace upb
diff --git a/upb/bindings/googlepb/proto2.h b/upb/bindings/googlepb/proto2.int.h
index 516b7fd..5ce44c4 100644
--- a/upb/bindings/googlepb/proto2.h
+++ b/upb/bindings/googlepb/proto2.int.h
@@ -8,8 +8,7 @@
// message that uses GeneratedMessageReflection (which includes all messages
// generated by the proto2 compiler as well as DynamicMessage).
//
-// This is a low-level interface; the high-level interface in google.h is
-// more user-friendly.
+// This is an internal-only interface.
#ifndef UPB_GOOGLE_PROTO2_H_
#define UPB_GOOGLE_PROTO2_H_
@@ -32,7 +31,7 @@ class Handlers;
}
namespace upb {
-namespace google {
+namespace googlepb {
// Sets field handlers in the given Handlers object for writing to a single
// field (as described by "proto2_f" and "upb_f") into a message constructed
@@ -46,16 +45,15 @@ bool TrySetWriteHandlers(const ::google::protobuf::FieldDescriptor* proto2_f,
const ::google::protobuf::Message& prototype,
const upb::FieldDef* upb_f, upb::Handlers* h);
-// Returns a prototype for the given field in "m", if it is weak. The returned
-// message could be the linked-in message type or OpaqueMessage, if the weak
-// message is *not* linked in. Otherwise returns NULL.
-const proto2::Message* GetFieldPrototype(const proto2::Message& m,
- const proto2::FieldDescriptor* f);
-const ::google::protobuf::Message* GetFieldPrototype(
+// Returns a prototype for the given field in "m", if the given message uses
+// GeneratedMessageReflection. Otherwise returns NULL.
+const proto2::Message* GetProto2FieldPrototype(
+ const proto2::Message& m, const proto2::FieldDescriptor* f);
+const ::google::protobuf::Message* GetProto2FieldPrototype(
const ::google::protobuf::Message& m,
const ::google::protobuf::FieldDescriptor* f);
-} // namespace google
+} // namespace googlepb
} // namespace upb
#endif // UPB_GOOGLE_PROTO2_H_
diff --git a/upb/bindings/lua/table.c b/upb/bindings/lua/table.c
index 51ba324..febadf0 100644
--- a/upb/bindings/lua/table.c
+++ b/upb/bindings/lua/table.c
@@ -25,6 +25,7 @@
#include "lauxlib.h"
#include "upb/bindings/lua/upb.h"
#include "upb/def.h"
+#include "upb/symtab.h"
static void lupbtable_setnum(lua_State *L, int tab, const char *key,
lua_Number val) {
@@ -141,6 +142,12 @@ static int lupbtable_enumdef_ntoi(lua_State *L) {
return 1;
}
+static int lupbtable_symtab_symtab(lua_State *L) {
+ const upb_symtab *s = lupb_symtab_check(L, 1);
+ lupbtable_pushstrtable(L, &s->symtab);
+ return 1;
+}
+
static void lupbtable_setfieldi(lua_State *L, const char *field, int i) {
lua_pushnumber(L, i);
lua_setfield(L, -2, field);
@@ -151,11 +158,15 @@ static const struct luaL_Reg lupbtable_toplevel_m[] = {
{"msgdef_ntof", lupbtable_msgdef_ntof},
{"enumdef_iton", lupbtable_enumdef_iton},
{"enumdef_ntoi", lupbtable_enumdef_ntoi},
+ {"symtab_symtab", lupbtable_symtab_symtab},
{NULL, NULL}
};
int luaopen_upbtable(lua_State *L) {
- lupb_newlib(L, "upb.table", lupbtable_toplevel_m);
+ static char module_key;
+ if (lupb_openlib(L, &module_key, "upb.table", lupbtable_toplevel_m)) {
+ return 1;
+ }
// We define these here because they are not public.
lupbtable_setfieldi(L, "CTYPE_PTR", UPB_CTYPE_PTR);
diff --git a/upb/bindings/lua/upb.c b/upb/bindings/lua/upb.c
index 022d689..dbeb937 100644
--- a/upb/bindings/lua/upb.c
+++ b/upb/bindings/lua/upb.c
@@ -6,6 +6,21 @@
*
* A Lua extension for upb. Exposes only the core library
* (sub-libraries are exposed in other extensions).
+ *
+ * 64-bit woes: Lua can only represent numbers of type lua_Number (which is
+ * double unless the user specifically overrides this). Doubles can represent
+ * the entire range of 64-bit integers, but lose precision once the integers are
+ * greater than 2^53.
+ *
+ * Lua 5.3 is adding support for integers, which will allow for 64-bit
+ * integers (which can be interpreted as signed or unsigned).
+ *
+ * LuaJIT supports 64-bit signed and unsigned boxed representations
+ * through its "cdata" mechanism, but this is not portable to regular Lua.
+ *
+ * Hopefully Lua 5.3 will come soon enough that we can either use Lua 5.3
+ * integer support or LuaJIT 64-bit cdata for users that need the entire
+ * domain of [u]int64 values.
*/
#include <float.h>
@@ -16,8 +31,15 @@
#include "upb/bindings/lua/upb.h"
#include "upb/handlers.h"
#include "upb/pb/glue.h"
+#include "upb/shim/shim.h"
+
+static const char upb_lua[] = {
+#include "upb/bindings/lua/upb.lua.h"
+};
// Lua metatable types.
+#define LUPB_MSG "lupb.msg"
+#define LUPB_ARRAY "lupb.array"
#define LUPB_MSGDEF "lupb.msgdef"
#define LUPB_ENUMDEF "lupb.enumdef"
#define LUPB_FIELDDEF "lupb.fielddef"
@@ -26,6 +48,15 @@
// Other table constants.
#define LUPB_OBJCACHE "lupb.objcache"
+static void lupb_msgdef_init(lua_State *L);
+static size_t lupb_msgdef_sizeof();
+
+/* Lua compatibility code *****************************************************/
+
+// Lua 5.1 and Lua 5.2 have slightly incompatible APIs. A little bit of
+// compatibility code can help hide the difference. Not too many people still
+// use Lua 5.1 but LuaJIT uses the Lua 5.1 API in some ways.
+
#if LUA_VERSION_NUM == 501
// Taken from Lua 5.2's source.
@@ -43,6 +74,12 @@ void *luaL_testudata(lua_State *L, int ud, const char *tname) {
return NULL; /* value is not a userdata with a metatable */
}
+static void lupb_newlib(lua_State *L, const char *name, const luaL_Reg *funcs) {
+ luaL_register(L, name, funcs);
+}
+
+#define lupb_setfuncs(L, l) luaL_register(L, NULL, l)
+
#elif LUA_VERSION_NUM == 502
int luaL_typerror(lua_State *L, int narg, const char *tname) {
@@ -51,11 +88,72 @@ int luaL_typerror(lua_State *L, int narg, const char *tname) {
return luaL_argerror(L, narg, msg);
}
+static void lupb_newlib(lua_State *L, const char *name, const luaL_Reg *funcs) {
+ // Lua 5.2 modules are not expected to set a global variable, so "name" is
+ // unused.
+ UPB_UNUSED(name);
+
+ // Can't use luaL_newlib(), because funcs is not the actual array.
+ // Could (micro-)optimize this a bit to count funcs for initial table size.
+ lua_createtable(L, 0, 8);
+ luaL_setfuncs(L, funcs, 0);
+}
+
+#define lupb_setfuncs(L, l) luaL_setfuncs(L, l, 0)
+
#else
#error Only Lua 5.1 and 5.2 are supported
#endif
-static const char *chkname(lua_State *L, int narg) {
+// Shims for upcoming Lua 5.3 functionality.
+bool lua_isinteger(lua_State *L, int argn) {
+ UPB_UNUSED(L);
+ UPB_UNUSED(argn);
+ return false;
+}
+
+
+/* Utility functions **********************************************************/
+
+// We store our module table in the registry, keyed by ptr.
+// For more info about the motivation/rationale, see this thread:
+// http://thread.gmane.org/gmane.comp.lang.lua.general/110632
+bool lupb_openlib(lua_State *L, void *ptr, const char *name,
+ const luaL_Reg *funcs) {
+ // Lookup cached module table.
+ lua_pushlightuserdata(L, ptr);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (!lua_isnil(L, -1)) {
+ return true;
+ }
+
+ lupb_newlib(L, name, funcs);
+
+ // Save module table in cache.
+ lua_pushlightuserdata(L, ptr);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+
+ return false;
+}
+
+// Pushes a new userdata with the given metatable and ensures that it has a
+// uservalue.
+static void *newudata_with_userval(lua_State *L, size_t size, const char *type) {
+ void *ret = lua_newuserdata(L, size);
+
+ // Set metatable.
+ luaL_getmetatable(L, type);
+ assert(!lua_isnil(L, -1)); // Should have been created by luaopen_upb.
+ lua_setmetatable(L, -2);
+
+ lua_newtable(L);
+ lua_setuservalue(L, -2);
+
+ return ret;
+}
+
+const char *lupb_checkname(lua_State *L, int narg) {
size_t len;
const char *name = luaL_checklstring(L, narg, &len);
if (strlen(name) != len)
@@ -63,65 +161,110 @@ static const char *chkname(lua_State *L, int narg) {
return name;
}
-static bool chkbool(lua_State *L, int narg, const char *type) {
+bool lupb_checkbool(lua_State *L, int narg) {
if (!lua_isboolean(L, narg)) {
- luaL_error(L, "%s must be true or false", type);
+ luaL_error(L, "must be true or false");
}
return lua_toboolean(L, narg);
}
-static bool streql(const char *a, const char *b) { return strcmp(a, b) == 0; }
+// Unlike luaL_checkstring(), this does not allow implicit conversion to string.
+void lupb_checkstring(lua_State *L, int narg) {
+ if (lua_type(L, narg) != LUA_TSTRING)
+ luaL_error(L, "Expected string");
+}
+
+// Unlike luaL_checkinteger, these do not implicitly convert from string or
+// round an existing double value. We allow floating-point input, but only if
+// the actual value is integral.
+#define INTCHECK(type, ctype) \
+ ctype lupb_check##type(lua_State *L, int narg) { \
+ if (lua_isinteger(L, narg)) { \
+ return lua_tointeger(L, narg); \
+ } \
+ \
+ /* Prevent implicit conversion from string. */ \
+ luaL_checktype(L, narg, LUA_TNUMBER); \
+ double n = lua_tonumber(L, narg); \
+ \
+ ctype i = (ctype)n; \
+ if ((double)i != n) { \
+ /* double -> ctype truncated or rounded. */ \
+ luaL_error(L, "number %f was not an integer or out of range for " #type, \
+ n); \
+ } \
+ return i; \
+ } \
+ void lupb_push##type(lua_State *L, ctype val) { \
+ /* TODO: push integer for Lua >= 5.3, 64-bit cdata for LuaJIT. */ \
+ /* This is lossy for some [u]int64 values, which isn't great, but */ \
+ /* crashing when we encounter these values seems worse. */ \
+ lua_pushnumber(L, val); \
+ }
+
+INTCHECK(int64, int64_t);
+INTCHECK(int32, int32_t);
+INTCHECK(uint64, uint64_t);
+INTCHECK(uint32, uint32_t);
-static uint32_t chkint32(lua_State *L, int narg, const char *name) {
- lua_Number n = lua_tonumber(L, narg);
- if (n > INT32_MAX || n < INT32_MIN || rint(n) != n)
- luaL_error(L, "Invalid %s", name);
- return n;
+double lupb_checkdouble(lua_State *L, int narg) {
+ // If we were being really hard-nosed here, we'd check whether the input was
+ // an integer that has no precise double representation. But doubles aren't
+ // generally expected to be exact like integers are, and worse this could
+ // cause data-dependent runtime errors: one run of the program could work fine
+ // because the integer calculations happened to be exactly representable in
+ // double, while the next could crash because of subtly different input.
+
+ luaL_checktype(L, narg, LUA_TNUMBER); // lua_tonumber() implicitly converts.
+ return lua_tonumber(L, narg);
}
-// Sets a fielddef default from the given Lua value.
-static void lupb_setdefault(lua_State *L, int narg, upb_fielddef *f) {
- if (upb_fielddef_type(f) == UPB_TYPE_BOOL) {
- upb_fielddef_setdefaultbool(f, chkbool(L, narg, "bool default"));
- } else {
- // Numeric type.
- lua_Number num = luaL_checknumber(L, narg);
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_INT32:
- case UPB_TYPE_ENUM:
- if (num > INT32_MAX || num < INT32_MIN || num != rint(num))
- luaL_error(L, "Cannot convert %f to 32-bit integer", num);
- upb_fielddef_setdefaultint32(f, num);
- break;
- case UPB_TYPE_INT64:
- if (num > INT64_MAX || num < INT64_MIN || num != rint(num))
- luaL_error(L, "Cannot convert %f to 64-bit integer", num);
- upb_fielddef_setdefaultint64(f, num);
- break;
- case UPB_TYPE_UINT32:
- if (num > UINT32_MAX || num < 0 || num != rint(num))
- luaL_error(L, "Cannot convert %f to unsigned 32-bit integer", num);
- upb_fielddef_setdefaultuint32(f, num);
- break;
- case UPB_TYPE_UINT64:
- if (num > UINT64_MAX || num < 0 || num != rint(num))
- luaL_error(L, "Cannot convert %f to unsigned 64-bit integer", num);
- upb_fielddef_setdefaultuint64(f, num);
- break;
- case UPB_TYPE_DOUBLE:
- if (num > DBL_MAX || num < -DBL_MAX) {
- // This could happen if lua_Number was long double.
- luaL_error(L, "Cannot convert %f to double", num);
- }
- upb_fielddef_setdefaultdouble(f, num);
- break;
- case UPB_TYPE_FLOAT:
- if (num > FLT_MAX || num < -FLT_MAX)
- luaL_error(L, "Cannot convert %f to float", num);
- upb_fielddef_setdefaultfloat(f, num);
- break;
- default: luaL_error(L, "invalid type");
- }
+float lupb_checkfloat(lua_State *L, int narg) {
+ // We don't worry about checking whether the input can be exactly converted to
+ // float -- see above.
+
+ luaL_checktype(L, narg, LUA_TNUMBER); // lua_tonumber() implicitly converts.
+ return lua_tonumber(L, narg);
+}
+
+void lupb_pushdouble(lua_State *L, double d) {
+ lua_pushnumber(L, d);
+}
+
+void lupb_pushfloat(lua_State *L, float d) {
+ lua_pushnumber(L, d);
+}
+
+static void lupb_checkval(lua_State *L, int narg, upb_fieldtype_t type) {
+ switch(type) {
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_ENUM:
+ lupb_checkint32(L, narg);
+ break;
+ case UPB_TYPE_INT64:
+ lupb_checkint64(L, narg);
+ break;
+ case UPB_TYPE_UINT32:
+ lupb_checkuint32(L, narg);
+ break;
+ case UPB_TYPE_UINT64:
+ lupb_checkuint64(L, narg);
+ break;
+ case UPB_TYPE_DOUBLE:
+ lupb_checkdouble(L, narg);
+ break;
+ case UPB_TYPE_FLOAT:
+ lupb_checkfloat(L, narg);
+ break;
+ case UPB_TYPE_BOOL:
+ lupb_checkbool(L, narg);
+ break;
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ lupb_checkstring(L, narg);
+ break;
+ case UPB_TYPE_MESSAGE:
+ lupb_assert(L, false);
}
}
@@ -132,6 +275,13 @@ void lupb_checkstatus(lua_State *L, upb_status *s) {
}
}
+static upb_fieldtype_t lupb_checkfieldtype(lua_State *L, int narg) {
+ int type = luaL_checkint(L, narg);
+ if (!upb_fielddef_checktype(type))
+ luaL_argerror(L, narg, "invalid field type");
+ return type;
+}
+
#define CHK(pred) do { \
upb_status status = UPB_STATUS_INIT; \
pred; \
@@ -139,21 +289,28 @@ void lupb_checkstatus(lua_State *L, upb_status *s) {
} while (0)
-/* refcounted *****************************************************************/
+/* lupb_refcounted ************************************************************/
-// All upb objects that use upb_refcounted share a common Lua userdata
-// representation and a common scheme for caching Lua wrapper object. They do
-// however have different metatables. Objects are cached in a weak table
-// indexed by the C pointer of the object they are caching.
+// All upb objects that use upb_refcounted have a userdata that begins with a
+// pointer to that object. Each type has its own metatable. Objects are cached
+// in a weak table indexed by the C pointer of the object they are caching.
+//
+// Note that we consistently use memcpy() to read to/from the object. This
+// allows the userdata to use its own struct without violating aliasing, as
+// long as it begins with a pointer.
-typedef union {
- const upb_refcounted *refcounted;
- const upb_def *def;
- upb_symtab *symtab;
-} lupb_refcounted;
+// Checks type; if it matches, pulls the pointer out of the wrapper.
+void *lupb_refcounted_check(lua_State *L, int narg, const char *type) {
+ void *ud = luaL_checkudata(L, narg, type);
+ void *ret;
+ memcpy(&ret, ud, sizeof ret);
+ if (!ret) luaL_error(L, "called into dead object");
+ return ret;
+}
-static bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj,
- const char *type, const void *owner) {
+bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj,
+ const char *type, const void *ref_donor,
+ size_t size) {
if (obj == NULL) {
lua_pushnil(L);
return false;
@@ -161,30 +318,44 @@ static bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj,
// Lookup our cache in the registry (we don't put our objects in the registry
// directly because we need our cache to be a weak table).
- lupb_refcounted *ud = NULL;
lua_getfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE);
assert(!lua_isnil(L, -1)); // Should have been created by luaopen_upb.
lua_pushlightuserdata(L, (void*)obj);
lua_rawget(L, -2);
- // Stack: objcache, cached value.
- bool create = lua_isnil(L, -1) ||
- // A corner case: it is possible for the value to be GC'd
- // already, in which case we should evict this entry and create
- // a new one.
- ((lupb_refcounted*)lua_touserdata(L, -1))->refcounted == NULL;
+ // Stack is now: objcache, cached value.
+
+ bool create = false;
+
+ if (lua_isnil(L, -1)) {
+ create = true;
+ } else {
+ void *ud = lua_touserdata(L, -1);
+ lupb_assert(L, ud);
+ void *ud_obj;
+ memcpy(&ud_obj, ud, sizeof(void*));
+
+ // A corner case: it is possible for the value to be GC'd
+ // already, in which case we should evict this entry and create
+ // a new one.
+ if (ud_obj == NULL) {
+ create = true;
+ }
+ }
+
+ void *ud = NULL;
+
if (create) {
// Remove bad cached value and push new value.
lua_pop(L, 1);
- // We take advantage of the fact that all of our objects are currently a
- // single pointer, and thus have the same layout.
- // TODO: this probably violates aliasing.
- ud = lua_newuserdata(L, sizeof(lupb_refcounted));
- ud->refcounted = obj;
- upb_refcounted_donateref(obj, owner, ud);
+ // All of our userdata begin with a pointer to the obj.
+ ud = lua_newuserdata(L, size);
+ memcpy(ud, &obj, sizeof(void*));
+ upb_refcounted_donateref(obj, ref_donor, ud);
luaL_getmetatable(L, type);
- assert(!lua_isnil(L, -1)); // Should have been created by luaopen_upb.
+ // Should have been created by luaopen_upb.
+ lupb_assert(L, !lua_isnil(L, -1));
lua_setmetatable(L, -2);
// Set it in the cache.
@@ -195,67 +366,111 @@ static bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj,
// Existing wrapper obj already has a ref.
ud = lua_touserdata(L, -1);
upb_refcounted_checkref(obj, ud);
- if (owner)
- upb_refcounted_unref(obj, owner);
+ if (ref_donor)
+ upb_refcounted_unref(obj, ref_donor);
}
+
lua_insert(L, -2);
lua_pop(L, 1);
return create;
}
-static void lupb_refcounted_pushnewrapper(lua_State *L, upb_refcounted *obj,
- const char *type, const void *owner) {
- bool created = lupb_refcounted_pushwrapper(L, obj, type, owner);
+void lupb_refcounted_pushnewrapper(lua_State *L, const upb_refcounted *obj,
+ const char *type, const void *ref_donor) {
+ bool created =
+ lupb_refcounted_pushwrapper(L, obj, type, ref_donor, sizeof(void *));
UPB_ASSERT_VAR(created, created == true);
}
+static int lupb_refcounted_gc(lua_State *L) {
+ void *ud = lua_touserdata(L, 1);
+ upb_refcounted *obj;
+ memcpy(&obj, ud, sizeof obj);
+ upb_refcounted_unref(obj, ud);
+
+ // Zero out pointer so we can detect a call into a GC'd object.
+ void *nullp = NULL;
+ memcpy(ud, &nullp, sizeof nullp);
+
+ return 0;
+}
+
+static const struct luaL_Reg lupb_refcounted_mm[] = {
+ {"__gc", lupb_refcounted_gc},
+ {NULL, NULL}
+};
+
/* lupb_def *******************************************************************/
static const upb_def *lupb_def_check(lua_State *L, int narg) {
- lupb_refcounted *r = luaL_testudata(L, narg, LUPB_MSGDEF);
- if (!r) r = luaL_testudata(L, narg, LUPB_ENUMDEF);
- if (!r) r = luaL_testudata(L, narg, LUPB_FIELDDEF);
- if (!r) luaL_typerror(L, narg, "upb def");
- if (!r->refcounted) luaL_error(L, "called into dead def");
- return r->def;
+ void *ud = luaL_testudata(L, narg, LUPB_MSGDEF);
+ if (!ud) ud = luaL_testudata(L, narg, LUPB_ENUMDEF);
+ if (!ud) ud = luaL_testudata(L, narg, LUPB_FIELDDEF);
+ if (!ud) luaL_typerror(L, narg, "upb def");
+
+ upb_def *ret;
+ memcpy(&ret, ud, sizeof ret);
+ if (!ret) luaL_error(L, "called into dead object");
+ return ret;
}
static upb_def *lupb_def_checkmutable(lua_State *L, int narg) {
const upb_def *def = lupb_def_check(L, narg);
if (upb_def_isfrozen(def))
- luaL_typerror(L, narg, "not allowed on frozen value");
+ luaL_error(L, "not allowed on frozen value");
return (upb_def*)def;
}
-bool lupb_def_pushwrapper(lua_State *L, const upb_def *def, const void *owner) {
+bool lupb_def_pushwrapper(lua_State *L, const upb_def *def,
+ const void *ref_donor) {
if (def == NULL) {
lua_pushnil(L);
return false;
}
const char *type = NULL;
- switch (def->type) {
- case UPB_DEF_MSG: type = LUPB_MSGDEF; break;
+ size_t size = sizeof(void*);
+
+ switch (upb_def_type(def)) {
+ case UPB_DEF_MSG: {
+ type = LUPB_MSGDEF;
+ size = lupb_msgdef_sizeof();
+ break;
+ }
case UPB_DEF_ENUM: type = LUPB_ENUMDEF; break;
case UPB_DEF_FIELD: type = LUPB_FIELDDEF; break;
default: luaL_error(L, "unknown deftype %d", def->type);
}
- return lupb_refcounted_pushwrapper(L, UPB_UPCAST(def), type, owner);
+
+ bool created =
+ lupb_refcounted_pushwrapper(L, UPB_UPCAST(def), type, ref_donor, size);
+
+ if (created && upb_def_type(def) == UPB_DEF_MSG) {
+ lupb_msgdef_init(L);
+ }
+
+ return created;
}
void lupb_def_pushnewrapper(lua_State *L, const upb_def *def,
- const void *owner) {
- bool created = lupb_def_pushwrapper(L, def, owner);
+ const void *ref_donor) {
+ bool created = lupb_def_pushwrapper(L, def, ref_donor);
UPB_ASSERT_VAR(created, created == true);
}
static int lupb_def_type(lua_State *L) {
const upb_def *def = lupb_def_check(L, 1);
- lua_pushnumber(L, upb_def_type(def));
+ lua_pushinteger(L, upb_def_type(def));
return 1;
}
+static int lupb_def_freeze(lua_State *L) {
+ upb_def *def = lupb_def_checkmutable(L, 1);
+ CHK(upb_def_freeze(&def, 1, &status));
+ return 0;
+}
+
static int lupb_def_isfrozen(lua_State *L) {
const upb_def *def = lupb_def_check(L, 1);
lua_pushboolean(L, upb_def_isfrozen(def));
@@ -269,234 +484,49 @@ static int lupb_def_fullname(lua_State *L) {
}
static int lupb_def_setfullname(lua_State *L) {
- CHK(upb_def_setfullname(lupb_def_checkmutable(L, 1), chkname(L, 2), &status));
+ const char *name = lupb_checkname(L, 2);
+ CHK(upb_def_setfullname(lupb_def_checkmutable(L, 1), name, &status));
return 0;
}
#define LUPB_COMMON_DEF_METHODS \
{"def_type", lupb_def_type}, \
{"full_name", lupb_def_fullname}, \
+ {"freeze", lupb_def_freeze}, \
{"is_frozen", lupb_def_isfrozen}, \
{"set_full_name", lupb_def_setfullname}, \
/* lupb_fielddef **************************************************************/
-static const upb_fielddef *lupb_fielddef_check(lua_State *L, int narg) {
- lupb_refcounted *r = luaL_checkudata(L, narg, LUPB_FIELDDEF);
- if (!r) luaL_typerror(L, narg, "upb fielddef");
- if (!r->refcounted) luaL_error(L, "called into dead fielddef");
- return upb_downcast_fielddef(r->def);
+const upb_fielddef *lupb_fielddef_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_FIELDDEF);
}
static upb_fielddef *lupb_fielddef_checkmutable(lua_State *L, int narg) {
const upb_fielddef *f = lupb_fielddef_check(L, narg);
if (upb_fielddef_isfrozen(f))
- luaL_typerror(L, narg, "not allowed on frozen value");
+ luaL_error(L, "not allowed on frozen value");
return (upb_fielddef*)f;
}
-// Setter functions; these are called by both the constructor and the individual
-// setter API calls like field:set_type().
-
-static void lupb_fielddef_dosetdefault(lua_State *L, upb_fielddef *f,
- int narg) {
- int type = lua_type(L, narg);
- upb_fieldtype_t upbtype = upb_fielddef_type(f);
- if (type == LUA_TSTRING) {
- if (!upb_fielddef_isstring(f) && upbtype != UPB_TYPE_ENUM)
- luaL_argerror(L, narg, "field does not expect a string default");
- size_t len;
- const char *str = lua_tolstring(L, narg, &len);
- CHK(upb_fielddef_setdefaultstr(f, str, len, &status));
- } else {
- lupb_setdefault(L, narg, f);
- }
-}
-
-static void lupb_fielddef_dosetisextension(lua_State *L, upb_fielddef *f,
- int narg) {
- CHK(upb_fielddef_setisextension(f, chkbool(L, narg, "is_extension")));
-}
-
-static void lupb_fielddef_dosetlabel(lua_State *L, upb_fielddef *f, int narg) {
- int label = luaL_checkint(L, narg);
- if (!upb_fielddef_checklabel(label))
- luaL_argerror(L, narg, "invalid field label");
- upb_fielddef_setlabel(f, label);
-}
-
-static void lupb_fielddef_dosetlazy(lua_State *L, upb_fielddef *f, int narg) {
- upb_fielddef_setlazy(f, chkbool(L, narg, "lazy"));
-}
-
-static void lupb_fielddef_dosetname(lua_State *L, upb_fielddef *f, int narg) {
- CHK(upb_fielddef_setname(f, chkname(L, narg), &status));
-}
-
-static void lupb_fielddef_dosetnumber(lua_State *L, upb_fielddef *f, int narg) {
- CHK(upb_fielddef_setnumber(f, luaL_checkint(L, narg), &status));
-}
-
-static void lupb_fielddef_dosetsubdef(lua_State *L, upb_fielddef *f, int narg) {
- const upb_def *def = NULL;
- if (!lua_isnil(L, narg))
- def = lupb_def_check(L, narg);
- CHK(upb_fielddef_setsubdef(f, def, &status));
-}
-
-static void lupb_fielddef_dosetsubdefname(lua_State *L, upb_fielddef *f,
- int narg) {
- const char *name = NULL;
- if (!lua_isnil(L, narg))
- name = chkname(L, narg);
- CHK(upb_fielddef_setsubdefname(f, name, &status));
-}
-
-static void lupb_fielddef_dosetcontainingtypename(lua_State *L, upb_fielddef *f,
- int narg) {
- const char *name = NULL;
- if (!lua_isnil(L, narg))
- name = chkname(L, narg);
- CHK(upb_fielddef_setcontainingtypename(f, name, &status));
-}
-
-static void lupb_fielddef_dosettype(lua_State *L, upb_fielddef *f, int narg) {
- int type = luaL_checkint(L, narg);
- if (!upb_fielddef_checktype(type))
- luaL_argerror(L, narg, "invalid field type");
- upb_fielddef_settype(f, type);
-}
-
-static void lupb_fielddef_dosetintfmt(lua_State *L, upb_fielddef *f, int narg) {
- int32_t intfmt = luaL_checknumber(L, narg);
- if (!upb_fielddef_checkintfmt(intfmt))
- luaL_argerror(L, narg, "invalid intfmt");
- upb_fielddef_setintfmt(f, intfmt);
-}
-
-static void lupb_fielddef_dosettagdelim(lua_State *L, upb_fielddef *f,
- int narg) {
- CHK(upb_fielddef_settagdelim(f, chkbool(L, narg, "tagdelim")));
-}
-
-// Setter API calls. These use the setter functions above.
-
-static int lupb_fielddef_setcontainingtypename(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetcontainingtypename(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setdefault(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetdefault(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setisextension(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetisextension(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setlabel(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetlabel(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setlazy(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetlazy(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setname(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetname(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setnumber(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetnumber(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setsubdef(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetsubdef(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setsubdefname(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetsubdefname(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_settype(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosettype(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setintfmt(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetintfmt(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_settagdelim(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosettagdelim(L, f, 2);
- return 0;
-}
-
-// Constructor and other methods.
-
static int lupb_fielddef_new(lua_State *L) {
upb_fielddef *f = upb_fielddef_new(&f);
- int narg = lua_gettop(L);
-
lupb_def_pushnewrapper(L, UPB_UPCAST(f), &f);
+ return 1;
+}
- if (narg == 0) return 1;
-
- // User can specify initialization values like so:
- // upb.FieldDef{label=upb.LABEL_REQUIRED, name="my_field", number=5,
- // type=upb.TYPE_INT32, default_value=12, type_name="Foo"}
- luaL_checktype(L, 1, LUA_TTABLE);
- for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
- luaL_checktype(L, -2, LUA_TSTRING);
- const char *key = lua_tostring(L, -2);
- int v = -1;
- if (streql(key, "name")) lupb_fielddef_dosetname(L, f, v);
- else if (streql(key, "number")) lupb_fielddef_dosetnumber(L, f, v);
- else if (streql(key, "type")) lupb_fielddef_dosettype(L, f, v);
- else if (streql(key, "label")) lupb_fielddef_dosetlabel(L, f, v);
- else if (streql(key, "lazy")) lupb_fielddef_dosetlazy(L, f, v);
- else if (streql(key, "is_extension"))
- lupb_fielddef_dosetisextension(L, f, v);
- else if (streql(key, "containing_type_name"))
- lupb_fielddef_dosetcontainingtypename(L, f, v);
- else if (streql(key, "default_value")) ; // Defer to second pass.
- else if (streql(key, "subdef")) ; // Defer to second pass.
- else if (streql(key, "subdef_name")) ; // Defer to second pass.
- else luaL_error(L, "Cannot set fielddef member '%s'", key);
- }
+// Getters
- // Have to do these in a second pass because these depend on the type, so we
- // have to make sure the type is set if the user specified one.
- for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
- const char *key = lua_tostring(L, -2);
- int v = -1;
- if (streql(key, "default_value")) lupb_fielddef_dosetdefault(L, f, v);
- else if (streql(key, "subdef")) lupb_fielddef_dosetsubdef(L, f, v);
- else if (streql(key, "subdef_name")) lupb_fielddef_dosetsubdefname(L, f, v);
- }
+static int lupb_fielddef_containingtype(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lupb_def_pushwrapper(L, UPB_UPCAST(upb_fielddef_containingtype(f)), NULL);
+ return 1;
+}
+static int lupb_fielddef_containingtypename(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ lua_pushstring(L, upb_fielddef_containingtypename(f));
return 1;
}
@@ -505,13 +535,13 @@ static int lupb_fielddef_default(lua_State *L) {
switch (upb_fielddef_type(f)) {
case UPB_TYPE_INT32:
int32:
- lua_pushnumber(L, upb_fielddef_defaultint32(f)); break;
+ lupb_pushint32(L, upb_fielddef_defaultint32(f)); break;
case UPB_TYPE_INT64:
- lua_pushnumber(L, upb_fielddef_defaultint64(f)); break;
+ lupb_pushint64(L, upb_fielddef_defaultint64(f)); break;
case UPB_TYPE_UINT32:
- lua_pushnumber(L, upb_fielddef_defaultuint32(f)); break;
+ lupb_pushuint32(L, upb_fielddef_defaultuint32(f)); break;
case UPB_TYPE_UINT64:
- lua_pushnumber(L, upb_fielddef_defaultuint64(f)); break;
+ lupb_pushuint64(L, upb_fielddef_defaultuint64(f)); break;
case UPB_TYPE_DOUBLE:
lua_pushnumber(L, upb_fielddef_defaultdouble(f)); break;
case UPB_TYPE_FLOAT:
@@ -519,11 +549,17 @@ static int lupb_fielddef_default(lua_State *L) {
case UPB_TYPE_BOOL:
lua_pushboolean(L, upb_fielddef_defaultbool(f)); break;
case UPB_TYPE_ENUM:
- if (!upb_fielddef_default_is_symbolic(f))
+ if (upb_fielddef_enumhasdefaultstr(f)) {
+ goto str;
+ } else if (upb_fielddef_enumhasdefaultint32(f)) {
goto int32;
- // Fallthrough.
+ } else {
+ lua_pushnil(L);
+ }
+ break;
case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES: {
+ case UPB_TYPE_BYTES:
+ str: {
size_t len;
const char *data = upb_fielddef_defaultstr(f, &len);
lua_pushlstring(L, data, len);
@@ -539,64 +575,68 @@ static int lupb_fielddef_getsel(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
upb_selector_t sel;
if (upb_handlers_getselector(f, luaL_checknumber(L, 2), &sel)) {
- lua_pushnumber(L, sel);
+ lua_pushinteger(L, sel);
return 1;
} else {
return 0;
}
}
-static int lupb_fielddef_label(lua_State *L) {
+static int lupb_fielddef_hassubdef(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushnumber(L, upb_fielddef_label(f));
+ lua_pushboolean(L, upb_fielddef_hassubdef(f));
return 1;
}
-static int lupb_fielddef_lazy(lua_State *L) {
+static int lupb_fielddef_index(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_lazy(f));
+ lua_pushinteger(L, upb_fielddef_index(f));
return 1;
}
-static int lupb_fielddef_name(lua_State *L) {
+static int lupb_fielddef_intfmt(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushstring(L, upb_fielddef_name(f));
+ lua_pushinteger(L, upb_fielddef_intfmt(f));
return 1;
}
-static int lupb_fielddef_number(lua_State *L) {
+static int lupb_fielddef_isextension(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- int32_t num = upb_fielddef_number(f);
- if (num)
- lua_pushnumber(L, num);
- else
- lua_pushnil(L);
+ lua_pushboolean(L, upb_fielddef_isextension(f));
return 1;
}
-static int lupb_fielddef_selectorbase(lua_State *L) {
+static int lupb_fielddef_istagdelim(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- if (!upb_fielddef_isfrozen(f))
- luaL_error(L, "_selectorbase is only defined for frozen fielddefs");
- lua_pushnumber(L, f->selector_base);
+ lua_pushboolean(L, upb_fielddef_istagdelim(f));
return 1;
}
-static int lupb_fielddef_hassubdef(lua_State *L) {
+static int lupb_fielddef_label(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_hassubdef(f));
+ lua_pushinteger(L, upb_fielddef_label(f));
return 1;
}
-static int lupb_fielddef_containingtype(lua_State *L) {
+static int lupb_fielddef_lazy(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lupb_def_pushwrapper(L, UPB_UPCAST(upb_fielddef_containingtype(f)), NULL);
+ lua_pushboolean(L, upb_fielddef_lazy(f));
return 1;
}
-static int lupb_fielddef_containingtypename(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lua_pushstring(L, upb_fielddef_containingtypename(f));
+static int lupb_fielddef_name(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushstring(L, upb_fielddef_name(f));
+ return 1;
+}
+
+static int lupb_fielddef_number(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ int32_t num = upb_fielddef_number(f);
+ if (num)
+ lua_pushinteger(L, num);
+ else
+ lua_pushnil(L);
return 1;
}
@@ -620,43 +660,147 @@ static int lupb_fielddef_subdefname(lua_State *L) {
static int lupb_fielddef_type(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
if (upb_fielddef_typeisset(f))
- lua_pushnumber(L, upb_fielddef_type(f));
+ lua_pushinteger(L, upb_fielddef_type(f));
else
lua_pushnil(L);
return 1;
}
-static int lupb_fielddef_index(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushnumber(L, upb_fielddef_index(f));
- return 1;
+// Setters
+
+static int lupb_fielddef_setcontainingtypename(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ const char *name = NULL;
+ if (!lua_isnil(L, 2))
+ name = lupb_checkname(L, 2);
+ CHK(upb_fielddef_setcontainingtypename(f, name, &status));
+ return 0;
}
-static int lupb_fielddef_intfmt(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushnumber(L, upb_fielddef_intfmt(f));
- return 1;
+static int lupb_fielddef_setdefault(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+
+ switch (upb_fielddef_type(f)) {
+ case UPB_TYPE_INT32:
+ upb_fielddef_setdefaultint32(f, lupb_checkint32(L, 2));
+ break;
+ case UPB_TYPE_INT64:
+ upb_fielddef_setdefaultint64(f, lupb_checkint64(L, 2));
+ break;
+ case UPB_TYPE_UINT32:
+ upb_fielddef_setdefaultuint32(f, lupb_checkuint32(L, 2));
+ break;
+ case UPB_TYPE_UINT64:
+ upb_fielddef_setdefaultuint64(f, lupb_checkuint64(L, 2));
+ break;
+ case UPB_TYPE_DOUBLE:
+ upb_fielddef_setdefaultdouble(f, lupb_checkdouble(L, 2));
+ break;
+ case UPB_TYPE_FLOAT:
+ upb_fielddef_setdefaultfloat(f, lupb_checkfloat(L, 2));
+ break;
+ case UPB_TYPE_BOOL:
+ upb_fielddef_setdefaultbool(f, lupb_checkbool(L, 2));
+ break;
+ case UPB_TYPE_MESSAGE:
+ return luaL_error(L, "Message types cannot have defaults.");
+ case UPB_TYPE_ENUM:
+ if (lua_type(L, 2) != LUA_TSTRING) {
+ upb_fielddef_setdefaultint32(f, lupb_checkint32(L, 2));
+ break;
+ }
+ // Else fall through and set string default.
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_STRING: {
+ size_t len;
+ const char *str = lua_tolstring(L, 2, &len);
+ CHK(upb_fielddef_setdefaultstr(f, str, len, &status));
+ }
+ }
+ return 0;
}
-static int lupb_fielddef_isextension(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_isextension(f));
- return 1;
+static int lupb_fielddef_setisextension(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ CHK(upb_fielddef_setisextension(f, lupb_checkbool(L, 2)));
+ return 0;
}
-static int lupb_fielddef_istagdelim(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_istagdelim(f));
- return 1;
+static int lupb_fielddef_setlabel(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ int label = luaL_checkint(L, 2);
+ if (!upb_fielddef_checklabel(label))
+ luaL_argerror(L, 2, "invalid field label");
+ upb_fielddef_setlabel(f, label);
+ return 0;
+}
+
+static int lupb_fielddef_setlazy(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ upb_fielddef_setlazy(f, lupb_checkbool(L, 2));
+ return 0;
+}
+
+static int lupb_fielddef_setname(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ CHK(upb_fielddef_setname(f, lupb_checkname(L, 2), &status));
+ return 0;
+}
+
+static int lupb_fielddef_setnumber(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ CHK(upb_fielddef_setnumber(f, luaL_checkint(L, 2), &status));
+ return 0;
+}
+
+static int lupb_fielddef_setsubdef(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ const upb_def *def = NULL;
+ if (!lua_isnil(L, 2))
+ def = lupb_def_check(L, 2);
+ CHK(upb_fielddef_setsubdef(f, def, &status));
+ return 0;
+}
+
+static int lupb_fielddef_setsubdefname(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ const char *name = NULL;
+ if (!lua_isnil(L, 2))
+ name = lupb_checkname(L, 2);
+ CHK(upb_fielddef_setsubdefname(f, name, &status));
+ return 0;
+}
+
+static int lupb_fielddef_settype(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ upb_fielddef_settype(f, lupb_checkfieldtype(L, 2));
+ return 0;
+}
+
+static int lupb_fielddef_setintfmt(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ int32_t intfmt = luaL_checknumber(L, 2);
+ if (!upb_fielddef_checkintfmt(intfmt))
+ luaL_argerror(L, 2, "invalid intfmt");
+ upb_fielddef_setintfmt(f, intfmt);
+ return 0;
}
-static int lupb_fielddef_gc(lua_State *L) {
- lupb_refcounted *r = luaL_checkudata(L, 1, LUPB_FIELDDEF);
- upb_def_unref(r->def, r);
- r->refcounted = NULL;
+static int lupb_fielddef_settagdelim(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ bool is_tag_delim = lupb_checkbool(L, 2);
+ CHK(upb_fielddef_settagdelim(f, is_tag_delim));
return 0;
}
+static int lupb_fielddef_selectorbase(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ if (!upb_fielddef_isfrozen(f))
+ luaL_error(L, "_selectorbase is only defined for frozen fielddefs");
+ lua_pushinteger(L, f->selector_base);
+ return 1;
+}
+
static const struct luaL_Reg lupb_fielddef_m[] = {
LUPB_COMMON_DEF_METHODS
@@ -696,82 +840,64 @@ static const struct luaL_Reg lupb_fielddef_m[] = {
{NULL, NULL}
};
-static const struct luaL_Reg lupb_fielddef_mm[] = {
- {"__gc", lupb_fielddef_gc},
- {NULL, NULL}
-};
-
/* lupb_msgdef ****************************************************************/
+typedef struct {
+ const upb_msgdef *md;
+
+ // These members are initialized lazily the first time a message is created
+ // for this def.
+ uint16_t *field_offsets;
+ size_t msg_size;
+ size_t hasbits_size;
+ lua_State *L;
+} lupb_msgdef;
+
+static size_t lupb_msgdef_sizeof() {
+ return sizeof(lupb_msgdef);
+}
+
const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg) {
- lupb_refcounted *r = luaL_checkudata(L, narg, LUPB_MSGDEF);
- if (!r) luaL_typerror(L, narg, LUPB_MSGDEF);
- if (!r->refcounted) luaL_error(L, "called into dead msgdef");
- return upb_downcast_msgdef(r->def);
+ return lupb_refcounted_check(L, narg, LUPB_MSGDEF);
+}
+
+lupb_msgdef *lupb_msgdef_check2(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_MSGDEF);
}
static upb_msgdef *lupb_msgdef_checkmutable(lua_State *L, int narg) {
const upb_msgdef *m = lupb_msgdef_check(L, narg);
if (upb_msgdef_isfrozen(m))
- luaL_typerror(L, narg, "not allowed on frozen value");
+ luaL_error(L, "not allowed on frozen value");
return (upb_msgdef*)m;
}
-static int lupb_msgdef_gc(lua_State *L) {
- lupb_refcounted *r = luaL_checkudata(L, 1, LUPB_MSGDEF);
- upb_def_unref(r->def, r);
- r->refcounted = NULL;
- return 0;
-}
-
static int lupb_msgdef_new(lua_State *L) {
- int narg = lua_gettop(L);
upb_msgdef *md = upb_msgdef_new(&md);
lupb_def_pushnewrapper(L, UPB_UPCAST(md), &md);
-
- if (narg == 0) return 1;
-
- // User can specify initialization values like so:
- // upb.MessageDef{full_name="MyMessage", extstart=8000, fields={...}}
- luaL_checktype(L, 1, LUA_TTABLE);
- for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
- luaL_checktype(L, -2, LUA_TSTRING);
- const char *key = lua_tostring(L, -2);
-
- if (streql(key, "full_name")) { // full_name="MyMessage"
- CHK(upb_def_setfullname(UPB_UPCAST(md), chkname(L, -1), &status));
- } else if (streql(key, "fields")) { // fields={...}
- // Iterate over the list of fields.
- luaL_checktype(L, -1, LUA_TTABLE);
- for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, -1);
- CHK(upb_msgdef_addfield(md, f, NULL, &status));
- }
- } else {
- // TODO: extrange=
- luaL_error(L, "Unknown initializer key '%s'", key);
- }
- }
return 1;
}
+// Unlike other refcounted types we need a custom __gc so that we free our field
+// offsets.
+static int lupb_msgdef_gc(lua_State *L) {
+ lupb_refcounted_gc(L);
+ lupb_msgdef *lmd = luaL_checkudata(L, -1, LUPB_MSGDEF);
+ free(lmd->field_offsets);
+ return 0;
+}
+
+static void lupb_msgdef_init(lua_State *L) {
+ lupb_msgdef *lmd = luaL_checkudata(L, -1, LUPB_MSGDEF);
+ lmd->L = L;
+ lmd->field_offsets = NULL;
+}
+
static int lupb_msgdef_add(lua_State *L) {
upb_msgdef *m = lupb_msgdef_checkmutable(L, 1);
- luaL_checktype(L, 2, LUA_TTABLE);
- int n = lua_rawlen(L, 2);
- // TODO: add upb interface that lets us avoid this malloc/free.
- upb_fielddef **fields = malloc(n * sizeof(upb_fielddef*));
- for (int i = 0; i < n; i++) {
- lua_rawgeti(L, -1, i + 1);
- fields[i] = lupb_fielddef_checkmutable(L, -1);
- lua_pop(L, 1);
- }
-
- upb_status status = UPB_STATUS_INIT;
- upb_msgdef_addfields(m, fields, n, NULL, &status);
- free(fields);
- lupb_checkstatus(L, &status);
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 2);
+ CHK(upb_msgdef_addfield(m, f, NULL, &status));
return 0;
}
@@ -823,7 +949,9 @@ static int lupb_msgdef_fields(lua_State *L) {
const upb_msgdef *m = lupb_msgdef_check(L, 1);
upb_msg_iter *i = lua_newuserdata(L, sizeof(upb_msg_iter));
upb_msg_begin(i, m);
- lua_pushcclosure(L, &lupb_msgiter_next, 1);
+ // Need to guarantee that the msgdef outlives the iter.
+ lua_pushvalue(L, 1);
+ lua_pushcclosure(L, &lupb_msgiter_next, 2);
return 1;
}
@@ -850,65 +978,27 @@ static const struct luaL_Reg lupb_msgdef_m[] = {
/* lupb_enumdef ***************************************************************/
const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg) {
- lupb_refcounted *r = luaL_checkudata(L, narg, LUPB_ENUMDEF);
- if (!r) luaL_typerror(L, narg, LUPB_ENUMDEF);
- if (!r->refcounted) luaL_error(L, "called into dead enumdef");
- return upb_downcast_enumdef(r->def);
+ return lupb_refcounted_check(L, narg, LUPB_ENUMDEF);
}
static upb_enumdef *lupb_enumdef_checkmutable(lua_State *L, int narg) {
const upb_enumdef *f = lupb_enumdef_check(L, narg);
if (upb_enumdef_isfrozen(f))
- luaL_typerror(L, narg, "not allowed on frozen value");
+ luaL_error(L, "not allowed on frozen value");
return (upb_enumdef*)f;
}
-static int lupb_enumdef_gc(lua_State *L) {
- lupb_refcounted *r = luaL_checkudata(L, 1, LUPB_ENUMDEF);
- upb_def_unref(r->def, r);
- r->refcounted = NULL;
- return 0;
-}
-
static int lupb_enumdef_new(lua_State *L) {
- int narg = lua_gettop(L);
upb_enumdef *e = upb_enumdef_new(&e);
lupb_def_pushnewrapper(L, UPB_UPCAST(e), &e);
-
- if (narg == 0) return 1;
-
- // User can specify initialization values like so:
- // upb.EnumDef{full_name="MyEnum",
- // values={
- // {"FOO_VALUE_1", 1},
- // {"FOO_VALUE_2", 2}
- // }
- // }
- luaL_checktype(L, 1, LUA_TTABLE);
- for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
- luaL_checktype(L, -2, LUA_TSTRING);
- const char *key = lua_tostring(L, -2);
- if (streql(key, "values")) {
- for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
- lua_rawgeti(L, -1, 1);
- luaL_checktype(L, -1, LUA_TSTRING);
- const char *name = lua_tostring(L, -1);
- lua_rawgeti(L, -2, 2);
- CHK(upb_enumdef_addval(e, name, chkint32(L, -1, "value"), &status));
- lua_pop(L, 2); // The key/val we got from lua_rawgeti()
- }
- } else if (streql(key, "full_name")) {
- CHK(upb_def_setfullname(UPB_UPCAST(e), chkname(L, -1), &status));
- } else {
- luaL_error(L, "Unknown initializer key '%s'", key);
- }
- }
return 1;
}
static int lupb_enumdef_add(lua_State *L) {
upb_enumdef *e = lupb_enumdef_checkmutable(L, 1);
- CHK(upb_enumdef_addval(e, chkname(L, 2), chkint32(L, 3, "value"), &status));
+ const char *name = lupb_checkname(L, 2);
+ int32_t val = lupb_checkint32(L, 3);
+ CHK(upb_enumdef_addval(e, name, val, &status));
return 0;
}
@@ -923,11 +1013,13 @@ static int lupb_enumdef_value(lua_State *L) {
int type = lua_type(L, 2);
if (type == LUA_TNUMBER) {
// Pushes "nil" for a NULL pointer.
- lua_pushstring(L, upb_enumdef_iton(e, chkint32(L, 2, "value")));
+ int32_t key = lupb_checkint32(L, 2);
+ lua_pushstring(L, upb_enumdef_iton(e, key));
} else if (type == LUA_TSTRING) {
+ const char *key = lua_tostring(L, 2);
int32_t num;
- if (upb_enumdef_ntoi(e, lua_tostring(L, 2), &num)) {
- lua_pushnumber(L, num);
+ if (upb_enumdef_ntoi(e, key, &num)) {
+ lua_pushinteger(L, num);
} else {
lua_pushnil(L);
}
@@ -943,7 +1035,7 @@ static int lupb_enumiter_next(lua_State *L) {
upb_enum_iter *i = lua_touserdata(L, lua_upvalueindex(1));
if (upb_enum_done(i)) return 0;
lua_pushstring(L, upb_enum_iter_name(i));
- lua_pushnumber(L, upb_enum_iter_number(i));
+ lua_pushinteger(L, upb_enum_iter_number(i));
upb_enum_next(i);
return 2;
}
@@ -952,12 +1044,13 @@ static int lupb_enumdef_values(lua_State *L) {
const upb_enumdef *e = lupb_enumdef_check(L, 1);
upb_enum_iter *i = lua_newuserdata(L, sizeof(upb_enum_iter));
upb_enum_begin(i, e);
- lua_pushcclosure(L, &lupb_enumiter_next, 1);
+ // Need to guarantee that the enumdef outlives the iter.
+ lua_pushvalue(L, 1);
+ lua_pushcclosure(L, &lupb_enumiter_next, 2);
return 1;
}
static const struct luaL_Reg lupb_enumdef_mm[] = {
- {"__gc", lupb_enumdef_gc},
{"__len", lupb_enumdef_len},
{NULL, NULL}
};
@@ -976,83 +1069,95 @@ static const struct luaL_Reg lupb_enumdef_m[] = {
// Inherits a ref on the symtab.
// Checks that narg is a proper lupb_symtab object. If it is, leaves its
// metatable on the stack for cache lookups/updates.
-upb_symtab *lupb_symtab_check(lua_State *L, int narg) {
- lupb_refcounted *r = luaL_checkudata(L, narg, LUPB_SYMTAB);
- if (!r) luaL_typerror(L, narg, LUPB_SYMTAB);
- if (!r->refcounted) luaL_error(L, "called into dead symtab");
- return r->symtab;
+const upb_symtab *lupb_symtab_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_SYMTAB);
}
-// narg is a lua table containing a list of defs to add.
-void lupb_symtab_doadd(lua_State *L, upb_symtab *s, int narg) {
- luaL_checktype(L, narg, LUA_TTABLE);
- // Iterate over table twice. First iteration to count entries and
- // check constraints.
- int n = 0;
- for (lua_pushnil(L); lua_next(L, narg); lua_pop(L, 1)) {
- lupb_def_check(L, -1);
- ++n;
- }
+static upb_symtab *lupb_symtab_checkmutable(lua_State *L, int narg) {
+ const upb_symtab *s = lupb_symtab_check(L, narg);
+ if (upb_symtab_isfrozen(s))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_symtab*)s;
+}
- // Second iteration to build deflist and layout.
- upb_def **defs = malloc(n * sizeof(*defs));
- n = 0;
- for (lua_pushnil(L); lua_next(L, narg); lua_pop(L, 1)) {
- upb_def *def = lupb_def_checkmutable(L, -1);
- defs[n++] = def;
- }
+void lupb_symtab_pushwrapper(lua_State *L, const upb_symtab *s,
+ const void *ref_donor) {
+ lupb_refcounted_pushwrapper(L, UPB_UPCAST(s), LUPB_SYMTAB, ref_donor,
+ sizeof(void *));
+}
- upb_status status = UPB_STATUS_INIT;
- upb_symtab_add(s, defs, n, NULL, &status);
- free(defs);
- lupb_checkstatus(L, &status);
+void lupb_symtab_pushnewrapper(lua_State *L, const upb_symtab *s,
+ const void *ref_donor) {
+ lupb_refcounted_pushnewrapper(L, UPB_UPCAST(s), LUPB_SYMTAB, ref_donor);
}
static int lupb_symtab_new(lua_State *L) {
- int narg = lua_gettop(L);
upb_symtab *s = upb_symtab_new(&s);
- lupb_refcounted_pushnewrapper(L, UPB_UPCAST(s), LUPB_SYMTAB, &s);
- if (narg > 0) lupb_symtab_doadd(L, s, 1);
+ lupb_symtab_pushnewrapper(L, s, &s);
return 1;
}
-static int lupb_symtab_add(lua_State *L) {
- lupb_symtab_doadd(L, lupb_symtab_check(L, 1), 2);
+static int lupb_symtab_freeze(lua_State *L) {
+ upb_symtab_freeze(lupb_symtab_checkmutable(L, 1));
return 0;
}
-static int lupb_symtab_gc(lua_State *L) {
- lupb_refcounted *r = luaL_checkudata(L, 1, LUPB_SYMTAB);
- upb_symtab_unref(r->symtab, r);
- r->refcounted = NULL;
+static int lupb_symtab_isfrozen(lua_State *L) {
+ lua_pushboolean(L, upb_symtab_isfrozen(lupb_symtab_check(L, 1)));
+ return 1;
+}
+
+static int lupb_symtab_add(lua_State *L) {
+ upb_symtab *s = lupb_symtab_checkmutable(L, 1);
+ luaL_checktype(L, 2, LUA_TTABLE);
+ // Iterate over table twice. First iteration to count entries and
+ // check constraints.
+ int n = 0;
+ for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
+ lupb_def_checkmutable(L, -1);
+ ++n;
+ }
+
+ // Second iteration to build deflist.
+ // Allocate list with lua_newuserdata() so it is anchored as a GC root in
+ // case any Lua functions longjmp().
+ upb_def **defs = lua_newuserdata(L, n * sizeof(*defs));
+ n = 0;
+ for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
+ upb_def *def = lupb_def_checkmutable(L, -1);
+ defs[n++] = def;
+ }
+
+ CHK(upb_symtab_add(s, defs, n, NULL, &status));
return 0;
}
static int lupb_symtab_lookup(lua_State *L) {
- upb_symtab *s = lupb_symtab_check(L, 1);
+ const upb_symtab *s = lupb_symtab_check(L, 1);
for (int i = 2; i <= lua_gettop(L); i++) {
- const upb_def *def =
- upb_symtab_lookup(s, luaL_checkstring(L, i), &def);
- lupb_def_pushwrapper(L, def, &def);
+ const upb_def *def = upb_symtab_lookup(s, luaL_checkstring(L, i));
+ lupb_def_pushwrapper(L, def, NULL);
lua_replace(L, i);
}
return lua_gettop(L) - 1;
}
-static int lupb_symtab_getdefs(lua_State *L) {
- upb_symtab *s = lupb_symtab_check(L, 1);
- upb_deftype_t type = luaL_checkint(L, 2);
- int count;
- const upb_def **defs = upb_symtab_getdefs(s, type, &defs, &count);
+static int lupb_symtabiter_next(lua_State *L) {
+ upb_symtab_iter *i = lua_touserdata(L, lua_upvalueindex(1));
+ if (upb_symtab_done(i)) return 0;
+ lupb_def_pushwrapper(L, upb_symtab_iter_def(i), NULL);
+ upb_symtab_next(i);
+ return 1;
+}
- // Create the table in which we will return the defs.
- lua_createtable(L, count, 0);
- for (int i = 0; i < count; i++) {
- const upb_def *def = defs[i];
- lupb_def_pushwrapper(L, def, &defs);
- lua_rawseti(L, -2, i + 1);
- }
- free(defs);
+static int lupb_symtab_defs(lua_State *L) {
+ const upb_symtab *s = lupb_symtab_check(L, 1);
+ upb_deftype_t type = lua_gettop(L) > 1 ? luaL_checkint(L, 2) : UPB_DEF_ANY;
+ upb_symtab_iter *i = lua_newuserdata(L, sizeof(upb_symtab_iter));
+ upb_symtab_begin(i, s, type);
+ // Need to guarantee that the symtab outlives the iter.
+ lua_pushvalue(L, 1);
+ lua_pushcclosure(L, &lupb_symtabiter_next, 2);
return 1;
}
@@ -1061,7 +1166,7 @@ static int lupb_symtab_getdefs(lua_State *L) {
// the descriptor.proto schema and the protobuf binary format.
static int lupb_symtab_load_descriptor(lua_State *L) {
size_t len;
- upb_symtab *s = lupb_symtab_check(L, 1);
+ upb_symtab *s = lupb_symtab_checkmutable(L, 1);
const char *str = luaL_checklstring(L, 2, &len);
CHK(upb_load_descriptor_into_symtab(s, str, len, &status));
return 0;
@@ -1069,67 +1174,598 @@ static int lupb_symtab_load_descriptor(lua_State *L) {
static const struct luaL_Reg lupb_symtab_m[] = {
{"add", lupb_symtab_add},
- {"getdefs", lupb_symtab_getdefs},
+ {"defs", lupb_symtab_defs},
+ {"freeze", lupb_symtab_freeze},
+ {"is_frozen", lupb_symtab_isfrozen},
{"lookup", lupb_symtab_lookup},
{"load_descriptor", lupb_symtab_load_descriptor},
{NULL, NULL}
};
-static const struct luaL_Reg lupb_symtab_mm[] = {
- {"__gc", lupb_symtab_gc},
+
+/* lupb_array *****************************************************************/
+
+// A lupb_array provides a strongly-typed array.
+//
+// For the moment we store all values in the userdata's environment table /
+// userval, for simplicity. Later we may wish to move the data into raw
+// memory as both a space and time optimization.
+//
+// Compared to regular Lua tables:
+//
+// - we only allow integer indices.
+// - all entries must match the type of the table.
+// - we do not allow "holes" in the array; you can only assign to an existing
+// index or one past the end (which will grow the array by one).
+
+typedef struct {
+ uint32_t size;
+ upb_fieldtype_t type;
+ const upb_msgdef *msgdef; // Only when type == UPB_TYPE_MESSAGE
+} lupb_array;
+
+static lupb_array *lupb_array_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_ARRAY);
+}
+
+static uint32_t lupb_array_checkindex(lua_State *L, int narg, uint32_t max) {
+ uint32_t n = lupb_checkuint32(L, narg);
+ if (n == 0 || n > max) { // Lua uses 1-based indexing. :(
+ luaL_error(L, "Invalid array index.");
+ }
+ return n;
+}
+
+static int lupb_array_new(lua_State *L) {
+ lupb_array *array = newudata_with_userval(L, sizeof(*array), LUPB_ARRAY);
+ array->size = 0;
+
+ if (lua_type(L, 1) == LUA_TNUMBER) {
+ array->type = lupb_checkfieldtype(L, 1);
+ if (array->type == UPB_TYPE_MESSAGE) {
+ return luaL_error(
+ L, "For message arrays construct with the specific message type.");
+ }
+ } else {
+ array->type = UPB_TYPE_MESSAGE;
+ array->msgdef = lupb_msgdef_check(L, 1);
+
+ // Store a reference to this msgdef in the environment table to ensure it
+ // outlives this array.
+ lua_getuservalue(L, -1);
+ lua_pushvalue(L, 1);
+ lua_rawseti(L, -2, 0);
+ lua_pop(L, 1); // Pop userval.
+ }
+
+ return 1;
+}
+
+static int lupb_array_newindex(lua_State *L) {
+ lupb_array *array = lupb_array_check(L, 1);
+ uint32_t n = lupb_array_checkindex(L, 2, array->size + 1);
+
+ if (n == array->size + 1) {
+ array->size++;
+ }
+
+ if (array->type == UPB_TYPE_MESSAGE) {
+ if (array->msgdef != lupb_msg_checkdef(L, 3)) {
+ return luaL_error(L, "Tried to assign wrong message type.");
+ }
+ } else {
+ lupb_checkval(L, 3, array->type);
+ }
+
+ // Write value to userval table.
+ lua_getuservalue(L, 1);
+ lua_pushvalue(L, 3);
+ lua_rawseti(L, -2, n);
+
+ return 0; // 1 for chained assignments?
+}
+
+static int lupb_array_index(lua_State *L) {
+ lupb_array *array = lupb_array_check(L, 1);
+ uint32_t n = lupb_array_checkindex(L, 2, array->size);
+
+ lua_getuservalue(L, 1);
+ lua_rawgeti(L, -1, n);
+ return 1;
+}
+
+static int lupb_array_len(lua_State *L) {
+ lupb_array *array = lupb_array_check(L, 1);
+ lua_pushnumber(L, array->size);
+ return 1;
+}
+
+static const struct luaL_Reg lupb_array_mm[] = {
+ {"__index", lupb_array_index},
+ {"__len", lupb_array_len},
+ {"__newindex", lupb_array_newindex},
+ {NULL, NULL}
+};
+
+/* lupb_msg **************************************************************/
+
+// A lupb_msg is a userdata where:
+//
+// - the userdata's memory contains hasbits and primitive fields.
+// - the userdata's environment table / uservalue contains references to string
+// fields, submessage fields, and array fields.
+
+typedef struct {
+ const lupb_msgdef *lmd;
+ char data[];
+} lupb_msg;
+
+#define MSGDEF_INDEX 0
+
+static bool in_userval(const upb_fielddef *f) {
+ return upb_fielddef_isseq(f) || upb_fielddef_issubmsg(f) ||
+ upb_fielddef_isstring(f);
+}
+
+static size_t lupb_sizeof(lua_State *L, const upb_fielddef *f) {
+ switch (upb_fielddef_type(f)) {
+ case UPB_TYPE_BOOL:
+ return 1;
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_UINT32:
+ case UPB_TYPE_ENUM:
+ case UPB_TYPE_FLOAT:
+ return 4;
+ case UPB_TYPE_INT64:
+ case UPB_TYPE_UINT64:
+ case UPB_TYPE_DOUBLE:
+ return 8;
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_MESSAGE:
+ lupb_assert(L, false);
+ return 0;
+ }
+}
+
+static int div_round_up(size_t n, size_t d) {
+ int ret = n / d;
+ // If there was a positive remainder, then the result was rounded down and we
+ // need to compensate by adding one.
+ if (n % d > 0) ++ret;
+ return ret;
+}
+
+static size_t align_up(size_t val, size_t align) {
+ return val % align == 0 ? val : val + align - (val % align);
+}
+
+// If we always read/write as a consistent type to each value, this shouldn't
+// violate aliasing.
+#define DEREF(msg, ofs, type) *(type*)(&msg->data[ofs])
+
+lupb_msg *lupb_msg_check(lua_State *L, int narg) {
+ lupb_msg *msg = luaL_checkudata(L, narg, LUPB_MSG);
+ if (!msg->lmd) luaL_error(L, "called into dead msg");
+ return msg;
+}
+
+const upb_msgdef *lupb_msg_checkdef(lua_State *L, int narg) {
+ return lupb_msg_check(L, narg)->lmd->md;
+}
+
+static const upb_fielddef *lupb_msg_checkfield(lua_State *L,
+ const lupb_msgdef *lmd,
+ int fieldarg) {
+ const char *fieldname = luaL_checkstring(L, fieldarg);
+ const upb_fielddef *f = upb_msgdef_ntof(lmd->md, fieldname);
+
+ if (!f) {
+ const char *msg = lua_pushfstring(L, "no such field: %s", fieldname);
+ luaL_argerror(L, fieldarg, msg);
+ return NULL; // Never reached.
+ }
+
+ return f;
+}
+
+// Assigns offsets for storing data in instances of messages for this type, if
+// they have not already been assigned. "narg" should be the stack location of
+// a Lua msgdef object. It should be frozen (if it is not, we will throw an
+// error). It should not throw errors in any other case, since we may have
+// values on our stack that would leak if we longjmp'd across them.
+//
+// TODO(haberman): (if we want to avoid this and be robust against even lua
+// errors due to OOM, we should stop using upb_handlers_newfrozen() and
+// implement it ourselves with a Lua table as cache, since that would get
+// cleaned up properly on error).
+static lupb_msgdef *lupb_msg_assignoffsets(lua_State *L, int narg) {
+ lupb_msgdef *lmd = lupb_msgdef_check2(L, narg);
+ if (!upb_msgdef_isfrozen(lmd->md))
+ luaL_error(L, "msgdef must be frozen");
+
+ if (lmd->field_offsets) {
+ // Already assigned.
+ return lmd;
+ }
+
+ int n = upb_msgdef_numfields(lmd->md);
+ uint16_t *offsets = malloc(sizeof(*offsets) * n);
+
+ // Offset with the raw data part; starts with hasbits.
+ size_t hasbits_size = div_round_up(n, 8);
+ size_t data_ofs = hasbits_size;
+ // Index within the userval.
+ // Starts at one to not collide with MSGDEF_INDEX.
+ size_t userval_idx = 1;
+
+ // Assign offsets.
+ upb_msg_iter i;
+ for (upb_msg_begin(&i, lmd->md); !upb_msg_done(&i); upb_msg_next(&i)) {
+ upb_fielddef *f = upb_msg_iter_field(&i);
+ if (in_userval(f)) {
+ offsets[upb_fielddef_index(f)] = userval_idx++;
+ } else {
+ size_t size = lupb_sizeof(L, f);
+ data_ofs = align_up(data_ofs, size);
+ offsets[upb_fielddef_index(f)] = data_ofs;
+ data_ofs += size;
+ }
+ }
+
+ lmd->field_offsets = offsets;
+ lmd->msg_size = sizeof(lupb_msg) + data_ofs;
+ lmd->hasbits_size = hasbits_size;
+
+ // Now recursively assign offsets for all submessages, and also add them to
+ // the uservalue to ensure that all the lupb_msgdef objects for our
+ // submessages outlive us. This is particularly important if/when we build
+ // handlers to populate this msgdef.
+
+ lua_pushvalue(L, narg);
+ lua_newtable(L); // This will be our userval.
+
+ int idx = 1;
+ for (upb_msg_begin(&i, lmd->md); !upb_msg_done(&i); upb_msg_next(&i)) {
+ upb_fielddef *f = upb_msg_iter_field(&i);
+ if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE) {
+ bool created = lupb_def_pushwrapper(L, upb_fielddef_subdef(f), NULL);
+ UPB_ASSERT_VAR(created, !created);
+ lupb_msg_assignoffsets(L, -1);
+ lua_rawseti(L, -2, idx++); // Append to uservalue.
+ }
+ }
+
+ lua_setuservalue(L, -2);
+ lua_pop(L, 1); // copy of msgdef
+
+ return lmd;
+}
+
+void lupb_msg_pushnew(lua_State *L, int narg) {
+ lupb_msgdef *lmd = lupb_msg_assignoffsets(L, narg);
+
+ // Add passed-in MessageDef to a table which will become the msg's userval.
+ lua_pushvalue(L, narg);
+ lua_newtable(L);
+ lua_pushvalue(L, narg);
+ lua_rawseti(L, -2, MSGDEF_INDEX);
+
+ lupb_msg *msg = newudata_with_userval(L, lmd->msg_size, LUPB_MSG);
+ memset(msg, 0, lmd->msg_size);
+
+ // Create a msg->msgdef reference, both:
+ // 1. a pointer in the userdata itself (for easy access) and
+ msg->lmd = lmd;
+
+ // 2. a reference in Lua-space from the msg's uservalue to the messagedef
+ // wrapper object (so the msgdef wrapper object will always outlive us,
+ // GC-wise).
+ lua_pushvalue(L, -2); // Push the table from before.
+ lua_setuservalue(L, -2); // Pop table, now msg is at top again.
+ lua_remove(L, -2); // Remove table, so new message is only new value.
+}
+
+static int lupb_msg_new(lua_State *L) {
+ lupb_msg_pushnew(L, 1);
+ return 1;
+}
+
+static bool lupb_msg_has(const lupb_msg *msg, const upb_fielddef *f) {
+ uint16_t idx = upb_fielddef_index(f);
+ return msg->data[idx / 8] & (1 << (idx % 8));
+}
+
+static void lupb_msg_set(lupb_msg *msg, const upb_fielddef *f) {
+ uint16_t idx = upb_fielddef_index(f);
+ msg->data[idx / 8] |= (1 << (idx % 8));
+}
+
+static int lupb_msg_index(lua_State *L) {
+ lupb_msg *msg = lupb_msg_check(L, 1);
+ const upb_fielddef *f = lupb_msg_checkfield(L, msg->lmd, 2);
+
+ if (!upb_fielddef_isseq(f) && !lupb_msg_has(msg, f)) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ int ofs = msg->lmd->field_offsets[upb_fielddef_index(f)];
+
+ if (in_userval(f)) {
+ lua_getuservalue(L, 1);
+ lua_pushinteger(L, ofs);
+ lua_rawget(L, -2);
+ } else {
+ switch (upb_fielddef_type(f)) {
+ case UPB_TYPE_FLOAT:
+ lupb_pushfloat(L, DEREF(msg, ofs, float));
+ break;
+ case UPB_TYPE_DOUBLE:
+ lupb_pushdouble(L, DEREF(msg, ofs, double));
+ break;
+ case UPB_TYPE_BOOL:
+ lua_pushboolean(L, DEREF(msg, ofs, bool));
+ break;
+ case UPB_TYPE_ENUM:
+ case UPB_TYPE_INT32:
+ lupb_pushint32(L, DEREF(msg, ofs, int32_t));
+ break;
+ case UPB_TYPE_UINT32:
+ lupb_pushuint32(L, DEREF(msg, ofs, uint32_t));
+ break;
+ case UPB_TYPE_INT64:
+ if (LUA_VERSION_NUM < 503) {
+ // Check value? Lua < 5.3.0 has no native integer support, lua_Number
+ // is probably double which can't exactly represent large int64s.
+ }
+ lupb_pushint64(L, DEREF(msg, ofs, int64_t));
+ break;
+ case UPB_TYPE_UINT64:
+ if (LUA_VERSION_NUM < 503) {
+ // Check value? Lua < 5.3.0 has no native integer support, lua_Number
+ // is probably double which can't exactly represent large uint64s.
+ }
+ lupb_pushuint64(L, DEREF(msg, ofs, uint64_t));
+ break;
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_MESSAGE:
+ lupb_assert(L, false);
+ break;
+ }
+ }
+
+ return 1;
+}
+
+int lupb_msg_newindex(lua_State *L) {
+ lupb_msg *msg = lupb_msg_check(L, 1);
+ const upb_fielddef *f = lupb_msg_checkfield(L, msg->lmd, 2);
+
+ lupb_msg_set(msg, f);
+
+ int ofs = msg->lmd->field_offsets[upb_fielddef_index(f)];
+
+ if (in_userval(f)) {
+ // Type-check and then store in the userval.
+ if (upb_fielddef_isseq(f)) {
+ lupb_array *array = lupb_array_check(L, 3);
+ if (array->type != upb_fielddef_type(f) ||
+ (array->type == UPB_TYPE_MESSAGE &&
+ array->msgdef != upb_fielddef_msgsubdef(f))) {
+ return luaL_error(L, "Array type mismatch");
+ }
+ } else if (upb_fielddef_isstring(f)) {
+ lupb_checkstring(L, 3);
+ } else {
+ if (lupb_msg_checkdef(L, 3) != upb_fielddef_msgsubdef(f)) {
+ return luaL_error(L, "Message type mismatch");
+ }
+ }
+ lua_getuservalue(L, 1);
+ lua_pushvalue(L, 3);
+ lua_rawseti(L, -2, ofs);
+ } else {
+ switch (upb_fielddef_type(f)) {
+ case UPB_TYPE_FLOAT:
+ DEREF(msg, ofs, float) = lupb_checkfloat(L, 3);
+ break;
+ case UPB_TYPE_DOUBLE:
+ DEREF(msg, ofs, double) = lupb_checkdouble(L, 3);
+ break;
+ case UPB_TYPE_ENUM:
+ case UPB_TYPE_INT32:
+ DEREF(msg, ofs, int32_t) = lupb_checkint32(L, 3);
+ break;
+ case UPB_TYPE_UINT32:
+ DEREF(msg, ofs, uint32_t) = lupb_checkuint32(L, 3);
+ break;
+ case UPB_TYPE_INT64:
+ DEREF(msg, ofs, int64_t) = lupb_checkint64(L, 3);
+ break;
+ case UPB_TYPE_UINT64:
+ DEREF(msg, ofs, uint64_t) = lupb_checkuint64(L, 3);
+ break;
+ case UPB_TYPE_BOOL:
+ DEREF(msg, ofs, bool) = lupb_checkbool(L, 3);
+ break;
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_MESSAGE:
+ lupb_assert(L, false);
+ }
+ }
+
+ return 0; // 1 for chained assignments?
+}
+
+static const struct luaL_Reg lupb_msg_mm[] = {
+ {"__index", lupb_msg_index},
+ {"__newindex", lupb_msg_newindex},
{NULL, NULL}
};
+/* lupb_msg populating handlers ***********************************************/
+
+// NOTE: doesn't support repeated or submessage fields yet. Coming soon.
+
+typedef struct {
+ uint32_t ofs;
+ uint32_t hasbit;
+} lupb_handlerdata;
+
+static void lupb_sethasbit(lupb_msg *msg, uint32_t hasbit) {
+ msg->data[hasbit / 8] |= 1 << (hasbit % 8);
+}
+
+static size_t strhandler(void *closure, const void *hd, const char *str,
+ size_t len, const upb_bufhandle *handle) {
+ lupb_msg *msg = closure;
+ const lupb_handlerdata *data = hd;
+ lua_State *L = msg->lmd->L;
+ lua_pushlstring(L, str, len);
+ lua_rawseti(L, -2, data->ofs);
+ lupb_sethasbit(msg, data->hasbit);
+ return len;
+}
+
+const void *newhandlerdata(upb_handlers *h, uint32_t ofs, uint32_t hasbit) {
+ lupb_handlerdata *data = malloc(sizeof(*data));
+ data->ofs = ofs;
+ data->hasbit = hasbit;
+ upb_handlers_addcleanup(h, data, free);
+ return data;
+}
+
+void callback(const void *closure, upb_handlers *h) {
+ lua_State *L = (lua_State*)closure;
+ lupb_def_pushwrapper(L, UPB_UPCAST(upb_handlers_msgdef(h)), NULL);
+ lupb_msgdef *lmd = lupb_msg_assignoffsets(L, -1);
+ upb_msg_iter i;
+ upb_msg_begin(&i, upb_handlers_msgdef(h));
+ for (; !upb_msg_done(&i); upb_msg_next(&i)) {
+ upb_fielddef *f = upb_msg_iter_field(&i);
+ int hasbit = upb_fielddef_index(f);
+ uint16_t ofs = lmd->field_offsets[upb_fielddef_index(f)];
+ if (upb_fielddef_isseq(f)) {
+ luaL_error(L, "Doesn't support repeated fields yet.");
+ } else {
+ switch (upb_fielddef_type(f)) {
+ case UPB_TYPE_BOOL:
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_UINT32:
+ case UPB_TYPE_ENUM:
+ case UPB_TYPE_FLOAT:
+ case UPB_TYPE_INT64:
+ case UPB_TYPE_UINT64:
+ case UPB_TYPE_DOUBLE:
+ hasbit += offsetof(lupb_msg, data) * 8;
+ ofs += offsetof(lupb_msg, data);
+ upb_shim_set(h, f, ofs, hasbit);
+ break;
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES: {
+ upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
+ upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, ofs, hasbit));
+ // XXX: does't currently handle split buffers.
+ upb_handlers_setstring(h, f, strhandler, &attr);
+ upb_handlerattr_uninit(&attr);
+ break;
+ }
+ case UPB_TYPE_MESSAGE:
+ luaL_error(L, "Doesn't support submessages yet.");
+ break;
+ }
+ }
+ }
+ lua_pop(L, 1); // msgdef wrapper
+}
+
+const upb_handlers *lupb_msg_newwritehandlers(lua_State *L, int narg,
+ const void *owner) {
+ lupb_msgdef *lmd = lupb_msg_assignoffsets(L, narg);
+ return upb_handlers_newfrozen(lmd->md, owner, callback, L);
+}
+
+
/* lupb toplevel **************************************************************/
-static int lupb_def_freeze(lua_State *L) {
+static int lupb_freeze(lua_State *L) {
int n = lua_gettop(L);
- upb_def **defs = malloc(n * sizeof(upb_def*));
+ // Scratch memory; lua_newuserdata() anchors it as a GC root in case any Lua
+ // functions fail.
+ upb_def **defs = lua_newuserdata(L, n * sizeof(upb_def*));
for (int i = 0; i < n; i++) {
// Could allow an array of defs here also.
defs[i] = lupb_def_checkmutable(L, i + 1);
}
- upb_status s = UPB_STATUS_INIT;
- upb_def_freeze(defs, n, &s);
- free(defs);
- lupb_checkstatus(L, &s);
+ CHK(upb_def_freeze(defs, n, &status));
return 0;
}
static const struct luaL_Reg lupb_toplevel_m[] = {
+ {"Array", lupb_array_new},
{"EnumDef", lupb_enumdef_new},
{"FieldDef", lupb_fielddef_new},
+ {"Message", lupb_msg_new},
{"MessageDef", lupb_msgdef_new},
{"SymbolTable", lupb_symtab_new},
- {"freeze", lupb_def_freeze},
+ {"freeze", lupb_freeze},
{NULL, NULL}
};
-// Register the given type with the given methods and metamethods.
-static void lupb_register_type(lua_State *L, const char *name,
- const luaL_Reg *m, const luaL_Reg *mm) {
+void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
+ const luaL_Reg *mm, bool refcount_gc) {
luaL_newmetatable(L, name);
- lupb_setfuncs(L, mm); // Register all mm in the metatable.
- lua_createtable(L, 0, 0);
- // Methods go in the mt's __index method. This implies that you can't
- // implement __index.
- lupb_setfuncs(L, m);
- lua_setfield(L, -2, "__index");
+
+ if (mm) {
+ lupb_setfuncs(L, mm);
+ }
+
+ if (refcount_gc) {
+ lupb_setfuncs(L, lupb_refcounted_mm);
+ }
+
+ if (m) {
+ // Methods go in the mt's __index method. This implies that you can't
+ // implement __index and also have methods.
+ lua_getfield(L, -1, "__index");
+ lupb_assert(L, lua_isnil(L, -1));
+ lua_pop(L, 1);
+
+ lua_createtable(L, 0, 0);
+ lupb_setfuncs(L, m);
+ lua_setfield(L, -2, "__index");
+ }
+
lua_pop(L, 1); // The mt.
}
static void lupb_setfieldi(lua_State *L, const char *field, int i) {
- lua_pushnumber(L, i);
+ lua_pushinteger(L, i);
lua_setfield(L, -2, field);
}
int luaopen_upb(lua_State *L) {
- lupb_register_type(L, LUPB_MSGDEF, lupb_msgdef_m, lupb_msgdef_mm);
- lupb_register_type(L, LUPB_ENUMDEF, lupb_enumdef_m, lupb_enumdef_mm);
- lupb_register_type(L, LUPB_FIELDDEF, lupb_fielddef_m, lupb_fielddef_mm);
- lupb_register_type(L, LUPB_SYMTAB, lupb_symtab_m, lupb_symtab_mm);
+ static char module_key;
+ if (lupb_openlib(L, &module_key, "upb", lupb_toplevel_m)) {
+ return 1;
+ }
+
+ // Non-refcounted types.
+ lupb_register_type(L, LUPB_ARRAY, NULL, lupb_array_mm, false);
+ lupb_register_type(L, LUPB_MSG, NULL, lupb_msg_mm, false);
+
+ // Refcounted types.
+ lupb_register_type(L, LUPB_ENUMDEF, lupb_enumdef_m, lupb_enumdef_mm, true);
+ lupb_register_type(L, LUPB_FIELDDEF, lupb_fielddef_m, NULL, true);
+ lupb_register_type(L, LUPB_SYMTAB, lupb_symtab_m, NULL, true);
+
+ // Refcounted but with custom __gc.
+ lupb_register_type(L, LUPB_MSGDEF, lupb_msgdef_m, lupb_msgdef_mm, false);
// Create our object cache.
lua_newtable(L);
@@ -1139,26 +1775,6 @@ int luaopen_upb(lua_State *L) {
lua_setmetatable(L, -2);
lua_setfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE);
- lupb_newlib(L, "upb", lupb_toplevel_m);
-
- // Define a couple functions as Lua source (kept here instead of a separate
- // Lua file so that upb.so is self-contained)
- const char *lua_source =
- "return function(upb)\n"
- " upb.build_defs = function(defs)\n"
- " local symtab = upb.SymbolTable(defs)\n"
- " return symtab:getdefs(upb.DEF_ANY)\n"
- " end\n"
- "end";
-
- if (luaL_dostring(L, lua_source) != 0)
- lua_error(L);
-
- // Call the chunk that will define the extra functions on upb, passing our
- // package dictionary as the argument.
- lua_pushvalue(L, -2);
- lua_call(L, 1, 0);
-
// Register constants.
lupb_setfieldi(L, "LABEL_OPTIONAL", UPB_LABEL_OPTIONAL);
lupb_setfieldi(L, "LABEL_REQUIRED", UPB_LABEL_REQUIRED);
@@ -1220,8 +1836,14 @@ int luaopen_upb(lua_State *L) {
lupb_setfieldi(L, "HANDLER_STARTSEQ", UPB_HANDLER_STARTSEQ);
lupb_setfieldi(L, "HANDLER_ENDSEQ", UPB_HANDLER_ENDSEQ);
+ // Call the chunk that will define the extra functions on upb, passing our
+ // package dictionary as the argument.
+ if (luaL_loadbuffer(L, upb_lua, sizeof(upb_lua), "upb.lua") ||
+ lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ lua_pushvalue(L, -2);
+ lua_call(L, 1, 0);
+
return 1; // Return package table.
}
-
-// Alternate names so that the library can be loaded as upb5_1 etc.
-int LUPB_OPENFUNC(upb)(lua_State *L) { return luaopen_upb(L); }
diff --git a/upb/bindings/lua/upb.h b/upb/bindings/lua/upb.h
index e6b4f2f..99fe8fe 100644
--- a/upb/bindings/lua/upb.h
+++ b/upb/bindings/lua/upb.h
@@ -10,36 +10,120 @@
#ifndef UPB_LUA_UPB_H_
#define UPB_LUA_UPB_H_
+#include "lauxlib.h"
#include "upb/def.h"
+#include "upb/handlers.h"
+#include "upb/symtab.h"
// Lua 5.1/5.2 compatibility code.
#if LUA_VERSION_NUM == 501
#define lua_rawlen lua_objlen
-#define lupb_newlib(L, name, l) luaL_register(L, name, l)
-#define lupb_setfuncs(L, l) luaL_register(L, NULL, l)
-#define LUPB_OPENFUNC(mod) luaopen_ ## mod ## upb5_1
+
+// Lua >= 5.2's getuservalue/setuservalue functions do not exist in prior
+// versions but the older function lua_getfenv() can provide 100% of its
+// capabilities (the reverse is not true).
+#define lua_getuservalue(L, index) lua_getfenv(L, index)
+#define lua_setuservalue(L, index) lua_setfenv(L, index)
void *luaL_testudata(lua_State *L, int ud, const char *tname);
#elif LUA_VERSION_NUM == 502
-// Lua 5.2 modules are not expected to set a global variable, so "name" is
-// unused.
-#define lupb_newlib(L, name, l) luaL_newlib(L, l)
-#define lupb_setfuncs(L, l) luaL_setfuncs(L, l, 0)
int luaL_typerror(lua_State *L, int narg, const char *tname);
-#define LUPB_OPENFUNC(mod) luaopen_ ## mod ## upb5_2
#else
#error Only Lua 5.1 and 5.2 are supported
#endif
+#define lupb_assert(L, predicate) \
+ if (!(predicate)) \
+ luaL_error(L, "internal error: %s, %s:%d ", #predicate, __FILE__, __LINE__);
+
+// Function for initializing the core library. This function is idempotent,
+// and should be called at least once before calling any of the functions that
+// construct core upb types.
+int luaopen_upb(lua_State *L);
+
+// Gets or creates a package table for a C module that is uniquely identified by
+// "ptr". The easiest way to supply a unique "ptr" is to pass the address of a
+// static variable private in the module's .c file.
+//
+// If this module has already been registered in this lua_State, pushes it and
+// returns true.
+//
+// Otherwise, creates a new module table for this module with the given name,
+// pushes it, and registers the given top-level functions in it. It also sets
+// it as a global variable, but only if the current version of Lua expects that
+// (ie Lua 5.1/LuaJIT).
+//
+// If "false" is returned, the caller is guaranteed that this lib has not been
+// registered in this Lua state before (regardless of any funny business the
+// user might have done to the global state), so the caller can safely perform
+// one-time initialization.
+bool lupb_openlib(lua_State *L, void *ptr, const char *name,
+ const luaL_Reg *funcs);
+
+// Custom check/push functions. Unlike the Lua equivalents, they are pinned to
+// specific types (instead of lua_Number, etc), and do not allow any implicit
+// conversion or data loss.
+int64_t lupb_checkint64(lua_State *L, int narg);
+int32_t lupb_checkint32(lua_State *L, int narg);
+uint64_t lupb_checkuint64(lua_State *L, int narg);
+uint32_t lupb_checkuint32(lua_State *L, int narg);
+double lupb_checkdouble(lua_State *L, int narg);
+float lupb_checkfloat(lua_State *L, int narg);
+bool lupb_checkbool(lua_State *L, int narg);
+const char *lupb_checkname(lua_State *L, int narg);
+
+void lupb_pushint64(lua_State *L, int64_t val);
+void lupb_pushint32(lua_State *L, int32_t val);
+void lupb_pushuint64(lua_State *L, uint64_t val);
+void lupb_pushuint32(lua_State *L, uint32_t val);
+void lupb_pushdouble(lua_State *L, double val);
+void lupb_pushfloat(lua_State *L, float val);
+void lupb_pushbool(lua_State *L, bool val);
+
+// Functions for getting/pushing wrappers to various types defined in the
+// core library.
+void *lupb_refcounted_check(lua_State *L, int narg, const char *type);
+const upb_msgdef *lupb_msg_checkdef(lua_State *L, int narg);
const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg);
const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg);
-const char *lupb_checkname(lua_State *L, int narg);
-bool lupb_def_pushwrapper(lua_State *L, const upb_def *def, const void *owner);
+const upb_fielddef *lupb_fielddef_check(lua_State *L, int narg);
+const upb_symtab *lupb_symtab_check(lua_State *L, int narg);
+
+void lupb_refcounted_pushnewrapper(lua_State *L, const upb_refcounted *obj,
+ const char *type, const void *ref_donor);
+bool lupb_def_pushwrapper(lua_State *L, const upb_def *def,
+ const void *ref_donor);
void lupb_def_pushnewrapper(lua_State *L, const upb_def *def,
- const void *owner);
+ const void *ref_donor);
+void lupb_symtab_pushwrapper(lua_State *L, const upb_symtab *s,
+ const void *ref_donor);
+void lupb_symtab_pushnewrapper(lua_State *L, const upb_symtab *s,
+ const void *ref_donor);
+
+// For constructing a new message. narg is the Lua value for the MessageDef
+// object.
+void lupb_msg_pushnew(lua_State *L, int narg);
+
+// Builds and returns a handlers object for populating a lupb_msg described by
+// the MessageDef at "narg".
+//
+// TODO(haberman): factor this so it doesn't have to take a lua_State. We
+// should be able to generate message handlers for a upb_msgdef that can be used
+// across many Lua states, so we can shared JIT code across lua_States.
+const upb_handlers *lupb_msg_newwritehandlers(lua_State *L, int narg,
+ const void *owner);
+
+// Registers a type with the given name, methods, and metamethods.
+// If "refcount_gc" is true, adds a __gc metamethod that does an unref.
+// Refcounted types must be allocated with lupb_refcounted_push[new]wrapper.
+void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
+ const luaL_Reg *mm, bool refcount_gc);
+
+// Checks the given upb_status and throws a Lua error if it is not ok.
+void lupb_checkstatus(lua_State *L, upb_status *s);
#endif // UPB_LUA_UPB_H_
diff --git a/upb/bindings/lua/upb.lua b/upb/bindings/lua/upb.lua
new file mode 100644
index 0000000..bda9dfe
--- /dev/null
+++ b/upb/bindings/lua/upb.lua
@@ -0,0 +1,146 @@
+--
+-- upb - a minimalist implementation of protocol buffers.
+--
+-- Copyright (c) 2009 Google Inc. See LICENSE for details.
+-- Author: Josh Haberman <jhaberman@gmail.com>
+--
+-- Pure-Lua support functions that are part of the "upb" module.
+-- This file is embedded and packaged into the "upb" C module binary -- it
+-- should not be installed or used directly!
+
+return function(upb)
+ -- A convenience function for building/linking/freezing defs
+ -- while maintaining their original order.
+ --
+ -- Sample usage:
+ -- local m1, m2 = upb.build_defs{
+ -- upb.MessageDef{full_name = "M1", fields = {
+ -- upb.FieldDef{
+ -- name = "m2",
+ -- number = 1,
+ -- type = upb.TYPE_MESSAGE,
+ -- subdef_name = ".M2"
+ -- },
+ -- }
+ -- },
+ -- upb.MessageDef{full_name = "M2"}
+ -- }
+ upb.build_defs = function(defs)
+ upb.SymbolTable(defs)
+ -- Lua 5.2 puts unpack in the table library.
+ return (unpack or table.unpack)(defs)
+ end
+
+ local ipairs_iter = function(array, last_index)
+ local next_index = last_index + 1
+ if next_index > #array then
+ return nil
+ end
+ return next_index, array[next_index]
+ end
+
+ -- For iterating over the indexes and values of a upb.Array.
+ --
+ -- for i, val in upb.ipairs(array) do
+ -- -- ...
+ -- end
+ upb.ipairs = function(array)
+ return ipairs_iter, array, 0
+ end
+
+ local set_named = function(obj, init)
+ for k, v in pairs(init) do
+ local func = obj["set_" .. k]
+ if not func then
+ error("Cannot set member: " .. k)
+ end
+ func(obj, v)
+ end
+ end
+
+ -- Capture references to the functions we're wrapping.
+ local RealFieldDef = upb.FieldDef
+ local RealEnumDef = upb.EnumDef
+ local RealMessageDef = upb.MessageDef
+ local RealSymbolTable = upb.SymbolTable
+
+ -- FieldDef constructor; a wrapper around the real constructor that can
+ -- set initial properties.
+ --
+ -- User can specify initialization values like so:
+ -- upb.FieldDef{label=upb.LABEL_REQUIRED, name="my_field", number=5,
+ -- type=upb.TYPE_INT32, default_value=12, type_name="Foo"}
+ upb.FieldDef = function(init)
+ local f = RealFieldDef()
+
+ if init then
+ -- Other members are often dependent on type, so set that first.
+ if init.type then
+ f:set_type(init.type)
+ init.type = nil
+ end
+
+ set_named(f, init)
+ end
+
+ return f
+ end
+
+
+ -- MessageDef constructor; a wrapper around the real constructor that can
+ -- set initial properties.
+ --
+ -- User can specify initialization values like so:
+ -- upb.MessageDef{full_name="MyMessage", extstart=8000, fields={...}}
+ upb.MessageDef = function(init)
+ local m = RealMessageDef()
+
+ if init then
+ for _, f in pairs(init.fields or {}) do
+ m:add(f)
+ end
+ init.fields = nil
+
+ set_named(m, init)
+ end
+
+ return m
+ end
+
+ -- EnumDef constructor; a wrapper around the real constructor that can
+ -- set initial properties.
+ --
+ -- User can specify initialization values like so:
+ -- upb.EnumDef{full_name="MyEnum",
+ -- values={
+ -- {"FOO_VALUE_1", 1},
+ -- {"FOO_VALUE_2", 2}
+ -- }
+ -- }
+ upb.EnumDef = function(init)
+ local e = RealEnumDef()
+
+ if init then
+ for _, val in pairs(init.values or {}) do
+ e:add(val[1], val[2])
+ end
+ init.values = nil
+
+ set_named(e, init)
+ end
+
+ return e
+ end
+
+ -- SymbolTable constructor; a wrapper around the real constructor that can
+ -- add an initial set of defs.
+ upb.SymbolTable = function(defs)
+ local s = RealSymbolTable()
+
+ if defs then
+ s:add(defs)
+ end
+
+ return s
+ end
+end
diff --git a/upb/bindings/lua/upb.pb.c b/upb/bindings/lua/upb.pb.c
new file mode 100644
index 0000000..c9f1f47
--- /dev/null
+++ b/upb/bindings/lua/upb.pb.c
@@ -0,0 +1,106 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2014 Google Inc. See LICENSE for details.
+ * Author: Josh Haberman <jhaberman@gmail.com>
+ *
+ * A Lua extension for upb.pb.
+ *
+ * Exposes all the types defined in upb/pb/{*}.h
+ * Also defines a few convenience functions on top.
+ */
+
+#include "upb/bindings/lua/upb.h"
+#include "upb/pb/decoder.h"
+
+#define LUPB_PBDECODERMETHOD "lupb.pb.decodermethod"
+
+#define MSGDEF_INDEX 1
+
+static upb_pbdecodermethod *lupb_pbdecodermethod_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_PBDECODERMETHOD);
+}
+
+static int lupb_pbdecodermethod_new(lua_State *L) {
+ const upb_handlers *handlers = lupb_msg_newwritehandlers(L, 1, &handlers);
+
+ upb_pbdecodermethodopts opts;
+ upb_pbdecodermethodopts_init(&opts, handlers);
+
+ const upb_pbdecodermethod *m = upb_pbdecodermethod_new(&opts, &m);
+ upb_handlers_unref(handlers, &handlers);
+ lupb_refcounted_pushnewrapper(L, UPB_UPCAST(m), LUPB_PBDECODERMETHOD, &m);
+
+ // We need to keep a pointer to the MessageDef (in Lua space) so we can
+ // construct new messages in parse().
+ lua_newtable(L);
+ lua_pushvalue(L, 1);
+ lua_rawseti(L, -2, MSGDEF_INDEX);
+ lua_setuservalue(L, -2);
+
+ return 1; // The DecoderMethod wrapper.
+}
+
+// Unlike most of our exposed Lua functions, this does not correspond to an
+// actual method on the underlying DecoderMethod. But it's convenient, and
+// important to implement in C because we can do stack allocation and
+// initialization of our runtime structures like the Decoder and Sink.
+static int lupb_pbdecodermethod_parse(lua_State *L) {
+ size_t len;
+ const upb_pbdecodermethod *method = lupb_pbdecodermethod_check(L, 1);
+ const char *pb = lua_tolstring(L, 2, &len);
+
+ const upb_handlers *handlers = upb_pbdecodermethod_desthandlers(method);
+
+ lua_getuservalue(L, 1);
+ lua_rawgeti(L, -1, MSGDEF_INDEX);
+ lupb_assert(L, !lua_isnil(L, -1));
+ lupb_msg_pushnew(L, -1); // Push new message.
+ void *msg = lua_touserdata(L, -1);
+
+ // Handlers need this.
+ lua_getuservalue(L, -1);
+
+ upb_pbdecoder decoder;
+ upb_status status = UPB_STATUS_INIT;
+ upb_pbdecoder_init(&decoder, method, &status);
+ upb_sink sink;
+ upb_sink_reset(&sink, handlers, msg);
+ upb_pbdecoder_resetoutput(&decoder, &sink);
+ upb_bufsrc_putbuf(pb, len, upb_pbdecoder_input(&decoder));
+ // TODO: Our need to call uninit isn't longjmp-safe; what if the decode
+ // triggers a Lua error? uninit is only needed if the decoder
+ // dynamically-allocated a growing stack -- ditch this feature and live with
+ // the compile-time limit? Or have a custom allocation function that
+ // allocates Lua GC-rooted memory?
+ upb_pbdecoder_uninit(&decoder);
+ lupb_checkstatus(L, &status);
+
+ lua_pop(L, 1); // Uservalue.
+
+ return 1;
+}
+
+static const struct luaL_Reg lupb_pbdecodermethod_m[] = {
+ {"parse", lupb_pbdecodermethod_parse},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg toplevel_m[] = {
+ {"DecoderMethod", lupb_pbdecodermethod_new},
+ {NULL, NULL}
+};
+
+int luaopen_upb_pb(lua_State *L) {
+ luaopen_upb(L);
+
+ static char module_key;
+ if (lupb_openlib(L, &module_key, "upb.pb", toplevel_m)) {
+ return 1;
+ }
+
+ lupb_register_type(L, LUPB_PBDECODERMETHOD, lupb_pbdecodermethod_m, NULL,
+ true);
+
+ return 1;
+}
diff --git a/upb/bindings/lua/upb/descriptor.c b/upb/bindings/lua/upb/descriptor.c
new file mode 100644
index 0000000..9915e11
--- /dev/null
+++ b/upb/bindings/lua/upb/descriptor.c
@@ -0,0 +1,20 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2009 Google Inc. See LICENSE for details.
+ * Author: Josh Haberman <jhaberman@gmail.com>
+ *
+ * A Lua extension for upb/descriptor.
+ */
+
+#include "upb/bindings/lua/upb.h"
+
+static const struct luaL_Reg toplevel_m[] = {
+ {NULL, NULL}
+};
+
+int luaopen_upb_descriptor(lua_State *L) {
+ lupb_newlib(L, "upb.descriptor", toplevel_m);
+
+ return 1; // Return package table.
+}
diff --git a/upb/bindings/stdc++/string.h b/upb/bindings/stdc++/string.h
index 668f3e3..5137486 100644
--- a/upb/bindings/stdc++/string.h
+++ b/upb/bindings/stdc++/string.h
@@ -6,6 +6,8 @@
#ifndef UPB_STDCPP_H_
#define UPB_STDCPP_H_
+#include "upb/sink.h"
+
namespace upb {
template <class T>
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback