| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "thrift/plugin/plugin.h" |
| |
| #ifdef _WIN32 |
| #include <fcntl.h> |
| #include <io.h> |
| #endif |
| |
| #include <cassert> |
| #include <functional> |
| #include <iostream> |
| #include <memory> |
| |
| #include <boost/range/adaptor/map.hpp> |
| #include <boost/range/algorithm/for_each.hpp> |
| |
| #include "thrift/generate/t_generator.h" |
| #include "thrift/plugin/type_util.h" |
| #include "thrift/protocol/TBinaryProtocol.h" |
| #include "thrift/transport/TBufferTransports.h" |
| #include "thrift/transport/TFDTransport.h" |
| #include "thrift/plugin/plugin_types.h" |
| |
| namespace apache { |
| namespace thrift { |
| namespace plugin { |
| |
| using apache::thrift::protocol::TBinaryProtocol; |
| using apache::thrift::transport::TFDTransport; |
| using apache::thrift::transport::TFramedTransport; |
| |
| #define THRIFT_CONVERT_FORWARD(from_type) \ |
| template <> \ |
| typename ToType<from_type>::type* convert_forward<from_type>(const from_type& from) |
| |
| #define THRIFT_CONVERT_COMPLETE_DECL(from_type) \ |
| template <> \ |
| void convert(const from_type& from, ToType<from_type>::type* to) |
| |
| #define THRIFT_CONVERT_UNARY_DECL(from_type) \ |
| template <> \ |
| typename ToType<from_type>::type* convert<from_type>(const from_type& from) |
| |
| #define THRIFT_CONVERSION_DECL(from_type) \ |
| THRIFT_CONVERT_FORWARD(from_type); \ |
| THRIFT_CONVERT_COMPLETE_DECL(from_type); \ |
| THRIFT_CONVERT_UNARY_DECL(from_type) |
| |
| #define THRIFT_CONVERT_COMPLETE(from_type) \ |
| THRIFT_CONVERSION_DECL(from_type) { \ |
| ToType<from_type>::type* to = convert_forward(from); \ |
| convert(from, to); \ |
| return to; \ |
| } \ |
| THRIFT_CONVERT_COMPLETE_DECL(from_type) |
| |
| #define THRIFT_CONVERSION(from_type, ...) \ |
| THRIFT_CONVERT_FORWARD(from_type) { \ |
| (void)from; \ |
| return new ToType<from_type>::type(__VA_ARGS__); \ |
| } \ |
| THRIFT_CONVERT_COMPLETE(from_type) |
| |
| #define THRIFT_ASSIGN_DOC() \ |
| do { \ |
| if (from.__isset.doc) \ |
| to->set_doc(from.doc); \ |
| } while (0) |
| |
| #define THRIFT_ASSIGN_ANNOTATIONS() \ |
| THRIFT_ASSIGN_DOC(); \ |
| do { \ |
| if (from.__isset.annotations) \ |
| to->annotations_ = from.annotations; \ |
| } while (0) |
| |
| #define THRIFT_ASSIGN_METADATA() \ |
| do { \ |
| to->set_name(from.metadata.name); \ |
| if (from.metadata.__isset.doc) \ |
| to->set_doc(from.metadata.doc); \ |
| if (from.metadata.__isset.annotations) \ |
| to->annotations_ = from.metadata.annotations; \ |
| } while (0) |
| |
| ::t_program* g_program = 0; |
| |
| template <typename C, typename S> |
| struct TypeCache { |
| C* operator[](const int64_t& k) { |
| typename std::map<int64_t, C*>::iterator it = cache.find(k); |
| if (it != cache.end()) { |
| return it->second; |
| } else { |
| typename std::map<int64_t, S>::const_iterator cit = source->find(k); |
| if (cit == source->end()) { |
| throw ThriftPluginError("Type not found"); |
| } |
| return (cache)[k] = convert_forward(cit->second); |
| } |
| } |
| |
| void compileAll() { |
| boost::for_each(*source | boost::adaptors::map_keys, |
| std::bind(&TypeCache::compile, this, std::placeholders::_1)); |
| } |
| |
| std::map<int64_t, S> const* source; |
| |
| void clear() { |
| source = nullptr ; |
| cache.clear() ; |
| } |
| |
| protected: |
| std::map<int64_t, C*> cache; |
| |
| private: |
| void compile(const int64_t& k) { |
| typename std::map<int64_t, S>::const_iterator cit = source->find(k); |
| if (cit == source->end()) { |
| throw ThriftPluginError("Type not found "); |
| } |
| convert(cit->second, (*this)[k]); |
| } |
| }; |
| std::map<int64_t, ::t_program*> g_program_cache; |
| TypeCache< ::t_type, t_type> g_type_cache; |
| TypeCache< ::t_const, t_const> g_const_cache; |
| TypeCache< ::t_service, t_service> g_service_cache; |
| |
| void clear_global_cache() { |
| g_type_cache.clear(); |
| g_const_cache.clear(); |
| g_service_cache.clear(); |
| } |
| |
| void set_global_cache(const TypeRegistry& from) { |
| g_type_cache.source = &from.types; |
| g_const_cache.source = &from.constants; |
| g_service_cache.source = &from.services; |
| |
| g_type_cache.compileAll(); |
| g_const_cache.compileAll(); |
| g_service_cache.compileAll(); |
| } |
| |
| template <typename T> |
| T* resolve_type(int64_t name) { |
| return reinterpret_cast<T*>(g_type_cache[name]); |
| } |
| |
| ::t_const* resolve_const(int64_t name) { |
| return g_const_cache[name]; |
| } |
| |
| ::t_service* resolve_service(int64_t name) { |
| return g_service_cache[name]; |
| } |
| |
| THRIFT_CONVERT_FORWARD(t_base_type) { |
| #define T_BASETYPE_CASE(type) \ |
| case t_base::TYPE_##type: \ |
| t = ::t_base_type::TYPE_##type; \ |
| break |
| |
| ::t_base_type::t_base t = ::t_base_type::TYPE_VOID; |
| bool is_binary = false; |
| switch (from.value) { |
| T_BASETYPE_CASE(VOID); |
| T_BASETYPE_CASE(STRING); |
| T_BASETYPE_CASE(BOOL); |
| T_BASETYPE_CASE(I8); |
| T_BASETYPE_CASE(I16); |
| T_BASETYPE_CASE(I32); |
| T_BASETYPE_CASE(I64); |
| T_BASETYPE_CASE(DOUBLE); |
| case t_base::TYPE_BINARY: |
| t = ::t_base_type::TYPE_STRING; |
| is_binary = true; |
| break; |
| } |
| ::t_base_type* to = new ::t_base_type(from.metadata.name, t); |
| to->set_binary(is_binary); |
| return to; |
| #undef T_BASETYPE_CASE |
| } |
| THRIFT_CONVERT_COMPLETE(t_base_type) { |
| THRIFT_ASSIGN_METADATA(); |
| } |
| |
| THRIFT_CONVERT_FORWARD(t_typedef) { |
| ::t_typedef* to; |
| if (from.forward) { |
| to = new ::t_typedef(g_program_cache[from.metadata.program_id], from.symbolic, true); |
| } else { |
| to = new ::t_typedef(g_program_cache[from.metadata.program_id], |
| resolve_type< ::t_type>(from.type), from.symbolic); |
| } |
| return to; |
| } |
| THRIFT_CONVERT_COMPLETE(t_typedef) { |
| THRIFT_ASSIGN_METADATA(); |
| } |
| THRIFT_CONVERSION(t_enum_value, from.name, from.value) { |
| assert(to); |
| THRIFT_ASSIGN_ANNOTATIONS(); |
| } |
| THRIFT_CONVERSION(t_enum, g_program_cache[from.metadata.program_id]) { |
| assert(to); |
| THRIFT_ASSIGN_METADATA(); |
| boost::for_each(from.constants | boost::adaptors::transformed(convert<t_enum_value>), |
| std::bind(&::t_enum::append, to, std::placeholders::_1)); |
| } |
| THRIFT_CONVERSION(t_list, resolve_type< ::t_type>(from.elem_type)) { |
| assert(to); |
| THRIFT_ASSIGN_METADATA(); |
| if (from.__isset.cpp_name) |
| to->set_cpp_name(from.cpp_name); |
| } |
| THRIFT_CONVERSION(t_set, resolve_type< ::t_type>(from.elem_type)) { |
| assert(to); |
| THRIFT_ASSIGN_METADATA(); |
| if (from.__isset.cpp_name) |
| to->set_cpp_name(from.cpp_name); |
| } |
| THRIFT_CONVERSION(t_map, |
| resolve_type< ::t_type>(from.key_type), |
| resolve_type< ::t_type>(from.val_type)) { |
| assert(to); |
| THRIFT_ASSIGN_METADATA(); |
| if (from.__isset.cpp_name) |
| to->set_cpp_name(from.cpp_name); |
| } |
| THRIFT_CONVERSION(t_const_value, ) { |
| #define T_CONST_VALUE_CASE(type) \ |
| if (from.__isset.type##_val) \ |
| to->set_##type(from.type##_val) |
| |
| assert(to); |
| if (from.__isset.map_val) { |
| to->set_map(); |
| for (std::map<t_const_value, t_const_value>::const_iterator it = from.map_val.begin(); |
| it != from.map_val.end(); it++) { |
| to->add_map(convert(it->first), convert(it->second)); |
| } |
| } else if (from.__isset.list_val) { |
| to->set_list(); |
| boost::for_each(from.list_val | boost::adaptors::transformed(&convert<t_const_value>), |
| std::bind(&::t_const_value::add_list, to, std::placeholders::_1)); |
| } else |
| T_CONST_VALUE_CASE(string); |
| else T_CONST_VALUE_CASE(integer); |
| else T_CONST_VALUE_CASE(double); |
| else if (from.__isset.const_identifier_val) { |
| to->set_identifier(from.const_identifier_val.identifier_val) ; |
| to->set_enum(resolve_type< ::t_enum>(from.const_identifier_val.enum_val)) ; |
| } |
| |
| #undef T_CONST_VALUE_CASE |
| } |
| THRIFT_CONVERSION(t_field, resolve_type< ::t_type>(from.type), from.name, from.key) { |
| assert(to); |
| THRIFT_ASSIGN_ANNOTATIONS(); |
| to->set_reference(from.reference); |
| to->set_req(static_cast< ::t_field::e_req>(from.req)); |
| if (from.__isset.value) { |
| to->set_value(convert(from.value)); |
| } |
| } |
| THRIFT_CONVERSION(t_struct, g_program_cache[from.metadata.program_id]) { |
| assert(to); |
| THRIFT_ASSIGN_METADATA(); |
| to->set_union(from.is_union); |
| to->set_xception(from.is_xception); |
| boost::for_each(from.members | boost::adaptors::transformed(convert<t_field>), |
| std::bind(&::t_struct::append, to, std::placeholders::_1)); |
| } |
| THRIFT_CONVERSION(t_const, |
| resolve_type< ::t_type>(from.type), |
| from.name, |
| convert<t_const_value>(from.value)) { |
| assert(to); |
| THRIFT_ASSIGN_DOC(); |
| } |
| |
| THRIFT_CONVERSION(t_function, |
| resolve_type< ::t_type>(from.returntype), |
| from.name, |
| resolve_type< ::t_struct>(from.arglist), |
| resolve_type< ::t_struct>(from.xceptions), |
| from.is_oneway) { |
| assert(to); |
| THRIFT_ASSIGN_DOC(); |
| } |
| |
| THRIFT_CONVERSION(t_service, g_program_cache[from.metadata.program_id]) { |
| assert(to); |
| assert(from.metadata.program_id); |
| assert(g_program_cache[from.metadata.program_id]); |
| THRIFT_ASSIGN_METADATA(); |
| |
| boost::for_each(from.functions | boost::adaptors::transformed(convert<t_function>), |
| std::bind(&::t_service::add_function, to, std::placeholders::_1)); |
| |
| if (from.__isset.extends_) |
| to->set_extends(resolve_service(from.extends_)); |
| } |
| |
| THRIFT_CONVERT_FORWARD(t_type) { |
| #define T_TYPE_CASE_FW_T(case, type) \ |
| if (from.__isset.case##_val) \ |
| return convert_forward<type>(from.case##_val) |
| #define T_TYPE_CASE_FW(case) T_TYPE_CASE_FW_T(case, t_##case) |
| |
| T_TYPE_CASE_FW(base_type); |
| T_TYPE_CASE_FW(typedef); |
| T_TYPE_CASE_FW(enum); |
| T_TYPE_CASE_FW(struct); |
| T_TYPE_CASE_FW_T(xception, t_struct); |
| T_TYPE_CASE_FW(list); |
| T_TYPE_CASE_FW(set); |
| T_TYPE_CASE_FW(map); |
| T_TYPE_CASE_FW(service); |
| throw ThriftPluginError("Invalid data: Type union has no value."); |
| #undef T_TYPE_CASE_FW_T |
| #undef T_TYPE_CASE_FW |
| } |
| THRIFT_CONVERT_COMPLETE(t_type) { |
| #define T_TYPE_CASE_T(case, type) \ |
| else if (from.__isset.case##_val) \ |
| convert<type, ::type>(from.case##_val, reinterpret_cast< ::type*>(to)) |
| #define T_TYPE_CASE(case) T_TYPE_CASE_T(case, t_##case) |
| |
| if (false) { |
| } |
| T_TYPE_CASE(base_type); |
| T_TYPE_CASE(typedef); |
| T_TYPE_CASE(enum); |
| T_TYPE_CASE(struct); |
| T_TYPE_CASE_T(xception, t_struct); |
| T_TYPE_CASE(list); |
| T_TYPE_CASE(set); |
| T_TYPE_CASE(map); |
| T_TYPE_CASE(service); |
| else { |
| throw ThriftPluginError("Invalid data: Type union has no value."); |
| } |
| #undef T_TYPE_CASE_T |
| #undef T_TYPE_CASE |
| } |
| |
| THRIFT_CONVERSION(t_scope, ) { |
| assert(to); |
| #define T_SCOPE_RESOLVE(type, name, a) \ |
| for (std::vector<int64_t>::const_iterator it = from.name##s.begin(); it != from.name##s.end(); \ |
| it++) { \ |
| ::t_##type* t = resolve_##type a(*it); \ |
| to->add_##name(t->get_name(), t); \ |
| } |
| T_SCOPE_RESOLVE(type, type, < ::t_type>); |
| T_SCOPE_RESOLVE(const, constant, ); |
| T_SCOPE_RESOLVE(service, service, ); |
| #undef T_SCOPE_RESOLVE |
| } |
| |
| THRIFT_CONVERT_FORWARD(t_program) { |
| ::t_program* to = new ::t_program(from.path, from.name); |
| for (std::vector<t_program>::const_iterator it = from.includes.begin(); it != from.includes.end(); |
| it++) { |
| to->add_include(convert_forward(*it)); |
| } |
| g_program_cache[from.program_id] = to; |
| return to; |
| } |
| THRIFT_CONVERT_COMPLETE(t_program) { |
| assert(to); |
| g_program = to; |
| convert<t_scope, ::t_scope>(from.scope, to->scope()); |
| THRIFT_ASSIGN_DOC(); |
| |
| to->set_out_path(from.out_path, from.out_path_is_absolute); |
| |
| boost::for_each(from.typedefs | boost::adaptors::transformed(&resolve_type< ::t_typedef>), |
| std::bind(&::t_program::add_typedef, to, std::placeholders::_1)); |
| boost::for_each(from.enums | boost::adaptors::transformed(&resolve_type< ::t_enum>), |
| std::bind(&::t_program::add_enum, to, std::placeholders::_1)); |
| for (std::vector<int64_t>::const_iterator it = from.objects.begin(); it != from.objects.end(); |
| it++) { |
| ::t_struct* t2 = resolve_type< ::t_struct>(*it); |
| if (t2->is_xception()) { |
| to->add_xception(t2); |
| } else { |
| to->add_struct(t2); |
| } |
| } |
| boost::for_each(from.consts | boost::adaptors::transformed(&resolve_const), |
| std::bind(&::t_program::add_const, to, std::placeholders::_1)); |
| boost::for_each(from.services | boost::adaptors::transformed(&resolve_service), |
| std::bind(&::t_program::add_service, to, std::placeholders::_1)); |
| |
| for (std::vector<t_program>::const_iterator it = from.includes.begin(); it != from.includes.end(); |
| it++) { |
| convert(*it, g_program_cache[it->program_id]); |
| } |
| std::for_each(from.c_includes.begin(), from.c_includes.end(), |
| std::bind(&::t_program::add_c_include, to, std::placeholders::_1)); |
| std::for_each(from.cpp_includes.begin(), from.cpp_includes.end(), |
| std::bind(&::t_program::add_cpp_include, to, std::placeholders::_1)); |
| for (std::map<std::string, std::string>::const_iterator it = from.namespaces.begin(); |
| it != from.namespaces.end(); it++) { |
| to->set_namespace(it->first, it->second); |
| } |
| |
| to->set_include_prefix(from.include_prefix); |
| to->set_namespace(from.namespace_); |
| } |
| |
| int GeneratorPlugin::exec(int, char* []) { |
| #ifdef _WIN32 |
| _setmode(fileno(stdin), _O_BINARY); |
| #endif |
| std::shared_ptr<TFramedTransport> transport( |
| new TFramedTransport(std::make_shared<TFDTransport>(fileno(stdin)))); |
| TBinaryProtocol proto(transport); |
| GeneratorInput input; |
| try { |
| input.read(&proto); |
| } catch (std::exception& err) { |
| std::cerr << "Error while receiving plugin data: " << err.what() << std::endl; |
| return -1; |
| } |
| initGlobals(); |
| ::t_program* p = g_program = convert_forward(input.program); |
| set_global_cache(input.type_registry); |
| convert(input.program, p); |
| |
| int ret = generate(p, input.parsed_options); |
| clearGlobals(); |
| |
| return ret; |
| } |
| |
| ::t_const_value::t_const_value_type const_value_case(const t_const_value& v) { |
| if (v.__isset.map_val) |
| return ::t_const_value::CV_MAP; |
| if (v.__isset.list_val) |
| return ::t_const_value::CV_LIST; |
| if (v.__isset.string_val) |
| return ::t_const_value::CV_STRING; |
| if (v.__isset.integer_val) |
| return ::t_const_value::CV_INTEGER; |
| if (v.__isset.double_val) |
| return ::t_const_value::CV_DOUBLE; |
| if (v.__isset.const_identifier_val) |
| return ::t_const_value::CV_IDENTIFIER; |
| throw ThriftPluginError("Unknown const value type"); |
| } |
| |
| bool t_const_value::operator<(const t_const_value& that) const { |
| ::t_const_value::t_const_value_type t1 = const_value_case(*this); |
| ::t_const_value::t_const_value_type t2 = const_value_case(that); |
| if (t1 != t2) |
| return t1 < t2; |
| switch (t1) { |
| case ::t_const_value::CV_INTEGER: |
| return integer_val < that.integer_val; |
| case ::t_const_value::CV_DOUBLE: |
| return double_val < that.double_val; |
| case ::t_const_value::CV_STRING: |
| return string_val < that.string_val; |
| case ::t_const_value::CV_MAP: |
| if (that.map_val.empty()) |
| return false; |
| else if (map_val.empty()) |
| return true; |
| else |
| return map_val.begin()->first < that.map_val.begin()->first; |
| case ::t_const_value::CV_LIST: |
| if (that.list_val.empty()) |
| return false; |
| else if (list_val.empty()) |
| return true; |
| else |
| return list_val.front() < that.list_val.front(); |
| case ::t_const_value::CV_IDENTIFIER: |
| return integer_val < that.integer_val; |
| } |
| throw ThriftPluginError("Unknown const value type"); |
| } |
| } |
| } |
| } |