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
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>
UnwrapThis(const Arguments & args)85 static T* UnwrapThis(const Arguments& args) {
86 return ObjectWrap::Unwrap<T>(args.This());
87 }
88
89 template <typename T>
UnwrapThis(const AccessorInfo & args)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:
Schema(Handle<Object> self,const DescriptorPool * pool)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
~Schema()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
NewMessage() const122 Message* NewMessage() const {
123 DBG("Type::NewMessage() EX:");
124 return schema_->NewMessage(descriptor_);
125 }
126
Constructor() const127 Handle<Function> Constructor() const {
128 DBG("Type::Constrocutor() EX:");
129 return handle_->GetInternalField(2).As<Function>();
130 }
131
NewObject(Handle<Value> properties) const132 Local<Object> NewObject(Handle<Value> properties) const {
133 DBG("Type::NewObjext(properties) EX:");
134 return Constructor()->NewInstance(1, &properties);
135 }
136
Type(Schema * schema,const Descriptor * descriptor,Handle<Object> self)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
ToJs(const Message & instance,const Reflection * reflection,const FieldDescriptor * field,const Type * message_type,int index)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
ToJs(const Message & instance) const239 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
Parse(const Arguments & args)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
ToProto(Message * instance,const FieldDescriptor * field,Handle<Value> value,const Type * type,bool repeated)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
ToProto(Message * instance,Handle<Object> src) const400 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
Serialize(const Arguments & args)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
ToString(const Arguments & args)456 static Handle<Value> ToString(const Arguments& args) {
457 return String::New(UnwrapThis<Type>(args)->descriptor_->full_name().c_str());
458 }
459 };
460
NewMessage(const Descriptor * descriptor)461 Message* NewMessage(const Descriptor* descriptor) {
462 DBG("Schema::NewMessage(descriptor) EX:");
463 return factory_.GetPrototype(descriptor)->New();
464 }
465
GetType(const Descriptor * descriptor)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
GetType(const Local<String> name,const AccessorInfo & args)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
NewSchema(const Arguments & args)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
Init()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
SchemaObjectTemplateInit(Handle<ObjectTemplate> target)550 extern "C" void SchemaObjectTemplateInit(Handle<ObjectTemplate> target) {
551 DBG("SchemaObjectTemplateInit(target) EX:");
552 target->Set(String::New("Schema"), protobuf_v8::SchemaTemplate);
553 }
554