protobuf_v8.cpp revision 6464068a31ff890d42d3da9cdf580d07c9c630d8
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(...) ALOGD(__VA_ARGS__) 38 39#else 40 41#define DBG(...) 42 43#endif 44 45using google::protobuf::Descriptor; 46using google::protobuf::DescriptorPool; 47using google::protobuf::DynamicMessageFactory; 48using google::protobuf::FieldDescriptor; 49using google::protobuf::FileDescriptorSet; 50using google::protobuf::Message; 51using google::protobuf::Reflection; 52 53//using ObjectWrap; 54//using Buffer; 55 56using std::map; 57using std::string; 58 59using v8::Array; 60using v8::AccessorInfo; 61using v8::Arguments; 62using v8::Boolean; 63using v8::Context; 64using v8::External; 65using v8::Function; 66using v8::FunctionTemplate; 67using v8::Integer; 68using v8::Handle; 69using v8::HandleScope; 70using v8::InvocationCallback; 71using v8::Local; 72using v8::NamedPropertyGetter; 73using v8::Number; 74using v8::Object; 75using v8::ObjectTemplate; 76using v8::Persistent; 77using v8::Script; 78using v8::String; 79using v8::Value; 80using v8::V8; 81 82namespace 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_->GetInternalField(2).As<Function>(); 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 = 168 Script::Compile(String::New(from.str().c_str()))->Run().As<Function>(); 169 constructor->SetHiddenValue(String::New("type"), self); 170 171 Handle<Function> bind = 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().As<Function>(); 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 array->Set(j, ToJs(instance, reflection, field, child_type, j)); 269 } 270 value = array; 271 } else { 272 value = ToJs(instance, reflection, field, child_type, -1); 273 } 274 275 DBG("Type::ToJs: set property[%d]=%s", i, ToCString(value)); 276 properties->Set(i, value); 277 } 278 279 DBG("Type::ToJs(Message) X:"); 280 return NewObject(properties); 281 } 282 283 static Handle<Value> Parse(const Arguments& args) { 284 DBG("Type::Parse(args) E:"); 285 Type* type = UnwrapThis<Type>(args); 286 Buffer* buf = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); 287 288 Message* message = type->NewMessage(); 289 message->ParseFromArray(buf->data(), buf->length()); 290 Handle<Object> result = type->ToJs(*message); 291 delete message; 292 293 DBG("Type::Parse(args) X:"); 294 return result; 295 } 296 297#define SET(TYPE, EXPR) \ 298 if (repeated) reflection->Add##TYPE(instance, field, EXPR); \ 299 else reflection->Set##TYPE(instance, field, EXPR) 300 301 static bool ToProto(Message* instance, 302 const FieldDescriptor* field, 303 Handle<Value> value, 304 const Type* type, 305 bool repeated) { 306 DBG("Type::ToProto(instance, field, value, type, repeated) E:"); 307 bool ok = true; 308 HandleScope scope; 309 310 DBG("Type::ToProto field->name()=%s", field->name().c_str()); 311 const Reflection* reflection = instance->GetReflection(); 312 switch (field->cpp_type()) { 313 case FieldDescriptor::CPPTYPE_MESSAGE: 314 DBG("Type::ToProto CPPTYPE_MESSAGE"); 315 ok = type->ToProto(repeated ? 316 reflection->AddMessage(instance, field) : 317 reflection->MutableMessage(instance, field), 318 value.As<Object>()); 319 break; 320 case FieldDescriptor::CPPTYPE_STRING: { 321 DBG("Type::ToProto CPPTYPE_STRING"); 322 String::AsciiValue ascii(value); 323 SET(String, string(*ascii, ascii.length())); 324 break; 325 } 326 case FieldDescriptor::CPPTYPE_INT32: 327 DBG("Type::ToProto CPPTYPE_INT32"); 328 SET(Int32, value->NumberValue()); 329 break; 330 case FieldDescriptor::CPPTYPE_UINT32: 331 DBG("Type::ToProto CPPTYPE_UINT32"); 332 SET(UInt32, value->NumberValue()); 333 break; 334 case FieldDescriptor::CPPTYPE_INT64: 335 DBG("Type::ToProto CPPTYPE_INT64"); 336 SET(Int64, value->NumberValue()); 337 break; 338 case FieldDescriptor::CPPTYPE_UINT64: 339 DBG("Type::ToProto CPPTYPE_UINT64"); 340 SET(UInt64, value->NumberValue()); 341 break; 342 case FieldDescriptor::CPPTYPE_FLOAT: 343 DBG("Type::ToProto CPPTYPE_FLOAT"); 344 SET(Float, value->NumberValue()); 345 break; 346 case FieldDescriptor::CPPTYPE_DOUBLE: 347 DBG("Type::ToProto CPPTYPE_DOUBLE"); 348 SET(Double, value->NumberValue()); 349 break; 350 case FieldDescriptor::CPPTYPE_BOOL: 351 DBG("Type::ToProto CPPTYPE_BOOL"); 352 SET(Bool, value->BooleanValue()); 353 break; 354 case FieldDescriptor::CPPTYPE_ENUM: 355 DBG("Type::ToProto CPPTYPE_ENUM"); 356 357 // Don't use SET as vd can be NULL 358 char error_buff[256]; 359 const google::protobuf::EnumValueDescriptor* vd; 360 int i32_value = 0; 361 const char *str_value = NULL; 362 const google::protobuf::EnumDescriptor* ed = field->enum_type(); 363 364 if (value->IsNumber()) { 365 i32_value = value->Int32Value(); 366 vd = ed->FindValueByNumber(i32_value); 367 if (vd == NULL) { 368 snprintf(error_buff, sizeof(error_buff), 369 "Type::ToProto Bad enum value, %d is not a member of enum %s", 370 i32_value, ed->full_name().c_str()); 371 } 372 } else { 373 str_value = ToCString(value); 374 // TODO: Why can str_value be corrupted sometimes? 375 ALOGD("str_value=%s", str_value); 376 vd = ed->FindValueByName(str_value); 377 if (vd == NULL) { 378 snprintf(error_buff, sizeof(error_buff), 379 "Type::ToProto Bad enum value, %s is not a member of enum %s", 380 str_value, ed->full_name().c_str()); 381 } 382 } 383 if (vd != NULL) { 384 if (repeated) { 385 reflection->AddEnum(instance, field, vd); 386 } else { 387 reflection->SetEnum(instance, field, vd); 388 } 389 } else { 390 v8::ThrowException(String::New(error_buff)); 391 ok = false; 392 } 393 break; 394 } 395 DBG("Type::ToProto(instance, field, value, type, repeated) X: ok=%d", ok); 396 return ok; 397 } 398#undef SET 399 400 bool ToProto(Message* instance, Handle<Object> src) const { 401 DBG("ToProto(Message *, Handle<Object>) E:"); 402 403 Handle<Function> to_array = handle_->GetInternalField(3).As<Function>(); 404 Handle<Array> properties = to_array->Call(src, 0, NULL).As<Array>(); 405 bool ok = true; 406 for (int i = 0; ok && (i < descriptor_->field_count()); i++) { 407 Handle<Value> value = properties->Get(i); 408 if (value->IsUndefined()) continue; 409 410 const FieldDescriptor* field = descriptor_->field(i); 411 const Type* child_type = 412 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ? 413 schema_->GetType(field->message_type()) : NULL; 414 if (field->is_repeated()) { 415 if(!value->IsArray()) { 416 ok = ToProto(instance, field, value, child_type, true); 417 } else { 418 Handle<Array> array = value.As<Array>(); 419 int length = array->Length(); 420 for (int j = 0; ok && (j < length); j++) { 421 ok = ToProto(instance, field, array->Get(j), child_type, true); 422 } 423 } 424 } else { 425 ok = ToProto(instance, field, value, child_type, false); 426 } 427 } 428 DBG("ToProto(Message *, Handle<Object>) X: ok=%d", ok); 429 return ok; 430 } 431 432 static Handle<Value> Serialize(const Arguments& args) { 433 Handle<Value> result; 434 DBG("Serialize(Arguments&) E:"); 435 if (!args[0]->IsObject()) { 436 DBG("Serialize(Arguments&) X: not an object"); 437 return v8::ThrowException(args[0]); 438 } 439 440 Type* type = UnwrapThis<Type>(args); 441 Message* message = type->NewMessage(); 442 if (type->ToProto(message, args[0].As<Object>())) { 443 int length = message->ByteSize(); 444 Buffer* buffer = Buffer::New(length); 445 message->SerializeWithCachedSizesToArray((google::protobuf::uint8*)buffer->data()); 446 delete message; 447 448 result = buffer->handle_; 449 } else { 450 result = v8::Undefined(); 451 } 452 DBG("Serialize(Arguments&) X"); 453 return result; 454 } 455 456 static Handle<Value> ToString(const Arguments& args) { 457 return String::New(UnwrapThis<Type>(args)->descriptor_->full_name().c_str()); 458 } 459 }; 460 461 Message* NewMessage(const Descriptor* descriptor) { 462 DBG("Schema::NewMessage(descriptor) EX:"); 463 return factory_.GetPrototype(descriptor)->New(); 464 } 465 466 Type* GetType(const Descriptor* descriptor) { 467 DBG("Schema::GetType(descriptor) E:"); 468 Type* result = types_[descriptor]; 469 if (result) return result; 470 471 result = types_[descriptor] = 472 new Type(this, descriptor, TypeTemplate->GetFunction()->NewInstance()); 473 474 // managed schema->[type] link 475 Handle<Array> types = handle_->GetInternalField(1).As<Array>(); 476 types->Set(types->Length(), result->handle_); 477 DBG("Schema::GetType(descriptor) X:"); 478 return result; 479 } 480 481 const DescriptorPool* pool_; 482 map<const Descriptor*, Type*> types_; 483 DynamicMessageFactory factory_; 484 485 static Handle<Value> GetType(const Local<String> name, 486 const AccessorInfo& args) { 487 DBG("Schema::GetType(name, args) E:"); 488 Schema* schema = UnwrapThis<Schema>(args); 489 const Descriptor* descriptor = 490 schema->pool_->FindMessageTypeByName(*String::AsciiValue(name)); 491 492 DBG("Schema::GetType(name, args) X:"); 493 return descriptor ? 494 schema->GetType(descriptor)->Constructor() : 495 Handle<Function>(); 496 } 497 498 static Handle<Value> NewSchema(const Arguments& args) { 499 DBG("Schema::NewSchema E: args.Length()=%d", args.Length()); 500 if (!args.Length()) { 501 return (new Schema(args.This(), 502 DescriptorPool::generated_pool()))->handle_; 503 } 504 505 Buffer* buf = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); 506 507 FileDescriptorSet descriptors; 508 if (!descriptors.ParseFromArray(buf->data(), buf->length())) { 509 DBG("Schema::NewSchema X: bad descriptor"); 510 return v8::ThrowException(String::New("Malformed descriptor")); 511 } 512 513 DescriptorPool* pool = new DescriptorPool; 514 for (int i = 0; i < descriptors.file_size(); i++) { 515 pool->BuildFile(descriptors.file(i)); 516 } 517 518 DBG("Schema::NewSchema X"); 519 return (new Schema(args.This(), pool))->handle_; 520 } 521 }; 522 523 void Init() { 524 DBG("Init E:"); 525 HandleScope handle_scope; 526 527 TypeTemplate = Persistent<FunctionTemplate>::New(FunctionTemplate::New()); 528 TypeTemplate->SetClassName(String::New("Type")); 529 // native self 530 // owning schema (so GC can manage our lifecyle) 531 // constructor 532 // toArray 533 TypeTemplate->InstanceTemplate()->SetInternalFieldCount(4); 534 535 SchemaTemplate = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Schema::NewSchema)); 536 SchemaTemplate->SetClassName(String::New("Schema")); 537 // native self 538 // array of types (so GC can manage our lifecyle) 539 SchemaTemplate->InstanceTemplate()->SetInternalFieldCount(2); 540 SchemaTemplate->InstanceTemplate()->SetNamedPropertyHandler(Schema::GetType); 541 542 ParseTemplate = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Schema::Type::Parse)); 543 SerializeTemplate = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Schema::Type::Serialize)); 544 545 DBG("Init X:"); 546 } 547 548} // namespace protobuf_v8 549 550extern "C" void SchemaObjectTemplateInit(Handle<ObjectTemplate> target) { 551 DBG("SchemaObjectTemplateInit(target) EX:"); 552 target->Set(String::New("Schema"), protobuf_v8::SchemaTemplate); 553} 554