parent
6e1aa9f136
commit
bb38fc9c4d
10 changed files with 1554 additions and 0 deletions
@ -0,0 +1,355 @@ |
||||
/*
|
||||
* Copyright (c) 2009-2022, Google LLC |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* * Neither the name of Google LLC nor the |
||||
* names of its contributors may be used to endorse or promote products |
||||
* derived from this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, |
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#include "upb/mini_descriptor.h" |
||||
|
||||
#include <assert.h> |
||||
#include <inttypes.h> |
||||
#include <stddef.h> |
||||
#include <stdint.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
|
||||
#include "upb/def.h" |
||||
#include "upb/mini_table.h" |
||||
|
||||
// Must be last.
|
||||
#include "upb/port_def.inc" |
||||
|
||||
/* DescState ******************************************************************/ |
||||
|
||||
// Manages the storage for mini descriptor strings as they are being encoded.
|
||||
// TODO(b/234740652): Move some of this state directly into the encoder, maybe.
|
||||
|
||||
typedef struct { |
||||
upb_MtDataEncoder e; |
||||
size_t bufsize; |
||||
char* buf; |
||||
char* ptr; |
||||
} DescState; |
||||
|
||||
static void upb_DescState_Init(DescState* d) { |
||||
d->bufsize = kUpb_MtDataEncoder_MinSize * 2; |
||||
d->buf = NULL; |
||||
d->ptr = NULL; |
||||
} |
||||
|
||||
static bool upb_DescState_Grow(DescState* d, upb_Arena* a) { |
||||
const size_t oldbufsize = d->bufsize; |
||||
const int used = d->ptr - d->buf; |
||||
|
||||
if (!d->buf) { |
||||
d->buf = upb_Arena_Malloc(a, d->bufsize); |
||||
if (!d->buf) return false; |
||||
d->ptr = d->buf; |
||||
d->e.end = d->buf + d->bufsize; |
||||
} |
||||
|
||||
if (oldbufsize - used < kUpb_MtDataEncoder_MinSize) { |
||||
d->bufsize *= 2; |
||||
d->buf = upb_Arena_Realloc(a, d->buf, oldbufsize, d->bufsize); |
||||
if (!d->buf) return false; |
||||
d->ptr = d->buf + used; |
||||
d->e.end = d->buf + d->bufsize; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
static void upb_DescState_Emit(const DescState* d, upb_StringView* str) { |
||||
*str = upb_StringView_FromDataAndSize(d->buf, d->ptr - d->buf); |
||||
} |
||||
|
||||
/******************************************************************************/ |
||||
|
||||
// Type and Field accessors.
|
||||
|
||||
static inline bool upb_Type_IsPackable(upb_FieldType type) { |
||||
return (type != kUpb_FieldType_String && type != kUpb_FieldType_Group && |
||||
type != kUpb_FieldType_Message && type != kUpb_FieldType_Bytes); |
||||
} |
||||
|
||||
static inline bool upb_Field_IsOneof(const google_protobuf_FieldDescriptorProto* f) { |
||||
return google_protobuf_FieldDescriptorProto_has_oneof_index(f); |
||||
} |
||||
|
||||
static inline bool upb_Field_IsOptional(const google_protobuf_FieldDescriptorProto* f) { |
||||
const upb_Label label = google_protobuf_FieldDescriptorProto_label(f); |
||||
return label == kUpb_Label_Optional; |
||||
} |
||||
|
||||
static inline bool upb_Field_IsRepeated(const google_protobuf_FieldDescriptorProto* f) { |
||||
const upb_Label label = google_protobuf_FieldDescriptorProto_label(f); |
||||
return label == kUpb_Label_Repeated; |
||||
} |
||||
|
||||
static inline bool upb_Field_IsRequired(const google_protobuf_FieldDescriptorProto* f) { |
||||
const upb_Label label = google_protobuf_FieldDescriptorProto_label(f); |
||||
return label == kUpb_Label_Required; |
||||
} |
||||
|
||||
static inline bool upb_Field_IsPackable(const google_protobuf_FieldDescriptorProto* f) { |
||||
if (!upb_Field_IsRepeated(f)) return false; |
||||
|
||||
const upb_FieldType type = google_protobuf_FieldDescriptorProto_type(f); |
||||
return upb_Type_IsPackable(type); |
||||
} |
||||
|
||||
static bool upb_Field_IsPacked(const google_protobuf_FieldDescriptorProto* f, |
||||
upb_Syntax syntax) { |
||||
if (!upb_Field_IsPackable(f)) return false; |
||||
|
||||
const bool has_options = google_protobuf_FieldDescriptorProto_has_options(f); |
||||
const google_protobuf_FieldOptions* options = google_protobuf_FieldDescriptorProto_options(f); |
||||
|
||||
switch (syntax) { |
||||
case kUpb_Syntax_Proto2: |
||||
if (!has_options) return false; |
||||
break; |
||||
|
||||
default: |
||||
if (!has_options) return true; |
||||
if (!google_protobuf_FieldOptions_has_packed(options)) return true; |
||||
break; |
||||
} |
||||
|
||||
return google_protobuf_FieldOptions_packed(options); |
||||
} |
||||
|
||||
static inline int Field_OneofIndex(const google_protobuf_FieldDescriptorProto* f) { |
||||
return google_protobuf_FieldDescriptorProto_oneof_index(f); |
||||
} |
||||
|
||||
static bool upb_Field_HasPresence(const google_protobuf_FieldDescriptorProto* f, |
||||
upb_Syntax syntax) { |
||||
if (upb_Field_IsRepeated(f)) return false; |
||||
|
||||
const upb_FieldType type = google_protobuf_FieldDescriptorProto_type(f); |
||||
return type == kUpb_FieldType_Message || type == kUpb_FieldType_Group || |
||||
upb_Field_IsOneof(f) || syntax == kUpb_Syntax_Proto2; |
||||
} |
||||
|
||||
uint64_t upb_Field_Modifier(const google_protobuf_FieldDescriptorProto* f, |
||||
upb_Syntax syntax) { |
||||
uint64_t out = 0; |
||||
if (upb_Field_IsRepeated(f)) { |
||||
out |= kUpb_FieldModifier_IsRepeated; |
||||
} |
||||
if (upb_Field_IsPacked(f, syntax)) { |
||||
out |= kUpb_FieldModifier_IsPacked; |
||||
} |
||||
if (google_protobuf_FieldDescriptorProto_type(f) == kUpb_FieldType_Enum && |
||||
syntax == kUpb_Syntax_Proto2) { |
||||
out |= kUpb_FieldModifier_IsClosedEnum; |
||||
} |
||||
if (upb_Field_IsOptional(f) && !upb_Field_HasPresence(f, syntax)) { |
||||
out |= kUpb_FieldModifier_IsProto3Singular; |
||||
} |
||||
if (upb_Field_IsRequired(f)) { |
||||
out |= kUpb_FieldModifier_IsRequired; |
||||
} |
||||
return out; |
||||
} |
||||
|
||||
/******************************************************************************/ |
||||
|
||||
// Sort by enum value.
|
||||
static int upb_MiniDescriptor_CompareEnums(const void* a, const void* b) { |
||||
const google_protobuf_EnumValueDescriptorProto* A = *(void**)a; |
||||
const google_protobuf_EnumValueDescriptorProto* B = *(void**)b; |
||||
if ((uint32_t)google_protobuf_EnumValueDescriptorProto_number(A) < |
||||
(uint32_t)google_protobuf_EnumValueDescriptorProto_number(B)) |
||||
return -1; |
||||
if ((uint32_t)google_protobuf_EnumValueDescriptorProto_number(A) > |
||||
(uint32_t)google_protobuf_EnumValueDescriptorProto_number(B)) |
||||
return 1; |
||||
return 0; |
||||
} |
||||
|
||||
// Sort by field number.
|
||||
static int upb_MiniDescriptor_CompareFields(const void* a, const void* b) { |
||||
const google_protobuf_FieldDescriptorProto* A = *(void**)a; |
||||
const google_protobuf_FieldDescriptorProto* B = *(void**)b; |
||||
if (google_protobuf_FieldDescriptorProto_number(A) < |
||||
google_protobuf_FieldDescriptorProto_number(B)) |
||||
return -1; |
||||
if (google_protobuf_FieldDescriptorProto_number(A) > |
||||
google_protobuf_FieldDescriptorProto_number(B)) |
||||
return 1; |
||||
return 0; |
||||
} |
||||
|
||||
// Sort first by oneof index then by field number.
|
||||
static int upb_MiniDescriptor_CompareOneofs(const void* a, const void* b) { |
||||
const google_protobuf_FieldDescriptorProto* A = *(void**)a; |
||||
const google_protobuf_FieldDescriptorProto* B = *(void**)b; |
||||
const int indexA = upb_Field_IsOneof(A) ? Field_OneofIndex(A) : -1; |
||||
const int indexB = upb_Field_IsOneof(B) ? Field_OneofIndex(B) : -1; |
||||
if (indexA < indexB) return -1; |
||||
if (indexA > indexB) return 1; |
||||
if (google_protobuf_FieldDescriptorProto_number(A) < |
||||
google_protobuf_FieldDescriptorProto_number(B)) |
||||
return -1; |
||||
if (google_protobuf_FieldDescriptorProto_number(A) > |
||||
google_protobuf_FieldDescriptorProto_number(B)) |
||||
return 1; |
||||
return 0; |
||||
} |
||||
|
||||
upb_StringView upb_MiniDescriptor_EncodeEnum( |
||||
const google_protobuf_EnumDescriptorProto* enum_type, upb_Arena* a) { |
||||
upb_StringView out; |
||||
out.data = NULL; |
||||
out.size = 0; |
||||
|
||||
size_t len = 0; |
||||
const google_protobuf_EnumValueDescriptorProto* const* value_types = |
||||
google_protobuf_EnumDescriptorProto_value(enum_type, &len); |
||||
|
||||
// Copy and sort.
|
||||
google_protobuf_EnumValueDescriptorProto** sorted = upb_gmalloc(len * sizeof(void*)); |
||||
if (!sorted) goto err; |
||||
memcpy(sorted, value_types, len * sizeof(void*)); |
||||
qsort(sorted, len, sizeof(void*), upb_MiniDescriptor_CompareEnums); |
||||
|
||||
DescState s; |
||||
upb_DescState_Init(&s); |
||||
|
||||
upb_MtDataEncoder_StartEnum(&s.e); |
||||
|
||||
for (size_t i = 0; i < len; i++) { |
||||
if (!upb_DescState_Grow(&s, a)) goto err; |
||||
const uint32_t number = google_protobuf_EnumValueDescriptorProto_number(sorted[i]); |
||||
s.ptr = upb_MtDataEncoder_PutEnumValue(&s.e, s.ptr, number); |
||||
UPB_ASSERT(s.ptr); |
||||
} |
||||
|
||||
if (!upb_DescState_Grow(&s, a)) goto err; |
||||
s.ptr = upb_MtDataEncoder_EndEnum(&s.e, s.ptr); |
||||
UPB_ASSERT(s.ptr); |
||||
|
||||
upb_DescState_Emit(&s, &out); |
||||
|
||||
err: |
||||
if (sorted) upb_gfree(sorted); |
||||
return out; |
||||
} |
||||
|
||||
upb_StringView upb_MiniDescriptor_EncodeExtension( |
||||
const google_protobuf_FieldDescriptorProto* extension_type, upb_Syntax syntax, |
||||
upb_Arena* a) { |
||||
upb_StringView out; |
||||
out.data = NULL; |
||||
out.size = 0; |
||||
|
||||
DescState s; |
||||
upb_DescState_Init(&s); |
||||
|
||||
if (!upb_DescState_Grow(&s, a)) goto err; |
||||
upb_MtDataEncoder_StartMessage(&s.e, s.ptr, 0); |
||||
|
||||
const upb_FieldType type = google_protobuf_FieldDescriptorProto_type(extension_type); |
||||
const int number = google_protobuf_FieldDescriptorProto_number(extension_type); |
||||
const uint64_t modifier = upb_Field_Modifier(extension_type, syntax); |
||||
upb_MtDataEncoder_PutField(&s.e, s.ptr, type, number, modifier); |
||||
|
||||
upb_DescState_Emit(&s, &out); |
||||
|
||||
err: |
||||
return out; |
||||
} |
||||
|
||||
upb_StringView upb_MiniDescriptor_EncodeMessage( |
||||
const google_protobuf_DescriptorProto* message_type, upb_Syntax syntax, |
||||
upb_Arena* a) { |
||||
upb_StringView out; |
||||
out.data = NULL; |
||||
out.size = 0; |
||||
|
||||
size_t len = 0; |
||||
const google_protobuf_FieldDescriptorProto* const* field_types = |
||||
google_protobuf_DescriptorProto_field(message_type, &len); |
||||
|
||||
// Copy and sort.
|
||||
google_protobuf_FieldDescriptorProto** sorted = upb_gmalloc(len * sizeof(void*)); |
||||
if (!sorted) goto err; |
||||
memcpy(sorted, field_types, len * sizeof(void*)); |
||||
qsort(sorted, len, sizeof(void*), upb_MiniDescriptor_CompareFields); |
||||
|
||||
DescState s; |
||||
upb_DescState_Init(&s); |
||||
|
||||
if (!upb_DescState_Grow(&s, a)) goto err; |
||||
upb_MtDataEncoder_StartMessage(&s.e, s.ptr, 0); |
||||
|
||||
// Encode the fields.
|
||||
size_t oneof_fields = 0; |
||||
for (size_t i = 0; i < len; i++) { |
||||
google_protobuf_FieldDescriptorProto* field_type = sorted[i]; |
||||
if (upb_Field_IsOneof(field_type)) { |
||||
// Put all oneof fields at the beginning of the list for the next pass.
|
||||
sorted[oneof_fields++] = field_type; |
||||
} |
||||
|
||||
const upb_FieldType type = google_protobuf_FieldDescriptorProto_type(field_type); |
||||
const int number = google_protobuf_FieldDescriptorProto_number(field_type); |
||||
const uint64_t modifier = upb_Field_Modifier(field_type, syntax); |
||||
|
||||
if (!upb_DescState_Grow(&s, a)) goto err; |
||||
s.ptr = upb_MtDataEncoder_PutField(&s.e, s.ptr, type, number, modifier); |
||||
UPB_ASSERT(s.ptr); |
||||
} |
||||
|
||||
qsort(sorted, oneof_fields, sizeof(void*), upb_MiniDescriptor_CompareOneofs); |
||||
|
||||
// Encode the oneofs.
|
||||
int previous_index = -1; |
||||
for (size_t i = 0; i < oneof_fields; i++) { |
||||
google_protobuf_FieldDescriptorProto* field_type = sorted[i]; |
||||
if (!upb_Field_IsOneof(field_type)) continue; |
||||
|
||||
const int index = Field_OneofIndex(field_type); |
||||
if (previous_index != index) { |
||||
if (!upb_DescState_Grow(&s, a)) goto err; |
||||
s.ptr = upb_MtDataEncoder_StartOneof(&s.e, s.ptr); |
||||
UPB_ASSERT(s.ptr); |
||||
|
||||
previous_index = index; |
||||
} |
||||
|
||||
if (!upb_DescState_Grow(&s, a)) goto err; |
||||
s.ptr = upb_MtDataEncoder_PutOneofField( |
||||
&s.e, s.ptr, google_protobuf_FieldDescriptorProto_number(field_type)); |
||||
UPB_ASSERT(s.ptr); |
||||
} |
||||
|
||||
upb_DescState_Emit(&s, &out); |
||||
|
||||
err: |
||||
if (sorted) upb_gfree(sorted); |
||||
return out; |
||||
} |
@ -0,0 +1,60 @@ |
||||
/*
|
||||
* Copyright (c) 2009-2022, Google LLC |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* * Neither the name of Google LLC nor the |
||||
* names of its contributors may be used to endorse or promote products |
||||
* derived from this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, |
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef UPB_MINI_DESCRIPTOR_H_ |
||||
#define UPB_MINI_DESCRIPTOR_H_ |
||||
|
||||
#include "upb/def.h" |
||||
#include "upb/upb.h" |
||||
|
||||
// Must be last.
|
||||
#include "upb/port_def.inc" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/** upb_MiniDescriptor ********************************************************/ |
||||
|
||||
upb_StringView upb_MiniDescriptor_EncodeEnum( |
||||
const google_protobuf_EnumDescriptorProto* enum_type, upb_Arena* a); |
||||
|
||||
upb_StringView upb_MiniDescriptor_EncodeExtension( |
||||
const google_protobuf_FieldDescriptorProto* extension_type, upb_Syntax syntax, |
||||
upb_Arena* a); |
||||
|
||||
upb_StringView upb_MiniDescriptor_EncodeMessage( |
||||
const google_protobuf_DescriptorProto* message_type, upb_Syntax syntax, |
||||
upb_Arena* a); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#include "upb/port_undef.inc" |
||||
|
||||
#endif /* UPB_MINI_DESCRIPTOR_H_ */ |
@ -0,0 +1,287 @@ |
||||
/*
|
||||
* Copyright (c) 2009-2022, Google LLC |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* * Neither the name of Google LLC nor the |
||||
* names of its contributors may be used to endorse or promote products |
||||
* derived from this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, |
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#include "upbc/code_generator_request.h" |
||||
|
||||
#include <assert.h> |
||||
#include <inttypes.h> |
||||
#include <setjmp.h> |
||||
#include <stddef.h> |
||||
#include <stdint.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
|
||||
#include "google/protobuf/compiler/plugin.upb.h" |
||||
#include "upb/def.h" |
||||
#include "upb/mini_descriptor.h" |
||||
#include "upb/mini_table.h" |
||||
|
||||
// Must be last.
|
||||
#include "upb/port_def.inc" |
||||
|
||||
enum { |
||||
kErrArenaMalloc = 1, |
||||
kErrEnumName, |
||||
kErrExtensionName, |
||||
kErrFieldName, |
||||
kErrFilePackage, |
||||
kErrMapCollision, |
||||
kErrMiniDescriptorsSet, |
||||
kErrStateGrow, |
||||
}; |
||||
|
||||
/* upbc_PathState *************************************************************/ |
||||
|
||||
// Manages the current fully qualified path name as we dig down into a proto.
|
||||
// Basically just a string that grows and shrinks like a stack.
|
||||
|
||||
typedef struct { |
||||
size_t len; |
||||
char path[4000]; // TODO(salo): make this dynamic
|
||||
} upbc_PathState; |
||||
|
||||
static void upbc_PathState_Init(upbc_PathState* p) { p->len = 0; } |
||||
|
||||
static void upbc_PathState_Push(upbc_PathState* p, upb_StringView name) { |
||||
if (p->len) { |
||||
p->path[p->len++] = '.'; |
||||
} |
||||
memcpy(&p->path[p->len], name.data, name.size); |
||||
p->len += name.size; |
||||
} |
||||
|
||||
static void upbc_PathState_Pop(upbc_PathState* p, upb_StringView name) { |
||||
p->len -= name.size; |
||||
if (p->len) { |
||||
p->len--; |
||||
} |
||||
} |
||||
|
||||
static upb_StringView upbc_PathState_String(const upbc_PathState* p) { |
||||
return upb_StringView_FromDataAndSize(p->path, p->len); |
||||
} |
||||
|
||||
/******************************************************************************/ |
||||
|
||||
// Kitchen sink storage for the mini descriptor state.
|
||||
|
||||
typedef struct { |
||||
upb_Arena* a; |
||||
upb_Syntax syntax; |
||||
|
||||
upbc_CodeGeneratorRequest* out; |
||||
|
||||
jmp_buf err; |
||||
|
||||
upbc_PathState path; |
||||
} upbc_ScrapeState; |
||||
|
||||
static void upbc_ScrapeState_Init(upbc_ScrapeState* s, upb_Arena* a) { |
||||
s->a = a; |
||||
|
||||
upbc_PathState_Init(&s->path); |
||||
|
||||
s->out = upbc_CodeGeneratorRequest_new(a); |
||||
if (!s->out) UPB_LONGJMP(s->err, kErrArenaMalloc); |
||||
} |
||||
|
||||
static void upbc_ScrapeState_Push(upbc_ScrapeState* s, upb_StringView name) { |
||||
upbc_PathState_Push(&s->path, name); |
||||
|
||||
const upb_StringView key = upbc_PathState_String(&s->path); |
||||
if (upbc_CodeGeneratorRequest_mini_descriptors_get(s->out, key, NULL)) { |
||||
UPB_LONGJMP(s->err, kErrMapCollision); |
||||
} |
||||
} |
||||
|
||||
static void upbc_ScrapeState_Pop(upbc_ScrapeState* s, upb_StringView name) { |
||||
upbc_PathState_Pop(&s->path, name); |
||||
} |
||||
|
||||
static void upbc_ScrapeState_String(upbc_ScrapeState* s, |
||||
upb_StringView encoding) { |
||||
const upb_StringView path = upbc_PathState_String(&s->path); |
||||
bool ok = upbc_CodeGeneratorRequest_mini_descriptors_set(s->out, path, |
||||
encoding, s->a); |
||||
if (!ok) UPB_LONGJMP(s->err, kErrMiniDescriptorsSet); |
||||
} |
||||
|
||||
/******************************************************************************/ |
||||
|
||||
// File accessors.
|
||||
|
||||
static upb_Syntax upbc_File_Syntax(const google_protobuf_FileDescriptorProto* file) { |
||||
if (google_protobuf_FileDescriptorProto_has_syntax(file)) { |
||||
const upb_StringView syntax = google_protobuf_FileDescriptorProto_syntax(file); |
||||
const upb_StringView proto3 = upb_StringView_FromString("proto3"); |
||||
if (upb_StringView_IsEqual(syntax, proto3)) return kUpb_Syntax_Proto3; |
||||
} |
||||
return kUpb_Syntax_Proto2; |
||||
} |
||||
|
||||
/******************************************************************************/ |
||||
|
||||
// Forward declaration.
|
||||
static void upbc_Scrape_Messages(upbc_ScrapeState*, |
||||
const google_protobuf_DescriptorProto* const*, size_t); |
||||
|
||||
static void upbc_Scrape_Enum(upbc_ScrapeState* s, |
||||
const google_protobuf_EnumDescriptorProto* enum_type) { |
||||
if (!google_protobuf_EnumDescriptorProto_has_name(enum_type)) { |
||||
UPB_LONGJMP(s->err, kErrEnumName); |
||||
} |
||||
const upb_StringView name = google_protobuf_EnumDescriptorProto_name(enum_type); |
||||
|
||||
upbc_ScrapeState_Push(s, name); |
||||
|
||||
const upb_StringView encoding = |
||||
upb_MiniDescriptor_EncodeEnum(enum_type, s->a); |
||||
|
||||
upbc_ScrapeState_String(s, encoding); |
||||
upbc_ScrapeState_Pop(s, name); |
||||
} |
||||
|
||||
static void upbc_Scrape_Enums( |
||||
upbc_ScrapeState* s, const google_protobuf_EnumDescriptorProto* const* enum_types, |
||||
size_t len) { |
||||
for (size_t i = 0; i < len; i++) { |
||||
upbc_Scrape_Enum(s, enum_types[i]); |
||||
} |
||||
} |
||||
|
||||
static void upbc_Scrape_Extension( |
||||
upbc_ScrapeState* s, const google_protobuf_FieldDescriptorProto* extension_type) { |
||||
if (!google_protobuf_FieldDescriptorProto_has_name(extension_type)) { |
||||
UPB_LONGJMP(s->err, kErrExtensionName); |
||||
} |
||||
const upb_StringView name = google_protobuf_FieldDescriptorProto_name(extension_type); |
||||
|
||||
upbc_ScrapeState_Push(s, name); |
||||
|
||||
const upb_StringView encoding = |
||||
upb_MiniDescriptor_EncodeExtension(extension_type, s->syntax, s->a); |
||||
|
||||
upbc_ScrapeState_String(s, encoding); |
||||
upbc_ScrapeState_Pop(s, name); |
||||
} |
||||
|
||||
static void upbc_Scrape_Extensions( |
||||
const google_protobuf_FieldDescriptorProto* const* extension_types, size_t len, |
||||
upbc_ScrapeState* s) { |
||||
for (size_t i = 0; i < len; i++) { |
||||
upbc_Scrape_Extension(s, extension_types[i]); |
||||
} |
||||
} |
||||
|
||||
static void upbc_Scrape_File(upbc_ScrapeState* s, |
||||
const google_protobuf_FileDescriptorProto* file_type) { |
||||
if (!google_protobuf_FileDescriptorProto_has_package(file_type)) { |
||||
UPB_LONGJMP(s->err, kErrFilePackage); |
||||
} |
||||
const upb_StringView package = google_protobuf_FileDescriptorProto_package(file_type); |
||||
upbc_ScrapeState_Push(s, package); |
||||
|
||||
s->syntax = upbc_File_Syntax(file_type); |
||||
|
||||
size_t len = 0; |
||||
const google_protobuf_EnumDescriptorProto* const* enum_types = |
||||
google_protobuf_FileDescriptorProto_enum_type(file_type, &len); |
||||
upbc_Scrape_Enums(s, enum_types, len); |
||||
|
||||
const google_protobuf_FieldDescriptorProto* const* extension_types = |
||||
google_protobuf_FileDescriptorProto_extension(file_type, &len); |
||||
upbc_Scrape_Extensions(extension_types, len, s); |
||||
|
||||
const google_protobuf_DescriptorProto* const* message_types = |
||||
google_protobuf_FileDescriptorProto_message_type(file_type, &len); |
||||
upbc_Scrape_Messages(s, message_types, len); |
||||
|
||||
upbc_ScrapeState_Pop(s, package); |
||||
} |
||||
|
||||
static void upbc_Scrape_Files( |
||||
upbc_ScrapeState* s, const google_protobuf_FileDescriptorProto* const* file_types, |
||||
size_t len) { |
||||
for (size_t i = 0; i < len; i++) { |
||||
upbc_Scrape_File(s, file_types[i]); |
||||
} |
||||
} |
||||
|
||||
static void upbc_Scrape_Message(upbc_ScrapeState* s, |
||||
const google_protobuf_DescriptorProto* message_type) { |
||||
if (!google_protobuf_DescriptorProto_has_name(message_type)) return; |
||||
|
||||
const upb_StringView name = google_protobuf_DescriptorProto_name(message_type); |
||||
upbc_ScrapeState_Push(s, name); |
||||
|
||||
const upb_StringView encoding = |
||||
upb_MiniDescriptor_EncodeMessage(message_type, s->syntax, s->a); |
||||
upbc_ScrapeState_String(s, encoding); |
||||
|
||||
size_t len = 0; |
||||
const google_protobuf_EnumDescriptorProto* const* enum_types = |
||||
google_protobuf_DescriptorProto_enum_type(message_type, &len); |
||||
upbc_Scrape_Enums(s, enum_types, len); |
||||
|
||||
const google_protobuf_FieldDescriptorProto* const* extension_types = |
||||
google_protobuf_DescriptorProto_extension(message_type, &len); |
||||
upbc_Scrape_Extensions(extension_types, len, s); |
||||
|
||||
const google_protobuf_DescriptorProto* const* nested_types = |
||||
google_protobuf_DescriptorProto_nested_type(message_type, &len); |
||||
upbc_Scrape_Messages(s, nested_types, len); |
||||
|
||||
upbc_ScrapeState_Pop(s, name); |
||||
} |
||||
|
||||
static void upbc_Scrape_Messages( |
||||
upbc_ScrapeState* s, const google_protobuf_DescriptorProto* const* message_types, |
||||
size_t len) { |
||||
for (size_t i = 0; i < len; i++) { |
||||
upbc_Scrape_Message(s, message_types[i]); |
||||
} |
||||
} |
||||
|
||||
upbc_CodeGeneratorRequest* upbc_MakeCodeGeneratorRequest( |
||||
google_protobuf_compiler_CodeGeneratorRequest* request, upb_Arena* a, |
||||
upb_Status* status) { |
||||
upbc_ScrapeState s; |
||||
int err = UPB_SETJMP(s.err); |
||||
if (err) { |
||||
upb_Status_SetErrorFormat(status, "%s(): error %d", __func__, err); |
||||
return NULL; |
||||
} |
||||
upbc_ScrapeState_Init(&s, a); |
||||
|
||||
size_t len = 0; |
||||
const google_protobuf_FileDescriptorProto* const* file_types = |
||||
google_protobuf_compiler_CodeGeneratorRequest_proto_file(request, &len); |
||||
upbc_Scrape_Files(&s, file_types, len); |
||||
|
||||
upbc_CodeGeneratorRequest_set_request(s.out, request); |
||||
return s.out; |
||||
} |
@ -0,0 +1,52 @@ |
||||
/*
|
||||
* Copyright (c) 2009-2022, Google LLC |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* * Neither the name of Google LLC nor the |
||||
* names of its contributors may be used to endorse or promote products |
||||
* derived from this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, |
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef UPBC_CODE_GENERATOR_REQUEST_H_ |
||||
#define UPBC_CODE_GENERATOR_REQUEST_H_ |
||||
|
||||
#include "upb/def.h" |
||||
#include "upb/upb.h" |
||||
#include "upbc/code_generator_request.upb.h" |
||||
|
||||
// Must be last.
|
||||
#include "upb/port_def.inc" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
upbc_CodeGeneratorRequest* upbc_MakeCodeGeneratorRequest( |
||||
struct google_protobuf_compiler_CodeGeneratorRequest* request, upb_Arena* a, |
||||
upb_Status* s); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#include "upb/port_undef.inc" |
||||
|
||||
#endif /* UPBC_CODE_GENERATOR_REQUEST_H_ */ |
@ -0,0 +1,13 @@ |
||||
syntax = "proto2"; |
||||
|
||||
package upbc; |
||||
|
||||
import "google/protobuf/compiler/plugin.proto"; |
||||
|
||||
message CodeGeneratorRequest { |
||||
// The pb sent by protoc to its plugins. |
||||
optional google.protobuf.compiler.CodeGeneratorRequest request = 1; |
||||
|
||||
// Mini descriptors for the above pb, keyed by the fully qualified names. |
||||
map<string, string> mini_descriptors = 2; |
||||
} |
@ -0,0 +1,144 @@ |
||||
// Copyright (c) 2009-2022, Google LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of Google LLC nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
|
||||
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <assert.h> |
||||
|
||||
#include <iostream> |
||||
#include <string> |
||||
|
||||
#include "google/protobuf/compiler/plugin.upb.h" |
||||
#include "google/protobuf/compiler/plugin.upbdefs.h" |
||||
#include "upb/json_decode.h" |
||||
#include "upb/json_encode.h" |
||||
#include "upb/mini_descriptor.h" |
||||
#include "upb/upb.h" |
||||
#include "upbc/code_generator_request.h" |
||||
#include "upbc/code_generator_request.upb.h" |
||||
#include "upbc/code_generator_request.upbdefs.h" |
||||
#include "upbc/subprocess.h" |
||||
|
||||
static constexpr char kDefaultPlugin[] = "protoc_dart_plugin"; |
||||
|
||||
static std::string JsonEncode(const upbc_CodeGeneratorRequest* request, |
||||
upb_Arena* a) { |
||||
upb_DefPool* s = upb_DefPool_New(); |
||||
const upb_MessageDef* m = upbc_CodeGeneratorRequest_getmsgdef(s); |
||||
|
||||
upb_Status status; |
||||
upb_Status_Clear(&status); |
||||
|
||||
const size_t json_size = upb_JsonEncode(request, m, s, 0, NULL, 0, &status); |
||||
assert(upb_Status_IsOk(&status)); |
||||
|
||||
char* json_buf = (char*)upb_Arena_Malloc(a, json_size + 1); |
||||
|
||||
(void)upb_JsonEncode(request, m, s, 0, json_buf, json_size + 1, &status); |
||||
assert(upb_Status_IsOk(&status)); |
||||
|
||||
upb_DefPool_Free(s); |
||||
|
||||
return std::string(json_buf, json_size); |
||||
} |
||||
|
||||
static google_protobuf_compiler_CodeGeneratorResponse* JsonDecode( |
||||
const std::string& json, upb_Arena* a) { |
||||
google_protobuf_compiler_CodeGeneratorResponse* response = |
||||
google_protobuf_compiler_CodeGeneratorResponse_new(a); |
||||
|
||||
upb_DefPool* s = upb_DefPool_New(); |
||||
const upb_MessageDef* m = google_protobuf_compiler_CodeGeneratorResponse_getmsgdef(s); |
||||
|
||||
upb_Status status; |
||||
upb_Status_Clear(&status); |
||||
|
||||
(void)upb_JsonDecode(json.c_str(), json.size(), response, m, s, 0, a, |
||||
&status); |
||||
assert(upb_Status_IsOk(&status)); |
||||
|
||||
upb_DefPool_Free(s); |
||||
|
||||
return response; |
||||
} |
||||
|
||||
static std::string Serialize( |
||||
const google_protobuf_compiler_CodeGeneratorResponse* response, upb_Arena* a) { |
||||
size_t len = 0; |
||||
const char* buf = |
||||
google_protobuf_compiler_CodeGeneratorResponse_serialize(response, a, &len); |
||||
return std::string(buf, len); |
||||
} |
||||
|
||||
int main() { |
||||
upb_Arena* a = upb_Arena_New(); |
||||
|
||||
// Read (binary) stdin into a string.
|
||||
const std::string input = {std::istreambuf_iterator<char>(std::cin), |
||||
std::istreambuf_iterator<char>()}; |
||||
|
||||
// Parse the request.
|
||||
auto inner_request = google_protobuf_compiler_CodeGeneratorRequest_parse( |
||||
input.c_str(), input.size(), a); |
||||
|
||||
// Check the request for a plugin name.
|
||||
std::string plugin = kDefaultPlugin; |
||||
if (google_protobuf_compiler_CodeGeneratorRequest_has_parameter(inner_request)) { |
||||
auto param = google_protobuf_compiler_CodeGeneratorRequest_parameter(inner_request); |
||||
plugin = std::string(param.data, param.size); |
||||
} |
||||
|
||||
// Wrap the request inside a upbc_CodeGeneratorRequest.
|
||||
upb_Status status; |
||||
upb_Status_Clear(&status); |
||||
auto outer_request = upbc_MakeCodeGeneratorRequest(inner_request, a, &status); |
||||
if (!upb_Status_IsOk(&status)) { |
||||
std::cerr << status.msg << std::endl; |
||||
return -1; |
||||
} |
||||
|
||||
const std::string json_request = JsonEncode(outer_request, a); |
||||
|
||||
// Launch the subprocess.
|
||||
upbc::Subprocess subprocess; |
||||
subprocess.Start(plugin, upbc::Subprocess::SEARCH_PATH); |
||||
|
||||
// Exchange JSON strings with the subprocess.
|
||||
std::string json_response, error; |
||||
const bool ok = subprocess.Communicate(json_request, &json_response, &error); |
||||
if (!ok) { |
||||
// Dump the JSON request to stderr if we can't launch the next plugin.
|
||||
std::cerr << json_request << std::endl; |
||||
return -1; |
||||
} |
||||
|
||||
// Decode and serialize the JSON response.
|
||||
const auto response = JsonDecode(json_response, a); |
||||
const std::string output = Serialize(response, a); |
||||
|
||||
// Question: Is this sufficient for sending reliably to stdout?
|
||||
std::cout << output; |
||||
|
||||
upb_Arena_Free(a); |
||||
return 0; |
||||
} |
@ -0,0 +1,462 @@ |
||||
/*
|
||||
* Copyright (c) 2009-2022, Google LLC |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* * Neither the name of Google LLC nor the |
||||
* names of its contributors may be used to endorse or promote products |
||||
* derived from this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, |
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
// Shamelessly copied from the protobuf compiler's subprocess.cc
|
||||
// except this version passes strings instead of Messages.
|
||||
|
||||
#include "upbc/subprocess.h" |
||||
|
||||
#include <algorithm> |
||||
#include <cstring> |
||||
#include <iostream> |
||||
|
||||
#ifndef _WIN32 |
||||
#include <errno.h> |
||||
#include <signal.h> |
||||
#include <sys/select.h> |
||||
#include <sys/wait.h> |
||||
#endif |
||||
|
||||
#include "absl/strings/substitute.h" |
||||
#include "upb/upb.h" |
||||
|
||||
/* Must be last. */ |
||||
#include "upb/port_def.inc" |
||||
|
||||
namespace upbc { |
||||
|
||||
namespace { |
||||
char* portable_strdup(const char* s) { |
||||
char* ns = (char*)malloc(strlen(s) + 1); |
||||
if (ns != nullptr) { |
||||
strcpy(ns, s); |
||||
} |
||||
return ns; |
||||
} |
||||
} // namespace
|
||||
|
||||
#ifdef _WIN32 |
||||
|
||||
static void CloseHandleOrDie(HANDLE handle) { |
||||
if (!CloseHandle(handle)) { |
||||
GOOGLE_LOG(FATAL) << "CloseHandle: " |
||||
<< Subprocess::Win32ErrorMessage(GetLastError()); |
||||
} |
||||
} |
||||
|
||||
Subprocess::Subprocess() |
||||
: process_start_error_(ERROR_SUCCESS), |
||||
child_handle_(nullptr), |
||||
child_stdin_(nullptr), |
||||
child_stdout_(nullptr) {} |
||||
|
||||
Subprocess::~Subprocess() { |
||||
if (child_stdin_ != nullptr) { |
||||
CloseHandleOrDie(child_stdin_); |
||||
} |
||||
if (child_stdout_ != nullptr) { |
||||
CloseHandleOrDie(child_stdout_); |
||||
} |
||||
} |
||||
|
||||
void Subprocess::Start(const std::string& program, SearchMode search_mode) { |
||||
// Create the pipes.
|
||||
HANDLE stdin_pipe_read; |
||||
HANDLE stdin_pipe_write; |
||||
HANDLE stdout_pipe_read; |
||||
HANDLE stdout_pipe_write; |
||||
|
||||
if (!CreatePipe(&stdin_pipe_read, &stdin_pipe_write, nullptr, 0)) { |
||||
GOOGLE_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError()); |
||||
} |
||||
if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, nullptr, 0)) { |
||||
GOOGLE_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError()); |
||||
} |
||||
|
||||
// Make child side of the pipes inheritable.
|
||||
if (!SetHandleInformation(stdin_pipe_read, HANDLE_FLAG_INHERIT, |
||||
HANDLE_FLAG_INHERIT)) { |
||||
GOOGLE_LOG(FATAL) << "SetHandleInformation: " |
||||
<< Win32ErrorMessage(GetLastError()); |
||||
} |
||||
if (!SetHandleInformation(stdout_pipe_write, HANDLE_FLAG_INHERIT, |
||||
HANDLE_FLAG_INHERIT)) { |
||||
GOOGLE_LOG(FATAL) << "SetHandleInformation: " |
||||
<< Win32ErrorMessage(GetLastError()); |
||||
} |
||||
|
||||
// Setup STARTUPINFO to redirect handles.
|
||||
STARTUPINFOA startup_info; |
||||
ZeroMemory(&startup_info, sizeof(startup_info)); |
||||
startup_info.cb = sizeof(startup_info); |
||||
startup_info.dwFlags = STARTF_USESTDHANDLES; |
||||
startup_info.hStdInput = stdin_pipe_read; |
||||
startup_info.hStdOutput = stdout_pipe_write; |
||||
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); |
||||
|
||||
if (startup_info.hStdError == INVALID_HANDLE_VALUE) { |
||||
GOOGLE_LOG(FATAL) << "GetStdHandle: " << Win32ErrorMessage(GetLastError()); |
||||
} |
||||
|
||||
// Invoking cmd.exe allows for '.bat' files from the path as well as '.exe'.
|
||||
// Using a malloc'ed string because CreateProcess() can mutate its second
|
||||
// parameter.
|
||||
char* command_line = |
||||
portable_strdup(("cmd.exe /c \"" + program + "\"").c_str()); |
||||
|
||||
// Create the process.
|
||||
PROCESS_INFORMATION process_info; |
||||
|
||||
if (CreateProcessA((search_mode == SEARCH_PATH) ? nullptr : program.c_str(), |
||||
(search_mode == SEARCH_PATH) ? command_line : nullptr, |
||||
nullptr, // process security attributes
|
||||
nullptr, // thread security attributes
|
||||
TRUE, // inherit handles?
|
||||
0, // obscure creation flags
|
||||
nullptr, // environment (inherit from parent)
|
||||
nullptr, // current directory (inherit from parent)
|
||||
&startup_info, &process_info)) { |
||||
child_handle_ = process_info.hProcess; |
||||
CloseHandleOrDie(process_info.hThread); |
||||
child_stdin_ = stdin_pipe_write; |
||||
child_stdout_ = stdout_pipe_read; |
||||
} else { |
||||
process_start_error_ = GetLastError(); |
||||
CloseHandleOrDie(stdin_pipe_write); |
||||
CloseHandleOrDie(stdout_pipe_read); |
||||
} |
||||
|
||||
CloseHandleOrDie(stdin_pipe_read); |
||||
CloseHandleOrDie(stdout_pipe_write); |
||||
free(command_line); |
||||
} |
||||
|
||||
bool Subprocess::Communicate(const std::string& input_data, |
||||
std::string* output_data, std::string* error) { |
||||
if (process_start_error_ != ERROR_SUCCESS) { |
||||
*error = Win32ErrorMessage(process_start_error_); |
||||
return false; |
||||
} |
||||
|
||||
GOOGLE_CHECK(child_handle_ != nullptr) << "Must call Start() first."; |
||||
|
||||
int input_pos = 0; |
||||
|
||||
while (child_stdout_ != nullptr) { |
||||
HANDLE handles[2]; |
||||
int handle_count = 0; |
||||
|
||||
if (child_stdin_ != nullptr) { |
||||
handles[handle_count++] = child_stdin_; |
||||
} |
||||
if (child_stdout_ != nullptr) { |
||||
handles[handle_count++] = child_stdout_; |
||||
} |
||||
|
||||
DWORD wait_result = |
||||
WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE); |
||||
|
||||
HANDLE signaled_handle = nullptr; |
||||
if (wait_result >= WAIT_OBJECT_0 && |
||||
wait_result < WAIT_OBJECT_0 + handle_count) { |
||||
signaled_handle = handles[wait_result - WAIT_OBJECT_0]; |
||||
} else if (wait_result == WAIT_FAILED) { |
||||
GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: " |
||||
<< Win32ErrorMessage(GetLastError()); |
||||
} else { |
||||
GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: Unexpected return code: " |
||||
<< wait_result; |
||||
} |
||||
|
||||
if (signaled_handle == child_stdin_) { |
||||
DWORD n; |
||||
if (!WriteFile(child_stdin_, input_data.data() + input_pos, |
||||
input_data.size() - input_pos, &n, nullptr)) { |
||||
// Child closed pipe. Presumably it will report an error later.
|
||||
// Pretend we're done for now.
|
||||
input_pos = input_data.size(); |
||||
} else { |
||||
input_pos += n; |
||||
} |
||||
|
||||
if (input_pos == input_data.size()) { |
||||
// We're done writing. Close.
|
||||
CloseHandleOrDie(child_stdin_); |
||||
child_stdin_ = nullptr; |
||||
} |
||||
} else if (signaled_handle == child_stdout_) { |
||||
char buffer[4096]; |
||||
DWORD n; |
||||
|
||||
if (!ReadFile(child_stdout_, buffer, sizeof(buffer), &n, nullptr)) { |
||||
// We're done reading. Close.
|
||||
CloseHandleOrDie(child_stdout_); |
||||
child_stdout_ = nullptr; |
||||
} else { |
||||
output_data->append(buffer, n); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (child_stdin_ != nullptr) { |
||||
// Child did not finish reading input before it closed the output.
|
||||
// Presumably it exited with an error.
|
||||
CloseHandleOrDie(child_stdin_); |
||||
child_stdin_ = nullptr; |
||||
} |
||||
|
||||
DWORD wait_result = WaitForSingleObject(child_handle_, INFINITE); |
||||
|
||||
if (wait_result == WAIT_FAILED) { |
||||
GOOGLE_LOG(FATAL) << "WaitForSingleObject: " |
||||
<< Win32ErrorMessage(GetLastError()); |
||||
} else if (wait_result != WAIT_OBJECT_0) { |
||||
GOOGLE_LOG(FATAL) << "WaitForSingleObject: Unexpected return code: " |
||||
<< wait_result; |
||||
} |
||||
|
||||
DWORD exit_code; |
||||
if (!GetExitCodeProcess(child_handle_, &exit_code)) { |
||||
GOOGLE_LOG(FATAL) << "GetExitCodeProcess: " |
||||
<< Win32ErrorMessage(GetLastError()); |
||||
} |
||||
|
||||
CloseHandleOrDie(child_handle_); |
||||
child_handle_ = nullptr; |
||||
|
||||
if (exit_code != 0) { |
||||
*error = absl::Substitute("Plugin failed with status code $0.", exit_code); |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
std::string Subprocess::Win32ErrorMessage(DWORD error_code) { |
||||
char* message; |
||||
|
||||
// WTF?
|
||||
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
||||
FORMAT_MESSAGE_IGNORE_INSERTS, |
||||
nullptr, error_code, |
||||
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), |
||||
(LPSTR)&message, // NOT A BUG!
|
||||
0, nullptr); |
||||
|
||||
std::string result = message; |
||||
LocalFree(message); |
||||
return result; |
||||
} |
||||
|
||||
// ===================================================================
|
||||
|
||||
#else // _WIN32
|
||||
|
||||
Subprocess::Subprocess() |
||||
: child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {} |
||||
|
||||
Subprocess::~Subprocess() { |
||||
if (child_stdin_ != -1) { |
||||
close(child_stdin_); |
||||
} |
||||
if (child_stdout_ != -1) { |
||||
close(child_stdout_); |
||||
} |
||||
} |
||||
|
||||
void Subprocess::Start(const std::string& program, SearchMode search_mode) { |
||||
// Note that we assume that there are no other threads, thus we don't have to
|
||||
// do crazy stuff like using socket pairs or avoiding libc locks.
|
||||
|
||||
// [0] is read end, [1] is write end.
|
||||
int stdin_pipe[2]; |
||||
int stdout_pipe[2]; |
||||
|
||||
int p0 = pipe(stdin_pipe); |
||||
int p1 = pipe(stdout_pipe); |
||||
UPB_ASSERT(p0 != -1); |
||||
UPB_ASSERT(p1 != -1); |
||||
|
||||
char* argv[2] = {portable_strdup(program.c_str()), nullptr}; |
||||
|
||||
child_pid_ = fork(); |
||||
if (child_pid_ == -1) { |
||||
std::cerr << "fork: " << strerror(errno); |
||||
} else if (child_pid_ == 0) { |
||||
// We are the child.
|
||||
dup2(stdin_pipe[0], STDIN_FILENO); |
||||
dup2(stdout_pipe[1], STDOUT_FILENO); |
||||
|
||||
close(stdin_pipe[0]); |
||||
close(stdin_pipe[1]); |
||||
close(stdout_pipe[0]); |
||||
close(stdout_pipe[1]); |
||||
|
||||
switch (search_mode) { |
||||
case SEARCH_PATH: |
||||
execvp(argv[0], argv); |
||||
break; |
||||
case EXACT_NAME: |
||||
execv(argv[0], argv); |
||||
break; |
||||
} |
||||
|
||||
// Write directly to STDERR_FILENO to avoid stdio code paths that may do
|
||||
// stuff that is unsafe here.
|
||||
int ignored; |
||||
ignored = write(STDERR_FILENO, argv[0], strlen(argv[0])); |
||||
const char* message = |
||||
": program not found or is not executable\n" |
||||
"Please specify a program using absolute path or make sure " |
||||
"the program is available in your PATH system variable\n"; |
||||
ignored = write(STDERR_FILENO, message, strlen(message)); |
||||
(void)ignored; |
||||
|
||||
// Must use _exit() rather than exit() to avoid flushing output buffers
|
||||
// that will also be flushed by the parent.
|
||||
_exit(1); |
||||
} else { |
||||
free(argv[0]); |
||||
|
||||
close(stdin_pipe[0]); |
||||
close(stdout_pipe[1]); |
||||
|
||||
child_stdin_ = stdin_pipe[1]; |
||||
child_stdout_ = stdout_pipe[0]; |
||||
} |
||||
} |
||||
|
||||
bool Subprocess::Communicate(const std::string& input_data, |
||||
std::string* output_data, std::string* error) { |
||||
if (child_stdin_ == -1) { |
||||
std::cerr << "Must call Start() first." << std::endl; |
||||
UPB_ASSERT(child_stdin_ != -1); |
||||
} |
||||
|
||||
// The "sighandler_t" typedef is GNU-specific, so define our own.
|
||||
typedef void SignalHandler(int); |
||||
|
||||
// Make sure SIGPIPE is disabled so that if the child dies it doesn't kill us.
|
||||
SignalHandler* old_pipe_handler = signal(SIGPIPE, SIG_IGN); |
||||
|
||||
int input_pos = 0; |
||||
int max_fd = std::max(child_stdin_, child_stdout_); |
||||
|
||||
while (child_stdout_ != -1) { |
||||
fd_set read_fds; |
||||
fd_set write_fds; |
||||
FD_ZERO(&read_fds); |
||||
FD_ZERO(&write_fds); |
||||
if (child_stdout_ != -1) { |
||||
FD_SET(child_stdout_, &read_fds); |
||||
} |
||||
if (child_stdin_ != -1) { |
||||
FD_SET(child_stdin_, &write_fds); |
||||
} |
||||
|
||||
if (select(max_fd + 1, &read_fds, &write_fds, nullptr, nullptr) < 0) { |
||||
if (errno == EINTR) { |
||||
// Interrupted by signal. Try again.
|
||||
continue; |
||||
} else { |
||||
std::cerr << "select: " << strerror(errno) << std::endl; |
||||
UPB_ASSERT(0); |
||||
} |
||||
} |
||||
|
||||
if (child_stdin_ != -1 && FD_ISSET(child_stdin_, &write_fds)) { |
||||
int n = write(child_stdin_, input_data.data() + input_pos, |
||||
input_data.size() - input_pos); |
||||
if (n < 0) { |
||||
// Child closed pipe. Presumably it will report an error later.
|
||||
// Pretend we're done for now.
|
||||
input_pos = input_data.size(); |
||||
} else { |
||||
input_pos += n; |
||||
} |
||||
|
||||
if (input_pos == (int)input_data.size()) { |
||||
// We're done writing. Close.
|
||||
close(child_stdin_); |
||||
child_stdin_ = -1; |
||||
} |
||||
} |
||||
|
||||
if (child_stdout_ != -1 && FD_ISSET(child_stdout_, &read_fds)) { |
||||
char buffer[4096]; |
||||
int n = read(child_stdout_, buffer, sizeof(buffer)); |
||||
|
||||
if (n > 0) { |
||||
output_data->append(buffer, (size_t)n); |
||||
} else { |
||||
// We're done reading. Close.
|
||||
close(child_stdout_); |
||||
child_stdout_ = -1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (child_stdin_ != -1) { |
||||
// Child did not finish reading input before it closed the output.
|
||||
// Presumably it exited with an error.
|
||||
close(child_stdin_); |
||||
child_stdin_ = -1; |
||||
} |
||||
|
||||
int status; |
||||
while (waitpid(child_pid_, &status, 0) == -1) { |
||||
if (errno != EINTR) { |
||||
std::cerr << "waitpid: " << strerror(errno) << std::endl; |
||||
UPB_ASSERT(0); |
||||
} |
||||
} |
||||
|
||||
// Restore SIGPIPE handling.
|
||||
signal(SIGPIPE, old_pipe_handler); |
||||
|
||||
if (WIFEXITED(status)) { |
||||
if (WEXITSTATUS(status) != 0) { |
||||
int error_code = WEXITSTATUS(status); |
||||
*error = |
||||
absl::Substitute("Plugin failed with status code $0.", error_code); |
||||
return false; |
||||
} |
||||
} else if (WIFSIGNALED(status)) { |
||||
int signal = WTERMSIG(status); |
||||
*error = absl::Substitute("Plugin killed by signal $0.", signal); |
||||
return false; |
||||
} else { |
||||
*error = "Neither WEXITSTATUS nor WTERMSIG is true?"; |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
#endif // !_WIN32
|
||||
|
||||
} // namespace upbc
|
@ -0,0 +1,99 @@ |
||||
/*
|
||||
* Copyright (c) 2009-2022, Google LLC |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* * Neither the name of Google LLC nor the |
||||
* names of its contributors may be used to endorse or promote products |
||||
* derived from this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, |
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
// Shamelessly copied from the protobuf compiler's subprocess.h
|
||||
// except this version passes strings instead of Messages.
|
||||
|
||||
#ifndef THIRD_PARTY_UPB_UPBC_H_ |
||||
#define THIRD_PARTY_UPB_UPBC_H_ |
||||
|
||||
#ifdef _WIN32 |
||||
#ifndef WIN32_LEAN_AND_MEAN |
||||
#define WIN32_LEAN_AND_MEAN // right...
|
||||
#endif |
||||
#include <windows.h> |
||||
#else // _WIN32
|
||||
#include <sys/types.h> |
||||
#include <unistd.h> |
||||
#endif // !_WIN32
|
||||
#include <string> |
||||
|
||||
namespace upbc { |
||||
|
||||
// Utility class for launching sub-processes.
|
||||
class Subprocess { |
||||
public: |
||||
Subprocess(); |
||||
~Subprocess(); |
||||
|
||||
enum SearchMode { |
||||
SEARCH_PATH, // Use PATH environment variable.
|
||||
EXACT_NAME // Program is an exact file name; don't use the PATH.
|
||||
}; |
||||
|
||||
// Start the subprocess. Currently we don't provide a way to specify
|
||||
// arguments as protoc plugins don't have any.
|
||||
void Start(const std::string& program, SearchMode search_mode); |
||||
|
||||
// Pipe the input message to the subprocess's stdin, then close the pipe.
|
||||
// Meanwhile, read from the subprocess's stdout and copy into *output.
|
||||
// All this is done carefully to avoid deadlocks.
|
||||
// Returns true if successful. On any sort of error, returns false and sets
|
||||
// *error to a description of the problem.
|
||||
bool Communicate(const std::string& input_data, std::string* output_data, |
||||
std::string* error); |
||||
|
||||
#ifdef _WIN32 |
||||
// Given an error code, returns a human-readable error message. This is
|
||||
// defined here so that CommandLineInterface can share it.
|
||||
static std::string Win32ErrorMessage(DWORD error_code); |
||||
#endif |
||||
|
||||
private: |
||||
#ifdef _WIN32 |
||||
DWORD process_start_error_; |
||||
HANDLE child_handle_; |
||||
|
||||
// The file handles for our end of the child's pipes. We close each and
|
||||
// set it to NULL when no longer needed.
|
||||
HANDLE child_stdin_; |
||||
HANDLE child_stdout_; |
||||
|
||||
#else // _WIN32
|
||||
pid_t child_pid_; |
||||
|
||||
// The file descriptors for our end of the child's pipes. We close each and
|
||||
// set it to -1 when no longer needed.
|
||||
int child_stdin_; |
||||
int child_stdout_; |
||||
|
||||
#endif // !_WIN32
|
||||
}; |
||||
|
||||
} // namespace upbc
|
||||
|
||||
#endif // THIRD_PARTY_UPB_UPBC_H_
|
Loading…
Reference in new issue