Home | History | Annotate | Download | only in cpp
      1 // Copyright 2010 Google Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License"); you
      4 // may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
     12 // implied. See the License for the specific language governing
     13 // permissions and limitations under the License.
     14 
     15 #include "protobuf_v8.h"
     16 
     17 #include <map>
     18 #include <string>
     19 #include <iostream>
     20 #include <sstream>
     21 
     22 #include <google/protobuf/dynamic_message.h>
     23 #include <google/protobuf/descriptor.h>
     24 #include <google/protobuf/descriptor.pb.h>
     25 
     26 #include "logging.h"
     27 #include "util.h"
     28 
     29 #include "node_buffer.h"
     30 #include "node_object_wrap.h"
     31 
     32 #include "node_util.h"
     33 
     34 //#define PROTOBUF_V8_DEBUG
     35 #ifdef  PROTOBUF_V8_DEBUG
     36 
     37 #define DBG(...) LOGD(__VA_ARGS__)
     38 
     39 #else
     40 
     41 #define DBG(...)
     42 
     43 #endif
     44 
     45 using google::protobuf::Descriptor;
     46 using google::protobuf::DescriptorPool;
     47 using google::protobuf::DynamicMessageFactory;
     48 using google::protobuf::FieldDescriptor;
     49 using google::protobuf::FileDescriptorSet;
     50 using google::protobuf::Message;
     51 using google::protobuf::Reflection;
     52 
     53 //using ObjectWrap;
     54 //using Buffer;
     55 
     56 using std::map;
     57 using std::string;
     58 
     59 using v8::Array;
     60 using v8::AccessorInfo;
     61 using v8::Arguments;
     62 using v8::Boolean;
     63 using v8::Context;
     64 using v8::External;
     65 using v8::Function;
     66 using v8::FunctionTemplate;
     67 using v8::Integer;
     68 using v8::Handle;
     69 using v8::HandleScope;
     70 using v8::InvocationCallback;
     71 using v8::Local;
     72 using v8::NamedPropertyGetter;
     73 using v8::Number;
     74 using v8::Object;
     75 using v8::ObjectTemplate;
     76 using v8::Persistent;
     77 using v8::Script;
     78 using v8::String;
     79 using v8::Value;
     80 using v8::V8;
     81 
     82 namespace protobuf_v8 {
     83 
     84   template <typename T>
     85   static T* UnwrapThis(const Arguments& args) {
     86     return ObjectWrap::Unwrap<T>(args.This());
     87   }
     88 
     89   template <typename T>
     90   static T* UnwrapThis(const AccessorInfo& args) {
     91     return ObjectWrap::Unwrap<T>(args.This());
     92   }
     93 
     94   Persistent<FunctionTemplate> SchemaTemplate;
     95   Persistent<FunctionTemplate> TypeTemplate;
     96   Persistent<FunctionTemplate> ParseTemplate;
     97   Persistent<FunctionTemplate> SerializeTemplate;
     98 
     99   class Schema : public ObjectWrap {
    100   public:
    101     Schema(Handle<Object> self, const DescriptorPool* pool)
    102         : pool_(pool) {
    103       DBG("Schema::Schema E:");
    104       factory_.SetDelegateToGeneratedFactory(true);
    105       self->SetInternalField(1, Array::New());
    106       Wrap(self);
    107       DBG("Schema::Schema X:");
    108     }
    109 
    110     virtual ~Schema() {
    111       DBG("~Schema::Schema E:");
    112       if (pool_ != DescriptorPool::generated_pool())
    113         delete pool_;
    114       DBG("~Schema::Schema X:");
    115     }
    116 
    117     class Type : public ObjectWrap {
    118     public:
    119       Schema* schema_;
    120       const Descriptor* descriptor_;
    121 
    122       Message* NewMessage() const {
    123         DBG("Type::NewMessage() EX:");
    124         return schema_->NewMessage(descriptor_);
    125       }
    126 
    127       Handle<Function> Constructor() const {
    128         DBG("Type::Constrocutor() EX:");
    129         return Handle<Function>::Cast(handle_->GetInternalField(2));
    130       }
    131 
    132       Local<Object> NewObject(Handle<Value> properties) const {
    133         DBG("Type::NewObjext(properties) EX:");
    134         return Constructor()->NewInstance(1, &properties);
    135       }
    136 
    137       Type(Schema* schema, const Descriptor* descriptor, Handle<Object> self)
    138         : schema_(schema), descriptor_(descriptor) {
    139         DBG("Type::Type(schema, descriptor, self) E:");
    140         // Generate functions for bulk conversion between a JS object
    141         // and an array in descriptor order:
    142         //   from = function(arr) { this.f0 = arr[0]; this.f1 = arr[1]; ... }
    143         //   to   = function()    { return [ this.f0, this.f1, ... ] }
    144         // This is faster than repeatedly calling Get/Set on a v8::Object.
    145         std::ostringstream from, to;
    146         from << "(function(arr) { if(arr) {";
    147         to << "(function() { return [ ";
    148 
    149         for (int i = 0; i < descriptor->field_count(); i++) {
    150           from <<
    151             "var x = arr[" << i << "]; "
    152             "if(x !== undefined) this['" <<
    153             descriptor->field(i)->camelcase_name() <<
    154             "'] = x; ";
    155 
    156           if (i > 0) to << ", ";
    157           to << "this['" << descriptor->field(i)->camelcase_name() << "']";
    158           DBG("field name=%s", descriptor->field(i)->name().c_str());
    159         }
    160 
    161         from << " }})";
    162         to << " ]; })";
    163 
    164         // managed type->schema link
    165         self->SetInternalField(1, schema_->handle_);
    166 
    167         Handle<Function> constructor = Handle<Function>::Cast(
    168           Script::Compile(String::New(from.str().c_str()))->Run());
    169         constructor->SetHiddenValue(String::New("type"), self);
    170 
    171         Handle<Function> bind = Handle<Function>::Cast(
    172           Script::Compile(String::New(
    173               "(function(self) {"
    174               "  var f = this;"
    175               "  return function(arg) {"
    176               "    return f.call(self, arg);"
    177               "  };"
    178               "})"))->Run());
    179         Handle<Value> arg = self;
    180         constructor->Set(String::New("parse"), bind->Call(ParseTemplate->GetFunction(), 1, &arg));
    181         constructor->Set(String::New("serialize"), bind->Call(SerializeTemplate->GetFunction(), 1, &arg));
    182         self->SetInternalField(2, constructor);
    183         self->SetInternalField(3, Script::Compile(String::New(to.str().c_str()))->Run());
    184 
    185         Wrap(self);
    186         DBG("Type::Type(schema, descriptor, self) X:");
    187       }
    188 
    189 #define GET(TYPE)                                                        \
    190       (index >= 0 ?                                                      \
    191        reflection->GetRepeated##TYPE(instance, field, index) :           \
    192        reflection->Get##TYPE(instance, field))
    193 
    194       static Handle<Value> ToJs(const Message& instance,
    195                                 const Reflection* reflection,
    196                                 const FieldDescriptor* field,
    197                                 const Type* message_type,
    198                                 int index) {
    199         DBG("Type::ToJs(instance, refelction, field, message_type) E:");
    200         switch (field->cpp_type()) {
    201         case FieldDescriptor::CPPTYPE_MESSAGE:
    202           DBG("Type::ToJs CPPTYPE_MESSAGE");
    203           return message_type->ToJs(GET(Message));
    204         case FieldDescriptor::CPPTYPE_STRING: {
    205           DBG("Type::ToJs CPPTYPE_STRING");
    206           const string& value = GET(String);
    207           return String::New(value.data(), value.length());
    208         }
    209         case FieldDescriptor::CPPTYPE_INT32:
    210           DBG("Type::ToJs CPPTYPE_INT32");
    211           return Integer::New(GET(Int32));
    212         case FieldDescriptor::CPPTYPE_UINT32:
    213           DBG("Type::ToJs CPPTYPE_UINT32");
    214           return Integer::NewFromUnsigned(GET(UInt32));
    215         case FieldDescriptor::CPPTYPE_INT64:
    216           DBG("Type::ToJs CPPTYPE_INT64");
    217           return Number::New(GET(Int64));
    218         case FieldDescriptor::CPPTYPE_UINT64:
    219           DBG("Type::ToJs CPPTYPE_UINT64");
    220           return Number::New(GET(UInt64));
    221         case FieldDescriptor::CPPTYPE_FLOAT:
    222           DBG("Type::ToJs CPPTYPE_FLOAT");
    223           return Number::New(GET(Float));
    224         case FieldDescriptor::CPPTYPE_DOUBLE:
    225           DBG("Type::ToJs CPPTYPE_DOUBLE");
    226           return Number::New(GET(Double));
    227         case FieldDescriptor::CPPTYPE_BOOL:
    228           DBG("Type::ToJs CPPTYPE_BOOL");
    229           return Boolean::New(GET(Bool));
    230         case FieldDescriptor::CPPTYPE_ENUM:
    231           DBG("Type::ToJs CPPTYPE_ENUM");
    232           return String::New(GET(Enum)->name().c_str());
    233         }
    234 
    235         return Handle<Value>();  // NOTREACHED
    236       }
    237 #undef GET
    238 
    239       Handle<Object> ToJs(const Message& instance) const {
    240         DBG("Type::ToJs(Message) E:");
    241         const Reflection* reflection = instance.GetReflection();
    242         const Descriptor* descriptor = instance.GetDescriptor();
    243 
    244         Handle<Array> properties = Array::New(descriptor->field_count());
    245         for (int i = 0; i < descriptor->field_count(); i++) {
    246           HandleScope scope;
    247 
    248           const FieldDescriptor* field = descriptor->field(i);
    249           bool repeated = field->is_repeated();
    250           if (repeated && !reflection->FieldSize(instance, field)) {
    251             DBG("Ignore repeated field with no size in reflection data");
    252             continue;
    253           }
    254           if (!repeated && !reflection->HasField(instance, field)) {
    255             DBG("Ignore field with no field in relfection data");
    256             continue;
    257           }
    258 
    259           const Type* child_type =
    260             (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
    261             schema_->GetType(field->message_type()) : NULL;
    262 
    263           Handle<Value> value;
    264           if (field->is_repeated()) {
    265             int size = reflection->FieldSize(instance, field);
    266             Handle<Array> array = Array::New(size);
    267             for (int j = 0; j < size; j++) {
    268               Handle<Value> index = Number::New(i);
    269               array->Set(index, ToJs(instance, reflection, field, child_type, j));
    270             }
    271             value = array;
    272           } else {
    273             value = ToJs(instance, reflection, field, child_type, -1);
    274           }
    275 
    276           DBG("Type::ToJs: set property[%d]=%s", i, ToCString(value));
    277           Handle<Value> key = Number::New(i);
    278           properties->Set(key, value);
    279         }
    280 
    281         DBG("Type::ToJs(Message) X:");
    282         return NewObject(properties);
    283       }
    284 
    285       static Handle<Value> Parse(const Arguments& args) {
    286         DBG("Type::Parse(args) E:");
    287         Type* type = UnwrapThis<Type>(args);
    288         Buffer* buf = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
    289 
    290         Message* message = type->NewMessage();
    291         message->ParseFromArray(buf->data(), buf->length());
    292         Handle<Object> result = type->ToJs(*message);
    293         delete message;
    294 
    295         DBG("Type::Parse(args) X:");
    296         return result;
    297       }
    298 
    299 #define SET(TYPE, EXPR)                                                 \
    300       if (repeated) reflection->Add##TYPE(instance, field, EXPR);       \
    301       else reflection->Set##TYPE(instance, field, EXPR)
    302 
    303       static bool ToProto(Message* instance,
    304                           const FieldDescriptor* field,
    305                           Handle<Value> value,
    306                           const Type* type,
    307                           bool repeated) {
    308         DBG("Type::ToProto(instance, field, value, type, repeated) E:");
    309         bool ok = true;
    310         HandleScope scope;
    311 
    312         DBG("Type::ToProto field->name()=%s", field->name().c_str());
    313         const Reflection* reflection = instance->GetReflection();
    314         switch (field->cpp_type()) {
    315         case FieldDescriptor::CPPTYPE_MESSAGE:
    316           DBG("Type::ToProto CPPTYPE_MESSAGE");
    317           ok = type->ToProto(repeated ?
    318                                 reflection->AddMessage(instance, field) :
    319                                 reflection->MutableMessage(instance, field),
    320                                     Handle<Object>::Cast(value));
    321           break;
    322         case FieldDescriptor::CPPTYPE_STRING: {
    323           DBG("Type::ToProto CPPTYPE_STRING");
    324           String::AsciiValue ascii(value);
    325           SET(String, string(*ascii, ascii.length()));
    326           break;
    327         }
    328         case FieldDescriptor::CPPTYPE_INT32:
    329           DBG("Type::ToProto CPPTYPE_INT32");
    330           SET(Int32, value->NumberValue());
    331           break;
    332         case FieldDescriptor::CPPTYPE_UINT32:
    333           DBG("Type::ToProto CPPTYPE_UINT32");
    334           SET(UInt32, value->NumberValue());
    335           break;
    336         case FieldDescriptor::CPPTYPE_INT64:
    337           DBG("Type::ToProto CPPTYPE_INT64");
    338           SET(Int64, value->NumberValue());
    339           break;
    340         case FieldDescriptor::CPPTYPE_UINT64:
    341           DBG("Type::ToProto CPPTYPE_UINT64");
    342           SET(UInt64, value->NumberValue());
    343           break;
    344         case FieldDescriptor::CPPTYPE_FLOAT:
    345           DBG("Type::ToProto CPPTYPE_FLOAT");
    346           SET(Float, value->NumberValue());
    347           break;
    348         case FieldDescriptor::CPPTYPE_DOUBLE:
    349           DBG("Type::ToProto CPPTYPE_DOUBLE");
    350           SET(Double, value->NumberValue());
    351           break;
    352         case FieldDescriptor::CPPTYPE_BOOL:
    353           DBG("Type::ToProto CPPTYPE_BOOL");
    354           SET(Bool, value->BooleanValue());
    355           break;
    356         case FieldDescriptor::CPPTYPE_ENUM:
    357           DBG("Type::ToProto CPPTYPE_ENUM");
    358 
    359           // Don't use SET as vd can be NULL
    360           char error_buff[256];
    361           const google::protobuf::EnumValueDescriptor* vd;
    362           int i32_value = 0;
    363           const char *str_value = NULL;
    364           const google::protobuf::EnumDescriptor* ed = field->enum_type();
    365 
    366           if (value->IsNumber()) {
    367             i32_value = value->Int32Value();
    368             vd = ed->FindValueByNumber(i32_value);
    369             if (vd == NULL) {
    370               snprintf(error_buff, sizeof(error_buff),
    371                   "Type::ToProto Bad enum value, %d is not a member of enum %s",
    372                       i32_value, ed->full_name().c_str());
    373             }
    374           } else {
    375             str_value = ToCString(value);
    376             // TODO: Why can str_value be corrupted sometimes?
    377             LOGD("str_value=%s", str_value);
    378             vd = ed->FindValueByName(str_value);
    379             if (vd == NULL) {
    380               snprintf(error_buff, sizeof(error_buff),
    381                   "Type::ToProto Bad enum value, %s is not a member of enum %s",
    382                       str_value, ed->full_name().c_str());
    383             }
    384           }
    385           if (vd != NULL) {
    386             if (repeated) {
    387                reflection->AddEnum(instance, field, vd);
    388             } else {
    389                reflection->SetEnum(instance, field, vd);
    390             }
    391           } else {
    392             v8::ThrowException(String::New(error_buff));
    393             ok = false;
    394           }
    395           break;
    396         }
    397         DBG("Type::ToProto(instance, field, value, type, repeated) X: ok=%d", ok);
    398         return ok;
    399       }
    400 #undef SET
    401 
    402       bool ToProto(Message* instance, Handle<Object> src) const {
    403         DBG("ToProto(Message *, Handle<Object>) E:");
    404 
    405         Handle<Function> to_array = Handle<Function>::Cast(handle_->GetInternalField(3));
    406         Handle<Array> properties = Handle<Array>::Cast(to_array->Call(src, 0, NULL));
    407         bool ok = true;
    408         for (int i = 0; ok && (i < descriptor_->field_count()); i++) {
    409           Handle<Value> value = properties->Get(Number::New(i));
    410           if (value->IsUndefined()) continue;
    411 
    412           const FieldDescriptor* field = descriptor_->field(i);
    413           const Type* child_type =
    414             (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
    415             schema_->GetType(field->message_type()) : NULL;
    416           if (field->is_repeated()) {
    417             if(!value->IsArray()) {
    418               ok = ToProto(instance, field, value, child_type, true);
    419             } else {
    420               Handle<Array> array = Handle<Array>::Cast(value);
    421               int length = array->Length();
    422               for (int j = 0; ok && (j < length); j++) {
    423                 ok = ToProto(instance, field, array->Get(Number::New(j)), child_type, true);
    424               }
    425             }
    426           } else {
    427             ok = ToProto(instance, field, value, child_type, false);
    428           }
    429         }
    430         DBG("ToProto(Message *, Handle<Object>) X: ok=%d", ok);
    431         return ok;
    432       }
    433 
    434       static Handle<Value> Serialize(const Arguments& args) {
    435         Handle<Value> result;
    436         DBG("Serialize(Arguments&) E:");
    437         if (!args[0]->IsObject()) {
    438           DBG("Serialize(Arguments&) X: not an object");
    439           return v8::ThrowException(args[0]);
    440         }
    441 
    442         Type* type = UnwrapThis<Type>(args);
    443         Message* message = type->NewMessage();
    444         if (type->ToProto(message, Handle<Object>::Cast(args[0]))) {
    445           int length = message->ByteSize();
    446           Buffer* buffer = Buffer::New(length);
    447           message->SerializeWithCachedSizesToArray((google::protobuf::uint8*)buffer->data());
    448           delete message;
    449 
    450           result = buffer->handle_;
    451         } else {
    452           result = v8::Undefined();
    453         }
    454         DBG("Serialize(Arguments&) X");
    455         return result;
    456       }
    457 
    458       static Handle<Value> ToString(const Arguments& args) {
    459         return String::New(UnwrapThis<Type>(args)->descriptor_->full_name().c_str());
    460       }
    461     };
    462 
    463     Message* NewMessage(const Descriptor* descriptor) {
    464       DBG("Schema::NewMessage(descriptor) EX:");
    465       return factory_.GetPrototype(descriptor)->New();
    466     }
    467 
    468     Type* GetType(const Descriptor* descriptor) {
    469       DBG("Schema::GetType(descriptor) E:");
    470       Type* result = types_[descriptor];
    471       if (result) return result;
    472 
    473       result = types_[descriptor] =
    474         new Type(this, descriptor, TypeTemplate->GetFunction()->NewInstance());
    475 
    476       // managed schema->[type] link
    477       Handle<Array> types = Handle<Array>::Cast(handle_->GetInternalField(1));
    478       Handle<Value> key = Number::New(types->Length());
    479       types->Set(key, result->handle_);
    480       DBG("Schema::GetType(descriptor) X:");
    481       return result;
    482     }
    483 
    484     const DescriptorPool* pool_;
    485     map<const Descriptor*, Type*> types_;
    486     DynamicMessageFactory factory_;
    487 
    488     static Handle<Value> GetType(const Local<String> name,
    489                                  const AccessorInfo& args) {
    490       DBG("Schema::GetType(name, args) E:");
    491       Schema* schema = UnwrapThis<Schema>(args);
    492       const Descriptor* descriptor =
    493         schema->pool_->FindMessageTypeByName(*String::AsciiValue(name));
    494 
    495       DBG("Schema::GetType(name, args) X:");
    496       return descriptor ?
    497         schema->GetType(descriptor)->Constructor() :
    498         Handle<Function>();
    499     }
    500 
    501     static Handle<Value> NewSchema(const Arguments& args) {
    502       DBG("Schema::NewSchema E: args.Length()=%d", args.Length());
    503       if (!args.Length()) {
    504         return (new Schema(args.This(),
    505                            DescriptorPool::generated_pool()))->handle_;
    506       }
    507 
    508       Buffer* buf = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
    509 
    510       FileDescriptorSet descriptors;
    511       if (!descriptors.ParseFromArray(buf->data(), buf->length())) {
    512         DBG("Schema::NewSchema X: bad descriptor");
    513         return v8::ThrowException(String::New("Malformed descriptor"));
    514       }
    515 
    516       DescriptorPool* pool = new DescriptorPool;
    517       for (int i = 0; i < descriptors.file_size(); i++) {
    518         pool->BuildFile(descriptors.file(i));
    519       }
    520 
    521       DBG("Schema::NewSchema X");
    522       return (new Schema(args.This(), pool))->handle_;
    523     }
    524   };
    525 
    526   void Init() {
    527     DBG("Init E:");
    528     HandleScope handle_scope;
    529 
    530     TypeTemplate = Persistent<FunctionTemplate>::New(FunctionTemplate::New());
    531     TypeTemplate->SetClassName(String::New("Type"));
    532     // native self
    533     // owning schema (so GC can manage our lifecyle)
    534     // constructor
    535     // toArray
    536     TypeTemplate->InstanceTemplate()->SetInternalFieldCount(4);
    537 
    538     SchemaTemplate = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Schema::NewSchema));
    539     SchemaTemplate->SetClassName(String::New("Schema"));
    540     // native self
    541     // array of types (so GC can manage our lifecyle)
    542     SchemaTemplate->InstanceTemplate()->SetInternalFieldCount(2);
    543     SchemaTemplate->InstanceTemplate()->SetNamedPropertyHandler(Schema::GetType);
    544 
    545     ParseTemplate = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Schema::Type::Parse));
    546     SerializeTemplate = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Schema::Type::Serialize));
    547 
    548     DBG("Init X:");
    549   }
    550 
    551 }  // namespace protobuf_v8
    552 
    553 extern "C" void SchemaObjectTemplateInit(Handle<ObjectTemplate> target) {
    554   DBG("SchemaObjectTemplateInit(target) EX:");
    555   target->Set(String::New("Schema"), protobuf_v8::SchemaTemplate);
    556 }
    557