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>
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<Function>::Cast(handle_->GetInternalField(2));
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 = 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
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 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
Parse(const Arguments & args)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
ToProto(Message * instance,const FieldDescriptor * field,Handle<Value> value,const Type * type,bool repeated)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
ToProto(Message * instance,Handle<Object> src) const402 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
Serialize(const Arguments & args)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
ToString(const Arguments & args)458 static Handle<Value> ToString(const Arguments& args) {
459 return String::New(UnwrapThis<Type>(args)->descriptor_->full_name().c_str());
460 }
461 };
462
NewMessage(const Descriptor * descriptor)463 Message* NewMessage(const Descriptor* descriptor) {
464 DBG("Schema::NewMessage(descriptor) EX:");
465 return factory_.GetPrototype(descriptor)->New();
466 }
467
GetType(const Descriptor * descriptor)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
GetType(const Local<String> name,const AccessorInfo & args)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
NewSchema(const Arguments & args)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
Init()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
SchemaObjectTemplateInit(Handle<ObjectTemplate> target)553 extern "C" void SchemaObjectTemplateInit(Handle<ObjectTemplate> target) {
554 DBG("SchemaObjectTemplateInit(target) EX:");
555 target->Set(String::New("Schema"), protobuf_v8::SchemaTemplate);
556 }
557