RTBKit  0.9
Open-source framework to create real-time ad bidding systems.
js/bid_request_js.cc
00001 /* bid_request_js.cc
00002    Jeremy Barnes, 6 April 2011
00003    Copyright (c) 2011 Datacratic.  All rights reserved.
00004 
00005    Bid Request.
00006 */
00007 
00008 
00009 #include "bid_request_js.h"
00010 #include "soa/js/js_wrapped.h"
00011 #include "jml/utils/smart_ptr_utils.h"
00012 #include "soa/types/js/id_js.h"
00013 #include "soa/types/js/url_js.h"
00014 #include "currency_js.h"
00015 #include <boost/make_shared.hpp>
00016 #include <boost/algorithm/string/trim.hpp>
00017 #include "rtbkit/openrtb/openrtb_parsing.h"
00018 
00019 
00020 using namespace std;
00021 using namespace v8;
00022 using namespace node;
00023 
00024 namespace Datacratic {
00025 
00026 struct JSConverters {
00027     std::function<void (void *, const JS::JSValue &)> fromJs;
00028     std::function<v8::Handle<v8::Value> (const void *, std::shared_ptr<void>)> toJs;
00029 };
00030 
00031 namespace JS {
00032 
00033 
00034 const char * const bidRequestModule = "bid_request";
00035 //so we can do require("standalone_demo")
00036 
00037 void to_js(JS::JSValue & value, const Format & f)
00038 {
00039     value = JS::toJS(f.print());
00040 }
00041 
00042 
00043 /*****************************************************************************/
00044 /* SEGMENT LIST JS                                                           */
00045 /*****************************************************************************/
00046 
00047 const char * SegmentListName = "SegmentList";
00048 
00049 struct SegmentListJS
00050     : public JSWrapped2<SegmentList, SegmentListJS, SegmentListName,
00051                         bidRequestModule> {
00052 
00053     SegmentListJS(v8::Handle<v8::Object> This,
00054               const std::shared_ptr<SegmentList> & list
00055                   = std::shared_ptr<SegmentList>())
00056     {
00057         HandleScope scope;
00058         wrap(This, list);
00059     }
00060 
00061     static Handle<v8::Value>
00062     New(const Arguments & args)
00063     {
00064         try {
00065             if (args.Length() == 0) {
00066                 new SegmentListJS(args.This(),
00067                                   std::make_shared<SegmentList>());
00068                 return args.This();
00069             }
00070             else {
00071                 if (SegmentListJS::tmpl->HasInstance(args[0])) {
00072                     throw ML::Exception("segment list from segment list");
00073                     //new SegmentListJS(args.This(),
00074                     //                  std::make_shared<SegmentList>
00075                     //                  (*SegmentListJS::fromJS(args[0])));
00076                     return args.This();
00077                 }
00078                 
00079                 Json::Value valInJson = JS::fromJS(args[0]);
00080                 new SegmentListJS(args.This(),
00081                                   std::make_shared<SegmentList>
00082                                   (SegmentList::createFromJson(valInJson)));
00083                 return args.This();
00084             }
00085         } HANDLE_JS_EXCEPTIONS;
00086     }
00087 
00088     static void
00089     Initialize()
00090     {
00091         Persistent<FunctionTemplate> t = Register(New);
00092         NODE_SET_PROTOTYPE_METHOD(t, "forEach", forEach);
00093         NODE_SET_PROTOTYPE_METHOD(t, "add", add);
00094         NODE_SET_PROTOTYPE_METHOD(t, "toArray", toArray);
00095         NODE_SET_PROTOTYPE_METHOD(t, "toString", toString);
00096         NODE_SET_PROTOTYPE_METHOD(t, "inspect", toString);
00097 
00098         registerMemberFn(&SegmentList::toJson, "toJSON");
00099 
00100         t->InstanceTemplate()
00101             ->SetAccessor(String::NewSymbol("length"), lengthGetter,
00102                           0, v8::Handle<v8::Value>(), DEFAULT,
00103                           PropertyAttribute(ReadOnly | DontEnum | DontDelete));
00104                           
00105         t->InstanceTemplate()
00106             ->SetIndexedPropertyHandler(getIndexed, setIndexed, queryIndexed,
00107                                         deleteIndexed, listIndexed);
00108     }
00109 
00110     static v8::Handle<v8::Value>
00111     add(const Arguments & args)
00112     {
00113         try {
00114             auto segs = getShared(args.This());
00115             segs->add(getArg<string>(args, 0, "segment"));
00116             segs->sort();
00117             return args.This();
00118         } HANDLE_JS_EXCEPTIONS;
00119     }
00120 
00121     static v8::Handle<v8::Value>
00122     forEach(const Arguments & args)
00123     {
00124         HandleScope scope;
00125         try {
00126             auto segs = getShared(args.This());
00127 
00128             v8::Local<v8::Function> fn = getArg(args, 0, "iterFunction");
00129 
00130             for (unsigned index = 0;  index < segs->size();  ++index) {
00131                 HandleScope scope;
00132                 JSValue value;
00133                 
00134                 if (index < segs->ints.size())
00135                     value = JS::toJS(segs->ints[index]);
00136                 else if (index - segs->ints.size() < segs->strings.size())
00137                     value = JS::toJS(segs->strings[index - segs->ints.size()]);
00138                 else throw ML::Exception("logic error in forEach");
00139 
00140                 // Argument 1: value
00141                 // Argument 2: index number
00142                 int argc = 2;
00143                 v8::Handle<v8::Value> argv[argc];
00144             
00145                 argv[0] = value;
00146                 argv[1] = v8::Uint32::New(index);
00147                 
00148                 v8::Handle<v8::Value> result
00149                     = fn->Call(args.This(), argc, argv);
00150                 
00151                 // Exception?
00152                 if (result.IsEmpty())
00153                     return scope.Close(result);
00154 
00155                 if (index == segs->size() - 1 && !result->IsUndefined())
00156                     return scope.Close(result);
00157             }
00158 
00159             return v8::Undefined();
00160         } HANDLE_JS_EXCEPTIONS;
00161     }
00162 
00163     static v8::Handle<v8::Value>
00164     getIndexed(uint32_t index, const v8::AccessorInfo & info)
00165     {
00166         try {
00167             auto segs = getShared(info.This());
00168             
00169             if (index < segs->ints.size())
00170                 return JS::toJS(segs->ints[index]);
00171             else if (index - segs->ints.size() < segs->strings.size())
00172                 return JS::toJS(segs->strings[index - segs->ints.size()]);
00173             else return v8::Undefined();
00174         } HANDLE_JS_EXCEPTIONS;
00175     }
00176 
00177     static v8::Handle<v8::Value>
00178     setIndexed(uint32_t index,
00179                v8::Local<v8::Value> value,
00180                const v8::AccessorInfo & info)
00181     {
00182         try {
00183             throw ML::Exception("can't modify segments argument");
00184         } HANDLE_JS_EXCEPTIONS;
00185     }
00186 
00187     static v8::Handle<v8::Integer>
00188     queryIndexed(uint32_t index,
00189                  const v8::AccessorInfo & info)
00190     {
00191         auto segs = getShared(info.This());
00192         int sz = segs->size();
00193 
00194         if (index <= sz)
00195             return v8::Integer::New(ReadOnly | DontDelete);
00196         
00197         return NULL_HANDLE;
00198     }
00199 
00200     static v8::Handle<v8::Array>
00201     listIndexed(const v8::AccessorInfo & info)
00202     {
00203         v8::HandleScope scope;
00204         auto segs = getShared(info.This());
00205         int sz = segs->size();
00206 
00207         v8::Handle<v8::Array> result(v8::Array::New(sz));
00208 
00209         for (unsigned i = 0;  i < sz;  ++i) {
00210             result->Set(v8::Uint32::New(i),
00211                         v8::Uint32::New(i));
00212         }
00213         
00214         return scope.Close(result);
00215     }
00216 
00217     static v8::Handle<v8::Boolean>
00218     deleteIndexed(uint32_t index,
00219                   const v8::AccessorInfo & info)
00220     {
00221         return NULL_HANDLE;
00222     }
00223 
00224     static v8::Handle<v8::Value>
00225     toArray(const v8::Arguments & args)
00226     {
00227         try {
00228             v8::HandleScope scope;
00229             auto segs = getShared(args.This());
00230             int sz = segs->size();
00231 
00232             v8::Handle<v8::Array> result(v8::Array::New(sz));
00233 
00234             for (unsigned i = 0;  i < segs->ints.size();  ++i) {
00235                 result->Set(v8::Uint32::New(i),
00236                             v8::Uint32::New(segs->ints[i]));
00237             }
00238             for (unsigned i = 0;  i < segs->strings.size();  ++i) {
00239                 result->Set(v8::Uint32::New(i + segs->ints.size()),
00240                             JS::toJS(segs->strings[i]));
00241             }
00242 
00243             return scope.Close(result);
00244         } HANDLE_JS_EXCEPTIONS;
00245     }
00246 
00247     static v8::Handle<v8::Value>
00248     toString(const v8::Arguments & args)
00249     {
00250         try {
00251             auto segs = getShared(args.This());
00252             return JS::toJS(boost::trim_copy(segs->toJson().toString()));
00253         } HANDLE_JS_EXCEPTIONS;
00254     }
00255 
00256     static v8::Handle<v8::Value>
00257     lengthGetter(v8::Local<v8::String> property,
00258                  const AccessorInfo & info)
00259     {
00260         try {
00261             return v8::Integer::New(getShared(info.This())->size());
00262         } HANDLE_JS_EXCEPTIONS;
00263     }
00264 };
00265 
00266 std::shared_ptr<SegmentList>
00267 from_js(const JSValue & value, std::shared_ptr<SegmentList> *)
00268 {
00269     if (SegmentListJS::tmpl->HasInstance(value))
00270         return SegmentListJS::fromJS(value);
00271 
00272     vector<string> values;
00273     JS::from_js(value, &values);
00274     return std::make_shared<SegmentList>(values);
00275 }
00276 
00277 SegmentList *
00278 from_js(const JSValue & value, SegmentList **)
00279 {
00280     return SegmentListJS::fromJS(value).get();
00281 }
00282 
00283 std::shared_ptr<SegmentList>
00284 from_js_ref(const JSValue & value, std::shared_ptr<SegmentList> *)
00285 {
00286     return SegmentListJS::fromJS(value);
00287 }
00288 
00289 void to_js(JS::JSValue & value, const std::shared_ptr<SegmentList> & br)
00290 {
00291     value = SegmentListJS::toJS(br);
00292 }
00293 
00294 SegmentList
00295 from_js(const JSValue & value, SegmentList *)
00296 {
00297     if (SegmentListJS::tmpl->HasInstance(value))
00298         return *SegmentListJS::fromJS(value);
00299     Json::Value valInJson = JS::fromJS(value);
00300     SegmentList result = SegmentList::createFromJson(valInJson);
00301     return result;
00302 }
00303 
00304 void to_js(JS::JSValue & value, const UserIds & uids)
00305 {
00306     to_js(value, static_cast<const std::map<std::string, Id> &>(uids));
00307 }
00308 
00309 UserIds 
00310 from_js(const JSValue & value, UserIds *)
00311 {
00312     UserIds result;
00313     static_cast<std::map<std::string, Id> &>(result)
00314         = from_js(value, (std::map<std::string, Id> *)0);
00315     return result;
00316 }
00317 
00318 
00319 /*****************************************************************************/
00320 /* SEGMENTS BY SOURCE JS                                                     */
00321 /*****************************************************************************/
00322 
00323 const char * SegmentsBySourceName = "SegmentsBySource";
00324 
00325 struct SegmentsBySourceJS
00326     : public JSWrapped2<SegmentsBySource, SegmentsBySourceJS,
00327                         SegmentsBySourceName,
00328                         bidRequestModule> {
00329 
00330     SegmentsBySourceJS(v8::Handle<v8::Object> This,
00331                        const std::shared_ptr<SegmentsBySource> & list
00332                            = std::shared_ptr<SegmentsBySource>())
00333     {
00334         HandleScope scope;
00335         wrap(This, list);
00336     }
00337 
00338     static Handle<v8::Value>
00339     New(const Arguments & args)
00340     {
00341         try {
00342             new SegmentsBySourceJS(args.This(),
00343                                    std::make_shared<SegmentsBySource>());
00344             return args.This();
00345         } HANDLE_JS_EXCEPTIONS;
00346     }
00347 
00348     static void
00349     Initialize()
00350     {
00351         Persistent<FunctionTemplate> t = Register(New);
00352 
00353         t->InstanceTemplate()
00354             ->SetNamedPropertyHandler(getNamed, setNamed, queryNamed,
00355                                       deleteNamed, listNamed);
00356 
00357     }
00358 
00359     static v8::Handle<v8::Value>
00360     getNamed(v8::Local<v8::String> property,
00361              const v8::AccessorInfo & info)
00362     {
00363         HandleScope scope;
00364         try {
00365             Local<v8::Value> object_prop
00366                 = info.This()->GetRealNamedProperty(property);
00367             if (!object_prop.IsEmpty())
00368                 return scope.Close(object_prop);
00369             
00370             // Is it a column name?
00371             string name = cstr(property);
00372             
00373             SegmentsBySource * segs = getShared(info.This());
00374             
00375             if (!segs->count(name))
00376                 return NULL_HANDLE;
00377 
00378             return scope.Close(JS::toJS(segs->find(name)->second));
00379         } HANDLE_JS_EXCEPTIONS;
00380     }
00381 
00382     static v8::Handle<v8::Value>
00383     setNamed(v8::Local<v8::String> property,
00384              v8::Local<v8::Value> value,
00385              const v8::AccessorInfo & info)
00386     {
00387         try {
00388             if (info.This()->HasRealNamedProperty(property)) {
00389                 if (info.This()->Set(property, value))
00390                     return value;
00391             }
00392             
00393             // Is it a column name?
00394             string name = cstr(property);
00395             SegmentsBySource * segs = getShared(info.This());
00396             
00397             // Is the value sensible?
00398             if (value->IsNull() || value->IsUndefined()) {
00399                 throw ML::Exception("can't set named to undefined");
00400             }
00401             
00402             std::shared_ptr<SegmentList> segs2
00403                 = from_js(value, &segs2);
00404 
00405             if (!segs2)
00406                 throw ML::Exception("can't set to null segments");
00407 
00408             (*segs)[name] = segs2;
00409             
00410             return v8::Undefined();
00411         } HANDLE_JS_EXCEPTIONS;
00412     }
00413 
00414     static v8::Handle<v8::Integer>
00415     queryNamed(v8::Local<v8::String> property,
00416                const v8::AccessorInfo & info)
00417     {
00418         if (property.IsEmpty() || property->IsNull()
00419             || property->IsUndefined())
00420             throw ML::Exception("queryNamed: invalid property");
00421 
00422         string name = cstr(property);
00423 
00424         SegmentsBySource * segs = getShared(info.This());
00425         
00426         if (segs->count(name))
00427             return v8::Integer::New(DontDelete);
00428         
00429         return NULL_HANDLE;
00430     }
00431 
00432     static v8::Handle<v8::Boolean>
00433     deleteNamed(v8::Local<v8::String> property,
00434                 const v8::AccessorInfo & info)
00435 
00436     {
00437         if (property.IsEmpty() || property->IsNull()
00438             || property->IsUndefined())
00439             throw ML::Exception("queryNamed: invalid property");
00440 
00441         string name = cstr(property);
00442 
00443         SegmentsBySource * segs = getShared(info.This());
00444 
00445         return v8::Boolean::New(segs->erase(name));
00446     }
00447 
00448     static v8::Handle<v8::Array>
00449     listNamed(const v8::AccessorInfo & info)
00450     {
00451         //cerr << "listNamed" << endl;
00452         HandleScope scope;
00453         try {
00454             SegmentsBySource * segs = getShared(info.This());
00455 
00456             int n = segs->size();
00457 
00458             v8::Handle<v8::Array> result = v8::Array::New(n);
00459 
00460             //cerr << "listNamed: " << ncol << " columns" << endl;
00461 
00462             unsigned i = 0;
00463             for (auto it = segs->begin(), end = segs->end();
00464                  it != end;  ++it,++i) {
00465                 v8::Local<Integer> key = v8::Integer::New(i);
00466                 v8::Handle<Value>  val = JS::toJS(it->first);
00467                 result->Set(key, val);
00468             }
00469             
00470             return scope.Close(result);
00471         } catch (...) {
00472             cerr << "got exception in listNamed" << endl;
00473             return NULL_HANDLE;
00474         }
00475     }
00476 };
00477 
00478 
00479 /*****************************************************************************/
00480 /* USER IDS JS                                                               */
00481 /*****************************************************************************/
00482 
00483 const char * UserIdsName = "UserIds";
00484 
00485 struct UserIdsJS
00486     : public JSWrapped2<UserIds, UserIdsJS,
00487                         UserIdsName,
00488                         bidRequestModule> {
00489 
00490     UserIdsJS(v8::Handle<v8::Object> This,
00491                        const std::shared_ptr<UserIds> & list
00492                            = std::shared_ptr<UserIds>())
00493     {
00494         HandleScope scope;
00495         wrap(This, list);
00496     }
00497 
00498     static Handle<v8::Value>
00499     New(const Arguments & args)
00500     {
00501         try {
00502             new UserIdsJS(args.This(),
00503                                    std::make_shared<UserIds>());
00504             return args.This();
00505         } HANDLE_JS_EXCEPTIONS;
00506     }
00507 
00508     static void
00509     Initialize()
00510     {
00511         Persistent<FunctionTemplate> t = Register(New);
00512 
00513         t->InstanceTemplate()
00514             ->SetNamedPropertyHandler(getNamed, setNamed, queryNamed,
00515                                       deleteNamed, listNamed);
00516     }
00517 
00518     static v8::Handle<v8::Value>
00519     getNamed(v8::Local<v8::String> property,
00520              const v8::AccessorInfo & info)
00521     {
00522         HandleScope scope;
00523         try {
00524             Local<v8::Value> object_prop
00525                 = info.This()->GetRealNamedProperty(property);
00526             if (!object_prop.IsEmpty())
00527                 return scope.Close(object_prop);
00528             
00529             // Is it a column name?
00530             string name = cstr(property);
00531             
00532             UserIds * ids = getShared(info.This());
00533             
00534             if (!ids->count(name))
00535                 return NULL_HANDLE;
00536 
00537             return scope.Close(JS::toJS(ids->find(name)->second));
00538         } HANDLE_JS_EXCEPTIONS;
00539     }
00540 
00541     static v8::Handle<v8::Value>
00542     setNamed(v8::Local<v8::String> property,
00543              v8::Local<v8::Value> value,
00544              const v8::AccessorInfo & info)
00545     {
00546         try {
00547             if (info.This()->HasRealNamedProperty(property)) {
00548                 if (info.This()->Set(property, value))
00549                     return value;
00550             }
00551             
00552             // Is it a column name?
00553             string name = cstr(property);
00554             UserIds * ids = getShared(info.This());
00555             
00556             // Is the value sensible?
00557             if (value->IsNull() || value->IsUndefined()) {
00558                 throw ML::Exception("can't set named to undefined");
00559             }
00560             
00561             Id id = from_js(value, &id);
00562 
00563             if (!id)
00564                 throw ML::Exception("can't set to null ID");
00565             
00566             ids->set(id, name);
00567             
00568             return v8::Undefined();
00569         } HANDLE_JS_EXCEPTIONS;
00570     }
00571 
00572     static v8::Handle<v8::Integer>
00573     queryNamed(v8::Local<v8::String> property,
00574                const v8::AccessorInfo & info)
00575     {
00576         if (property.IsEmpty() || property->IsNull()
00577             || property->IsUndefined())
00578             throw ML::Exception("queryNamed: invalid property");
00579 
00580         string name = cstr(property);
00581 
00582         UserIds * ids = getShared(info.This());
00583         
00584         if (ids->count(name))
00585             return v8::Integer::New(DontDelete);
00586         
00587         return NULL_HANDLE;
00588     }
00589 
00590     static v8::Handle<v8::Boolean>
00591     deleteNamed(v8::Local<v8::String> property,
00592                 const v8::AccessorInfo & info)
00593 
00594     {
00595         if (property.IsEmpty() || property->IsNull()
00596             || property->IsUndefined())
00597             throw ML::Exception("queryNamed: invalid property");
00598 
00599         string name = cstr(property);
00600 
00601         UserIds * ids = getShared(info.This());
00602 
00603         return v8::Boolean::New(ids->erase(name));
00604     }
00605 
00606     static v8::Handle<v8::Array>
00607     listNamed(const v8::AccessorInfo & info)
00608     {
00609         //cerr << "listNamed" << endl;
00610         HandleScope scope;
00611         try {
00612             UserIds * ids = getShared(info.This());
00613 
00614             int n = ids->size();
00615 
00616             v8::Handle<v8::Array> result = v8::Array::New(n);
00617 
00618             //cerr << "listNamed: " << ncol << " columns" << endl;
00619 
00620             unsigned i = 0;
00621             for (auto it = ids->begin(), end = ids->end();
00622                  it != end;  ++it,++i) {
00623                 v8::Local<Integer> key = v8::Integer::New(i);
00624                 v8::Handle<Value>  val = JS::toJS(it->first);
00625                 result->Set(key, val);
00626             }
00627             
00628             return scope.Close(result);
00629         } catch (...) {
00630             cerr << "got exception in listNamed" << endl;
00631             return NULL_HANDLE;
00632         }
00633     }
00634 };
00635 
00636 
00637 /*****************************************************************************/
00638 /* LOCATION JS                                                               */
00639 /*****************************************************************************/
00640 
00641 const char * LocationName = "Location";
00642 
00643 struct LocationJS
00644     : public JSWrapped2<Location, LocationJS,
00645                         LocationName,
00646                         bidRequestModule> {
00647 
00648     LocationJS(v8::Handle<v8::Object> This,
00649                        const std::shared_ptr<Location> & list
00650                            = std::shared_ptr<Location>())
00651     {
00652         HandleScope scope;
00653         wrap(This, list);
00654     }
00655 
00656     static Handle<v8::Value>
00657     New(const Arguments & args)
00658     {
00659         try {
00660             new LocationJS(args.This(),
00661                            std::make_shared<Location>());
00662             return args.This();
00663         } HANDLE_JS_EXCEPTIONS;
00664     }
00665 
00666     static void
00667     Initialize()
00668     {
00669         Persistent<FunctionTemplate> t = Register(New);
00670         registerRWProperty(&Location::countryCode, "countryCode");
00671         registerRWProperty(&Location::regionCode, "regionCode");
00672         registerRWProperty(&Location::cityName, "cityName");
00673         registerRWProperty(&Location::dma, "dma");
00674         registerRWProperty(&Location::timezoneOffsetMinutes,
00675                            "timezoneOffsetMinutes");
00676         registerMemberFn(&Location::toJson, "toJSON");
00677         registerMemberFn(&Location::fullLocationString,
00678                          "fullLocationString");
00679     }
00680 };
00681 
00682 // To/from JS goes via JSON for the moment...
00683 template<typename Obj, typename Base>
00684 struct PropertyAccessViaJson {
00685     static v8::Handle<v8::Value>
00686     getter(v8::Local<v8::String> property,
00687            const v8::AccessorInfo & info)
00688     {
00689         try {
00690             const ValueDescription * vd
00691                 = reinterpret_cast<const ValueDescription *>
00692                 (v8::External::Unwrap(info.Data()));
00693             Obj * o = Base::getShared(info.This());
00694             const StructureDescriptionBase::FieldDescription & fd
00695                 = vd->getField(cstr(property));
00696             StructuredJsonPrintingContext context;
00697             fd.description->printJson(addOffset(o, fd.offset), context);
00698             return JS::toJS(context.output);
00699         } HANDLE_JS_EXCEPTIONS;
00700     }
00701 
00702     static void
00703     setter(v8::Local<v8::String> property,
00704            v8::Local<v8::Value> value,
00705            const v8::AccessorInfo & info)
00706     {
00707         try {
00708             const ValueDescription * vd
00709                 = reinterpret_cast<const ValueDescription *>
00710                 (v8::External::Unwrap(info.Data()));
00711             Obj * o = Base::getShared(info.This());
00712             const StructureDescriptionBase::FieldDescription & fd
00713                 = vd->getField(cstr(property));
00714             Json::Value val = JS::fromJS(JSValue(value));
00715             StructuredJsonParsingContext context(val);
00716             fd.description->parseJson(addOffset(o, fd.offset), context);
00717         } HANDLE_JS_EXCEPTIONS_SETTER;
00718     }
00719 };
00720 
00721 #if 0
00722 struct WrappedStructureJS: public JS::JSWrapped {
00723     const ValueDescription * desc;
00724 };
00725 #endif
00726 
00727 void
00728 setFromJs(void * field,
00729           const JSValue & value,
00730           const ValueDescription & desc);
00731 
00732 v8::Handle<v8::Value>
00733 getFromJs(const void * field,
00734           const ValueDescription & desc,
00735           const std::shared_ptr<void> & owner);
00736 
00737 struct WrappedArrayJS: public JSWrappedBase {
00738     const ValueDescription * desc;
00739     void * value;  // value being read
00740 
00741     static v8::Persistent<v8::FunctionTemplate> tmpl;
00742     
00743     WrappedArrayJS(v8::Handle<v8::Object> This,
00744                    void * value = 0,
00745                    std::shared_ptr<void> owner = nullptr)
00746     {
00747         wrap(This, 64 /* bytes */, typeid(*this));
00748         this->value = value;
00749         this->owner_ = owner;
00750     }
00751 
00752     static Handle<v8::Value>
00753     New(const Arguments & args)
00754     {
00755         try {
00756             new WrappedArrayJS(args.This(), nullptr);
00757             return args.This();
00758         } HANDLE_JS_EXCEPTIONS;
00759     }
00760 
00761     static void Initialize()
00762     {
00763         tmpl = RegisterBase("WrappedArrayJS", "bonus", New);
00764         
00765         tmpl->InstanceTemplate()
00766             ->SetIndexedPropertyHandler(getIndexed, setIndexed, queryIndexed,
00767                                         deleteIndexed, listIndexed);
00768         
00769         tmpl->InstanceTemplate()
00770             ->SetAccessor(String::NewSymbol("length"), lengthGetter,
00771                           0, v8::Handle<v8::Value>(), DEFAULT,
00772                           PropertyAttribute(ReadOnly | DontEnum | DontDelete));
00773     }
00774 
00775     static WrappedArrayJS * getWrapper(const v8::Handle<v8::Object> & object)
00776     {
00777         return unwrap<WrappedArrayJS>(object);
00778     }
00779 
00780     static v8::Handle<v8::Value>
00781     lengthGetter(v8::Local<v8::String> property,
00782                  const AccessorInfo & info)
00783     {
00784         try {
00785             WrappedArrayJS * wrapper = getWrapper(info.This());
00786             size_t size = wrapper->desc->getArrayLength(wrapper->value);
00787             return JS::toJS(size);
00788         } HANDLE_JS_EXCEPTIONS;
00789     }
00790 
00791     static v8::Handle<v8::Value>
00792     getIndexed(uint32_t index, const v8::AccessorInfo & info)
00793     {
00794         v8::HandleScope scope;
00795         try {
00796             WrappedArrayJS * wrapper = getWrapper(info.This());
00797             size_t size = wrapper->desc->getArrayLength(wrapper->value);
00798             
00799             if (index >= size)
00800                 return v8::Undefined();
00801 
00802             void * element = wrapper->desc->getArrayElement(wrapper->value, index);
00803             
00804             return scope.Close(getFromJs(element, wrapper->desc->contained(),
00805                                          wrapper->owner_));
00806         } HANDLE_JS_EXCEPTIONS;
00807     }
00808 
00809     static v8::Handle<v8::Value>
00810     setIndexed(uint32_t index,
00811                v8::Local<v8::Value> value,
00812                const v8::AccessorInfo & info)
00813     {
00814         try {
00815             throw ML::Exception("setIndexed not done yet");
00816         } HANDLE_JS_EXCEPTIONS;
00817     }
00818 
00819     static v8::Handle<v8::Integer>
00820     queryIndexed(uint32_t index,
00821                  const v8::AccessorInfo & info)
00822     {
00823         WrappedArrayJS * wrapper = getWrapper(info.This());
00824         size_t size = wrapper->desc->getArrayLength(wrapper->value);
00825 
00826         if (index < size)
00827             return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
00828         
00829         return NULL_HANDLE;
00830     }
00831     
00832     static v8::Handle<v8::Boolean>
00833     deleteIndexed(uint32_t index,
00834                   const v8::AccessorInfo & info)
00835     {
00836         return NULL_HANDLE;
00837     }
00838 
00839     static v8::Handle<v8::Array>
00840     listIndexed(const v8::AccessorInfo & info)
00841     {
00842         v8::HandleScope scope;
00843 
00844         WrappedArrayJS * wrapper = getWrapper(info.This());
00845         size_t sz = wrapper->desc->getArrayLength(wrapper->value);
00846 
00847         v8::Handle<v8::Array> result(v8::Array::New(sz));
00848 
00849         for (unsigned i = 0;  i < sz;  ++i) {
00850             result->Set(v8::Uint32::New(i),
00851                         v8::Uint32::New(i));
00852         }
00853         
00854         return scope.Close(result);
00855     }
00856 };
00857 
00858 v8::Persistent<v8::FunctionTemplate>
00859 WrappedArrayJS::
00860 tmpl;
00861 
00862 // Wrap an array of values where elements are got or set in their entirity
00863 struct WrappedValueArrayJS: public JSWrappedBase {
00864     const ValueDescription * desc;
00865 };
00866 
00867 // Wrap an array of fundamental types that can be directly mapped by the
00868 // Javascript runtime
00869 struct WrappedFundamentalValueArrayJS: public JSWrappedBase {
00870     const ValueDescription * desc;
00871 };
00872 
00873 struct WrappedStructureJS: public JSWrappedBase {
00874     const ValueDescription * desc;
00875     void * value;  // value being read
00876 
00877     static v8::Persistent<v8::FunctionTemplate> tmpl;
00878     
00879     WrappedStructureJS(v8::Handle<v8::Object> This,
00880                    void * value = 0,
00881                    std::shared_ptr<void> owner = nullptr)
00882     {
00883         wrap(This, 64 /* bytes */, typeid(*this));
00884         this->value = value;
00885         this->owner_ = owner;
00886     }
00887 
00888     static Handle<v8::Value>
00889     New(const Arguments & args)
00890     {
00891         try {
00892             new WrappedStructureJS(args.This(), nullptr);
00893             return args.This();
00894         } HANDLE_JS_EXCEPTIONS;
00895     }
00896 
00897     static void Initialize()
00898     {
00899         tmpl = RegisterBase("WrappedStructureJS", "bonus", New);
00900         
00901         tmpl->InstanceTemplate()
00902             ->SetNamedPropertyHandler(getNamed, setNamed, queryNamed,
00903                                       deleteNamed, listNamed);
00904     }
00905 
00906     static WrappedStructureJS * getWrapper(const v8::Handle<v8::Object> & object)
00907     {
00908         return unwrap<WrappedStructureJS>(object);
00909     }
00910 
00911     static v8::Handle<v8::Value>
00912     getNamed(v8::Local<v8::String> property,
00913              const v8::AccessorInfo & info)
00914     {
00915         try {
00916             string name = cstr(property);
00917 
00918             WrappedStructureJS * wrapper = getWrapper(info.This());
00919             const ValueDescription::FieldDescription * fd
00920                 = wrapper->desc->hasField(wrapper->value, name);
00921             
00922             if (!fd)
00923                 return NULL_HANDLE;
00924 
00925             return getFromJs(addOffset(wrapper->value, fd->offset),
00926                              *fd->description,
00927                              wrapper->owner_);
00928 
00929         } HANDLE_JS_EXCEPTIONS;
00930     }
00931 
00932     static v8::Handle<v8::Value>
00933     setNamed(v8::Local<v8::String> property,
00934              v8::Local<v8::Value> value,
00935              const v8::AccessorInfo & info)
00936     {
00937         try {
00938             string name = cstr(property);
00939 
00940             WrappedStructureJS * wrapper = getWrapper(info.This());
00941             const ValueDescription::FieldDescription & fd
00942                 = wrapper->desc->getField(name);
00943             
00944             setFromJs(addOffset(wrapper->value, fd.offset), value,
00945                       *fd.description);
00946 
00947             return v8::Undefined();
00948         } HANDLE_JS_EXCEPTIONS;
00949     }
00950 
00951     static v8::Handle<v8::Integer>
00952     queryNamed(v8::Local<v8::String> property,
00953                const v8::AccessorInfo & info)
00954     {
00955         string name = cstr(property);
00956         WrappedStructureJS * wrapper = getWrapper(info.This());
00957         if (!wrapper->desc->hasField(wrapper->value, name))
00958             return NULL_HANDLE;
00959 
00960         return v8::Integer::New(DontDelete);
00961     }
00962 
00963     static v8::Handle<v8::Boolean>
00964     deleteNamed(v8::Local<v8::String> property,
00965                 const v8::AccessorInfo & info)
00966 
00967     {
00968         return v8::False();
00969     }
00970 
00971     static v8::Handle<v8::Array>
00972     listNamed(const v8::AccessorInfo & info)
00973     {
00974         try {
00975             HandleScope scope;
00976             WrappedStructureJS * wrapper = getWrapper(info.This());
00977             int numFields = wrapper->desc->getFieldCount(wrapper->value);
00978             
00979             int i = 0;
00980             v8::Handle<v8::Array> result = v8::Array::New(numFields);
00981             
00982             auto onField = [&] (const ValueDescription::FieldDescription & fd)
00983                 {
00984                     result->Set(v8::Uint32::New(i++), JS::toJS(fd.fieldName));
00985                 };
00986             
00987             wrapper->desc->forEachField(wrapper->value, onField);
00988 
00989             return scope.Close(result);
00990         } catch (const std::exception & exc) {
00991             cerr << "got exception in listNamed" << endl;
00992             cerr << exc.what() << endl;
00993             cerr << cstr(info.This()) << endl;
00994             //backtrace();
00995             return NULL_HANDLE;
00996         }
00997     }
00998 
00999 };
01000 
01001 v8::Persistent<v8::FunctionTemplate>
01002 WrappedStructureJS::
01003 tmpl;
01004 
01005 namespace {
01006 
01007 struct Init {
01008     Init()
01009     {
01010         registry.introduce("WrappedArrayJS", "bonus", WrappedArrayJS::Initialize);
01011         registry.introduce("WrappedStructureJS", "bonus", WrappedStructureJS::Initialize);
01012     }
01013 } init;
01014 
01015 } // file scope
01016 
01017 void
01018 initJsConverters(const ValueDescription & desc)
01019 {
01020     if (desc.jsConverters || desc.jsConvertersInitialized)
01021         return;
01022     
01023     std::unique_ptr<JSConverters> converters
01024         (new JSConverters);
01025 
01026     // Take a pointer so we bind over the pointer and copy it, not the
01027     // description
01028     auto descPtr = &desc;
01029 
01030     //cerr << "***** desc.kind = " << desc.kind << " for "
01031     //     << desc.typeName << endl;
01032 
01033     // Is it a structure?
01034     if (desc.kind == ValueKind::STRUCTURE) {
01035         //cerr << "got structure " << desc.typeName << endl;
01036 
01037         // Return structure-based converters
01038         converters->fromJs = [=] (void * field, const JSValue & val)
01039             {
01040                 // Is it an object of the correct type?
01041 
01042                 if (!val->IsObject()) {
01043                     throw ML::Exception("attempt to create structure from non-object "
01044                                         + cstr(val));
01045                 }
01046 #if 0
01047                 if (WrappedStructureJS::tmpl->HasInstance(val)) {
01048                     // Copy element by element
01049                 }
01050 #endif
01051 
01052                 // Firstly, clear all of the fields to their default value
01053                 descPtr->setDefault(field);
01054 
01055                 v8::HandleScope scope;
01056                 auto objPtr = v8::Object::Cast(*val);
01057                 
01058                 v8::Local<v8::Array> properties = objPtr->GetOwnPropertyNames();
01059 
01060                 for (int i = 0; i < properties->Length(); ++i) {
01061                     auto keyVal = properties->Get(i);
01062                     string fieldName = cstr(keyVal);
01063                     v8::Local<v8::Value> fieldVal = objPtr->Get(keyVal);
01064 
01065                     auto * fd = descPtr->hasField(field, fieldName);
01066                     if (!fd) {
01067                         Json::Value val = JS::fromJS(fieldVal);
01068                         // TODO: some kind of on-unknown-field function
01069 
01070                         cerr << "got unknown JS field " << fieldName << " = "
01071                              << val.toString() << endl;
01072                         
01073                         continue;
01074                     }
01075 
01076                     setFromJs(addOffset(field, fd->offset), fieldVal,
01077                               *fd->description);
01078                 }
01079             };
01080 
01081         converters->toJs = [=] (const void * field, std::shared_ptr<void> owner)
01082             {
01083                 //cerr << "to JS structure for " << descPtr->typeName << endl;
01084 
01085                 // Wrap it in a wrapped array object
01086                 v8::Local<v8::Object> result
01087                     = WrappedStructureJS::tmpl->GetFunction()->NewInstance();
01088                 auto wrapper = WrappedStructureJS::getWrapper(result);
01089                 wrapper->value = (void *)field;
01090                 wrapper->desc = descPtr;
01091                 wrapper->owner_ = owner;
01092 
01093                 return result;
01094             };
01095     }
01096 
01097     // Is it an array?
01098     if (desc.kind == ValueKind::ARRAY) {
01099         //cerr << "got array " << desc.typeName << endl;
01100 
01101         const ValueDescription * innerDesc = &descPtr->contained();
01102         
01103         // Convert each element of the array
01104         converters->fromJs = [=] (void * field, const JSValue & val)
01105             {
01106                 //cerr << "from JS array" << endl;
01107 
01108                 // Convert the entire lot in the JS into our type
01109                 if (val->IsNull()) {
01110                     descPtr->setArrayLength(field, 0);
01111                     return;
01112                 }
01113 
01114                 // Is it an object of the correct type?
01115                 if (val->IsObject()) {
01116                     if (WrappedArrayJS::tmpl->HasInstance(val)) {
01117                         //cerr << "Is a wrapped array" << endl;
01118                         auto wrapper = WrappedArrayJS::getWrapper(val);
01119 
01120                         // Same type?; do a direct copy
01121                         if (wrapper->desc->type == descPtr->type) {
01122                             descPtr->copyValue(wrapper->value, field);
01123                             return;
01124                         }
01125 
01126                         // Otherwise, copy element by element
01127                         size_t len = wrapper->desc->getArrayLength(wrapper->value);
01128                         
01129                         descPtr->setArrayLength(field, len);
01130 
01131                         auto & valContained = wrapper->desc->contained();
01132 
01133                         // Same inner type?
01134                         if (innerDesc->type == valContained.type) {
01135                             for (unsigned i = 0;  i < len;  ++i) {
01136                                 innerDesc->copyValue(wrapper->desc->getArrayElement(wrapper->value, i),
01137                                                      descPtr->getArrayElement(field, i));
01138                             }
01139                             return;
01140                         }
01141 
01142                         for (unsigned i = 0;  i < len;  ++i) {
01143                             for (unsigned i = 0;  i < len;  ++i) {
01144                                 innerDesc->convertAndCopy
01145                                 (wrapper->desc->getArrayElement(wrapper->value, i),
01146                                  *wrapper->desc,
01147                                  descPtr->getArrayElement(field, i));
01148                             }
01149                         }
01150 
01151                         return;
01152 
01153                         cerr << "descPtr = " << descPtr
01154                              << "wrapper->desc = " << wrapper->desc << endl;
01155                         cerr << "descPtr = " << descPtr->typeName
01156                              << "wrapper->desc = " << wrapper->desc->typeName << endl;
01157                         cerr << "field = " << field << " wrapper->value = "
01158                              << wrapper->value << endl;
01159 
01160                         // Copy element by element
01161                     }
01162                 }
01163 
01164                 if(!val->IsArray()) {
01165                     throw ML::Exception("invalid JSValue for array extraction");
01166                 }
01167 
01168                 auto arrPtr = v8::Array::Cast(*val);
01169 
01170                 descPtr->setArrayLength(field, arrPtr->Length());
01171 
01172                 for(int i=0; i<arrPtr->Length(); ++i) {
01173                     auto val = arrPtr->Get(i);
01174                     setFromJs(descPtr->getArrayElement(field, i), val, *innerDesc);
01175                 }
01176             };
01177 
01178         converters->toJs = [=] (const void * field, const std::shared_ptr<void> & owner)
01179             {
01180                 //cerr << "to JS array for " << descPtr->typeName << endl;
01181 
01182                 // Wrap it in a wrapped array object
01183                 v8::Local<v8::Object> result
01184                     = WrappedArrayJS::tmpl->GetFunction()->NewInstance();
01185                 auto wrapper = WrappedArrayJS::getWrapper(result);
01186                 wrapper->value = (void *)field;
01187                 wrapper->desc = descPtr;
01188                 wrapper->owner_ = owner;
01189 
01190                 return result;
01191             };
01192     }
01193 
01194     // Is it optional?
01195     if (desc.kind == ValueKind::OPTIONAL) {
01196         //cerr << "got optional " << desc.typeName << endl;
01197 
01198         const ValueDescription * innerDesc = &descPtr->contained();
01199 
01200         //cerr << "innerDesc = " << innerDesc << endl;
01201 
01202         // Return optional converters
01203         converters->fromJs = [=] (void * field, const JSValue & value)
01204             {
01205                 //cerr << "optional value = " << cstr(value) << endl;
01206                 // If the value is null, then we remove the optional value
01207                 if (value->IsNull() || value->IsUndefined()) {
01208                     descPtr->setDefault(field);
01209                     return;
01210                 }
01211 
01212                 //cerr << "*** setting inner value" << endl;
01213 
01214                 // Otherwise we get the inner value and set it
01215                 setFromJs(descPtr->optionalMakeValue(field),
01216                           value,
01217                           *innerDesc);
01218             };
01219 
01220         converters->toJs = [=] (const void * field, std::shared_ptr<void> owner)
01221             -> v8::Handle<v8::Value>
01222             {
01223                 // If the value is missing, we return null
01224                 if (descPtr->isDefault(field))
01225                     return v8::Null();
01226 
01227                 // Otherwise we return the inner value
01228                 return getFromJs(descPtr->optionalGetValue(field),
01229                                  *innerDesc,
01230                                  owner);
01231             };
01232     }
01233 
01234     //   Does it have any parent classes?
01235 
01236     // Is it an arithmetic type?
01237 
01238     // Default goes through JSON
01239     if (!converters->fromJs) {
01240         converters->fromJs = [=] (void * field, const JSValue & value)
01241             {
01242                 Json::Value val = JS::fromJS(value);
01243                 StructuredJsonParsingContext context(val);
01244                 descPtr->parseJson(field, context);
01245             };
01246     }
01247 
01248     if (!converters->toJs) {
01249         converters->toJs = [=] (const void * field, std::shared_ptr<void>)
01250             {
01251                 StructuredJsonPrintingContext context;
01252                 descPtr->printJson(field, context);
01253                 return JS::toJS(context.output);
01254             };
01255     }
01256 
01257     desc.jsConverters = converters.release();
01258     desc.jsConvertersInitialized = true;
01259 }
01260 
01261 void
01262 setFromJs(void * field,
01263           const JSValue & value,
01264           const ValueDescription & desc)
01265 {
01266     if (!desc.jsConvertersInitialized) {
01267         initJsConverters(desc);
01268     }
01269 
01270     desc.jsConverters->fromJs(field, value);
01271 }
01272 
01273 v8::Handle<v8::Value>
01274 getFromJs(const void * field,
01275           const ValueDescription & desc,
01276           const std::shared_ptr<void> & owner)
01277 {
01278     if (!desc.jsConvertersInitialized) {
01279         initJsConverters(desc);
01280     }
01281 
01282     return desc.jsConverters->toJs(field, owner);
01283 }
01284 
01285 
01286 template<typename Obj, typename Base>
01287 struct PropertyAccessViaDescription {
01288     static v8::Handle<v8::Value>
01289     getter(v8::Local<v8::String> property,
01290            const v8::AccessorInfo & info)
01291     {
01292         try {
01293             const ValueDescription * vd
01294                 = reinterpret_cast<const ValueDescription *>
01295                 (v8::External::Unwrap(info.Data()));
01296             auto p = Base::getSharedPtr(info.This());
01297             Obj * o = p.get();
01298             const StructureDescriptionBase::FieldDescription & fd
01299                 = vd->getField(cstr(property));
01300             return getFromJs(addOffset(o, fd.offset), *fd.description, p);
01301         } HANDLE_JS_EXCEPTIONS;
01302     }
01303 
01304     static void
01305     setter(v8::Local<v8::String> property,
01306            v8::Local<v8::Value> value,
01307            const v8::AccessorInfo & info)
01308     {
01309         try {
01310             const ValueDescription * vd
01311                 = reinterpret_cast<const ValueDescription *>
01312                 (v8::External::Unwrap(info.Data()));
01313             Obj * o = Base::getShared(info.This());
01314             const StructureDescriptionBase::FieldDescription & fd
01315                 = vd->getField(cstr(property));
01316             setFromJs(addOffset(o, fd.offset), value, *fd.description);
01317         } HANDLE_JS_EXCEPTIONS_SETTER;
01318     }
01319 };
01320 
01321 template<typename Wrapper, typename T>
01322 void registerFieldFromDescription(const StructureDescription<T> & desc,
01323                                   const std::string & fieldName)
01324 {
01325     Wrapper::tmpl->InstanceTemplate()
01326         ->SetAccessor(v8::String::NewSymbol(fieldName.c_str()),
01327                       PropertyAccessViaDescription<T, Wrapper>::getter,
01328                       PropertyAccessViaDescription<T, Wrapper>::setter,
01329                       v8::External::Wrap((void *)&desc),
01330                       v8::DEFAULT,
01331                       v8::PropertyAttribute(v8::DontDelete));
01332 }
01333 
01334 /*****************************************************************************/
01335 /* AD SPOT JS                                                                */
01336 /*****************************************************************************/
01337 
01338 extern const char * AdSpotName;
01339 const char * AdSpotName = "AdSpot";
01340 
01341 struct AdSpotJS
01342     : public JSWrapped2<AdSpot, AdSpotJS, AdSpotName,
01343                         bidRequestModule> {
01344 
01345         AdSpotJS(v8::Handle<v8::Object> This,
01346               const std::shared_ptr<AdSpot> & as
01347                   = std::shared_ptr<AdSpot>())
01348     {
01349         HandleScope scope;
01350         wrap(This, as);
01351     }
01352 
01353     static Handle<v8::Value>
01354     New(const Arguments & args)
01355     {
01356         try {
01357             new AdSpotJS(args.This(), ML::make_std_sp(new AdSpot()));
01358             return args.This();
01359         } HANDLE_JS_EXCEPTIONS;
01360     }
01361 
01362     static void
01363     Initialize()
01364     {
01365         Persistent<FunctionTemplate> t = Register(New);
01366 
01367         static DefaultDescription<AdSpot> desc;
01368 
01369         for (auto & f: desc.fields) {
01370             const char * name = f.first;
01371             registerFieldFromDescription<JSWrapped2>(desc, name);
01372         }
01373 
01374         registerRWProperty(&AdSpot::id, "id",
01375                            v8::DontDelete);
01376         registerRWProperty(&AdSpot::reservePrice, "reservePrice",
01377                            v8::DontDelete);
01378         registerROProperty(&AdSpot::formats, "formats",
01379                            v8::ReadOnly | v8::DontDelete);
01380 
01381 #if 0
01382         t->InstanceTemplate()
01383             ->SetAccessor(String::NewSymbol("width"), widthsGetter,
01384                           0, v8::Handle<v8::Value>(), DEFAULT,
01385                           PropertyAttribute(ReadOnly | DontDelete));
01386         t->InstanceTemplate()
01387             ->SetAccessor(String::NewSymbol("height"), heightsGetter,
01388                           0, v8::Handle<v8::Value>(), DEFAULT,
01389                           PropertyAttribute(ReadOnly | DontDelete));
01390 #endif
01391     }
01392 
01393 #if 0
01394     static v8::Handle<v8::Value>
01395     widthsGetter(v8::Local<v8::String> property,
01396                   const v8::AccessorInfo & info)
01397     {
01398         try {
01399             const auto & f = getShared(info.This())->formats;
01400             vector<int> v2;
01401             for (unsigned i = 0;  i < f.size();  ++i)
01402                 v2.push_back(f[i].width);
01403             return JS::toJS(v2);
01404         } HANDLE_JS_EXCEPTIONS;
01405     }
01406 
01407     static v8::Handle<v8::Value>
01408     heightsGetter(v8::Local<v8::String> property,
01409                   const v8::AccessorInfo & info)
01410     {
01411         try {
01412             const auto & f = getShared(info.This())->formats;
01413             vector<int> v2;
01414             for (unsigned i = 0;  i < f.size();  ++i)
01415                 v2.push_back(f[i].height);
01416             return JS::toJS(v2);
01417         } HANDLE_JS_EXCEPTIONS;
01418     }
01419 #endif
01420 
01421 };
01422 
01423 void to_js(JS::JSValue & value, const std::shared_ptr<AdSpot> & as)
01424 {
01425     value = AdSpotJS::toJS(as);
01426 }
01427 
01428 void to_js(JS::JSValue & value, const AdSpot & as)
01429 {
01430     to_js(value, ML::make_std_sp(new AdSpot(as)));
01431 }
01432 
01433 std::shared_ptr<AdSpot>
01434 from_js(const JSValue & value, std::shared_ptr<AdSpot> *)
01435 {
01436     return AdSpotJS::fromJS(value);
01437 }
01438 
01439 AdSpot
01440 from_js(const JSValue & value, AdSpot *)
01441 {
01442     return *AdSpotJS::fromJS(value).get();
01443 }
01444 
01445 
01446 /*****************************************************************************/
01447 /* BID REQUEST JS                                                            */
01448 /*****************************************************************************/
01449 
01450 const char * BidRequestName = "BidRequest";
01451 
01452 struct BidRequestJS
01453     : public JSWrapped2<BidRequest, BidRequestJS, BidRequestName,
01454                         bidRequestModule> {
01455 
01456     BidRequestJS(v8::Handle<v8::Object> This,
01457               const std::shared_ptr<BidRequest> & bid
01458                   = std::shared_ptr<BidRequest>())
01459     {
01460         HandleScope scope;
01461         wrap(This, bid);
01462     }
01463 
01464     static Handle<v8::Value>
01465     New(const Arguments & args)
01466     {
01467         try {
01468             if (args.Length() > 0) {
01469                 if (BidRequestJS::tmpl->HasInstance(args[0])) {
01470                     //cerr << "copy existing" << endl;
01471                     // Copy an existing bid request
01472                     std::shared_ptr<BidRequest> oldBr
01473                         = JS::fromJS(args[0]);
01474                     auto br = std::make_shared<BidRequest>();
01475                     *br = *oldBr;
01476                     new BidRequestJS(args.This(), br);
01477                     
01478                 }
01479                 else if (args[0]->IsString()) {
01480                     // Parse from a string
01481                     //cerr << "parse string" << endl;
01482                     Utf8String request = getArg<Utf8String>(args, 0, "request");
01483                     string source = getArg<string>(args, 1, "datacratic", "source");
01484                     new BidRequestJS(args.This(),
01485                                      ML::make_std_sp(BidRequest::parse(source, request)));
01486                 }
01487                 else if (args[0]->IsObject()) {
01488                     //cerr << "parse object" << endl;
01489                     // Parse from an object by going through JSON
01490                     Json::Value json = JS::fromJS(args[0]);
01491                     //cerr << "JSON = " << json << endl;
01492                     auto br = std::make_shared<BidRequest>();
01493                     *br = BidRequest::createFromJson(json);
01494                     new BidRequestJS(args.This(), br);
01495                 }
01496                 else throw ML::Exception("Cannot convert " + cstr(args[0])
01497                                          + " to BidRequest");
01498             } else {
01499                 new BidRequestJS(args.This(), ML::make_std_sp(new BidRequest()));
01500             }
01501             return args.This();
01502         } HANDLE_JS_EXCEPTIONS;
01503     }
01504 
01505     static void
01506     Initialize()
01507     {
01508         Persistent<FunctionTemplate> t = Register(New);
01509 
01510         registerRWProperty(&BidRequest::auctionId, "id", v8::DontDelete);
01511         registerRWProperty(&BidRequest::timestamp, "timestamp", v8::DontDelete);
01512         registerRWProperty(&BidRequest::isTest, "isTest", v8::DontDelete);
01513         registerRWProperty(&BidRequest::url, "url", v8::DontDelete);
01514         registerRWProperty(&BidRequest::meta, "meta", v8::DontDelete);
01515         registerRWProperty(&BidRequest::ipAddress, "ipAddress", v8::DontDelete);
01516         registerRWProperty(&BidRequest::userAgent, "userAgent", v8::DontDelete);
01517         registerRWProperty(&BidRequest::language, "language", v8::DontDelete);
01518         registerRWProperty(&BidRequest::protocolVersion,
01519                            "protocolVersion", v8::DontDelete);
01520         registerRWProperty(&BidRequest::exchange, "exchange", v8::DontDelete);
01521         registerRWProperty(&BidRequest::provider, "provider", v8::DontDelete);
01522 
01523         static DefaultDescription<BidRequest> desc;
01524 
01526         registerFieldFromDescription<BidRequestJS>(desc, "spots");
01527         registerFieldFromDescription<BidRequestJS>(desc, "imp");
01528 
01529 
01530         //registerRWProperty(&BidRequest::imp, "imp", v8::DontDelete);
01531         
01532         //registerRWProperty(&BidRequest::winSurcharge, "winSurchage",
01533         //                   v8::DontDelete);
01534         
01535         // TODO: these should go...
01536         //registerRWProperty(&BidRequest::creative, "creative", v8::DontDelete);
01537         registerMemberFn(&BidRequest::toJson, "toJSON");
01538 
01539         NODE_SET_PROTOTYPE_METHOD(t, "getSegmentsFromSource",
01540                                   getSegmentsFromSource);
01541 
01542         t->InstanceTemplate()
01543             ->SetAccessor(String::NewSymbol("segments"), segmentsGetter,
01544                           segmentsSetter, v8::Handle<v8::Value>(), DEFAULT,
01545                           PropertyAttribute(DontDelete));
01546 
01547         t->InstanceTemplate()
01548             ->SetAccessor(String::NewSymbol("restrictions"), restrictionsGetter,
01549                           restrictionsSetter, v8::Handle<v8::Value>(), DEFAULT,
01550                           PropertyAttribute(DontDelete));
01551 
01552         t->InstanceTemplate()
01553             ->SetAccessor(String::NewSymbol("userIds"), userIdsGetter,
01554                           userIdsSetter, v8::Handle<v8::Value>(), DEFAULT,
01555                           PropertyAttribute(DontDelete));
01556 
01557         t->InstanceTemplate()
01558             ->SetAccessor(String::NewSymbol("location"), locationGetter,
01559                           locationSetter, v8::Handle<v8::Value>(), DEFAULT,
01560                           PropertyAttribute(DontDelete));
01561     }
01562 
01563     static Handle<v8::Value>
01564     getSegmentsFromSource(const Arguments & args)
01565     {
01566         try {
01567             string segmentProvider = getArg(args, 0, "segmentProvider");
01568             auto sh = getShared(args.This());
01569             auto it = sh->segments.find(segmentProvider);
01570             if (it == sh->segments.end())
01571                 return v8::Null();
01572             return JS::toJS(it->second);
01573         } HANDLE_JS_EXCEPTIONS;
01574     }
01575 
01576     static v8::Handle<v8::Value>
01577     segmentsGetter(v8::Local<v8::String> property,
01578                   const v8::AccessorInfo & info)
01579     {
01580         try {
01581             v8::Handle<v8::Value> segs
01582                 = SegmentsBySourceJS::toJS
01583                 (ML::make_unowned_std_sp(getShared(info.This())->segments));
01584             SegmentsBySourceJS * wrapper
01585                 = SegmentsBySourceJS::getWrapper(segs);
01586             wrapper->owner_ = getSharedPtr(info.This());
01587             return segs;
01588         } HANDLE_JS_EXCEPTIONS;
01589     }
01590 
01591     static void
01592     segmentsSetter(v8::Local<v8::String> property,
01593                   v8::Local<v8::Value> value,
01594                   const v8::AccessorInfo & info)
01595     {
01596         try {
01597             if (SegmentsBySourceJS::tmpl->HasInstance(value)) {
01598                 getShared(info.This())->segments
01599                     = *SegmentsBySourceJS::getShared(value);
01600                 return;
01601             }
01602             if (value->IsObject()) {
01603                 map<std::string, std::shared_ptr<SegmentList> > segs;
01604                 segs = from_js(JSValue(value), &segs);
01605                 getShared(info.This())->segments = SegmentsBySource(segs);
01606                 return;
01607             }
01608             throw ML::Exception("can't convert " + cstr(value)
01609                                 + " into segment list");
01610         } HANDLE_JS_EXCEPTIONS_SETTER;
01611     }
01612 
01613     static v8::Handle<v8::Value>
01614     restrictionsGetter(v8::Local<v8::String> property,
01615                   const v8::AccessorInfo & info)
01616     {
01617         try {
01618             v8::Handle<v8::Value> segs
01619                 = SegmentsBySourceJS::toJS
01620                 (ML::make_unowned_std_sp(getShared(info.This())->restrictions));
01621             SegmentsBySourceJS * wrapper
01622                 = SegmentsBySourceJS::getWrapper(segs);
01623             wrapper->owner_ = getSharedPtr(info.This());
01624             return segs;
01625         } HANDLE_JS_EXCEPTIONS;
01626     }
01627 
01628     static void
01629     restrictionsSetter(v8::Local<v8::String> property,
01630                        v8::Local<v8::Value> value,
01631                        const v8::AccessorInfo & info)
01632     {
01633         try {
01634             if (SegmentsBySourceJS::tmpl->HasInstance(value)) {
01635                 getShared(info.This())->restrictions
01636                     = *SegmentsBySourceJS::getShared(value);
01637                 return;
01638             }
01639             if (value->IsObject()) {
01640                 map<std::string, std::shared_ptr<SegmentList> > segs;
01641                 segs = from_js(JSValue(value), &segs);
01642                 getShared(info.This())->restrictions = SegmentsBySource(segs);
01643                 return;
01644             }
01645             throw ML::Exception("can't convert " + cstr(value)
01646                                 + " into segment list");
01647         } HANDLE_JS_EXCEPTIONS_SETTER;
01648     }
01649 
01650     static v8::Handle<v8::Value>
01651     userIdsGetter(v8::Local<v8::String> property,
01652                   const v8::AccessorInfo & info)
01653     {
01654         try {
01655             auto owner = getSharedPtr(info.This());
01656             return UserIdsJS::toJS(owner->userIds, owner);
01657         } HANDLE_JS_EXCEPTIONS;
01658     }
01659 
01660     static void
01661     userIdsSetter(v8::Local<v8::String> property,
01662                        v8::Local<v8::Value> value,
01663                        const v8::AccessorInfo & info)
01664     {
01665         try {
01666             if (UserIdsJS::tmpl->HasInstance(value)) {
01667                 getShared(info.This())->userIds
01668                     = *UserIdsJS::getShared(value);
01669                 return;
01670             }
01671             if (value->IsObject()) {
01672                 map<std::string, Id> ids;
01673                 ids = from_js(JSValue(value), &ids);
01674                 auto & uids = getShared(info.This())->userIds;
01675                 for (auto it = ids.begin(), end = ids.end();
01676                      it != end;  ++it)
01677                     uids.add(it->second, it->first);
01678                 return;
01679             }
01680             throw ML::Exception("can't convert " + cstr(value)
01681                                 + " into segment list");
01682         } HANDLE_JS_EXCEPTIONS_SETTER;
01683     }
01684 
01685     static v8::Handle<v8::Value>
01686     locationGetter(v8::Local<v8::String> property,
01687                   const v8::AccessorInfo & info)
01688     {
01689         try {
01690             auto owner = getSharedPtr(info.This());
01691             return LocationJS::toJS(owner->location, owner);
01692         } HANDLE_JS_EXCEPTIONS;
01693     }
01694 
01695     static void
01696     locationSetter(v8::Local<v8::String> property,
01697                        v8::Local<v8::Value> value,
01698                        const v8::AccessorInfo & info)
01699     {
01700         try {
01701             if (LocationJS::tmpl->HasInstance(value)) {
01702                 getShared(info.This())->location
01703                     = *LocationJS::getShared(value);
01704                 return;
01705             }
01706             if (value->IsObject()) {
01707                 Json::Value json = JS::fromJS(value);
01708                 getShared(info.This())->location
01709                     = Location::createFromJson(json);
01710                 return;
01711             }
01712             throw ML::Exception("can't convert " + cstr(value)
01713                                 + " into location info");
01714         } HANDLE_JS_EXCEPTIONS_SETTER;
01715     }
01716 
01717 };
01718 
01719 std::shared_ptr<BidRequest>
01720 from_js(const JSValue & value, std::shared_ptr<BidRequest> *)
01721 {
01722     return BidRequestJS::fromJS(value);
01723 }
01724 
01725 BidRequest *
01726 from_js(const JSValue & value, BidRequest **)
01727 {
01728     return BidRequestJS::fromJS(value).get();
01729 }
01730 
01731 std::shared_ptr<BidRequest>
01732 from_js_ref(const JSValue & value, std::shared_ptr<BidRequest> *)
01733 {
01734     return BidRequestJS::fromJS(value);
01735 }
01736 
01737 void to_js(JS::JSValue & value, const std::shared_ptr<BidRequest> & br)
01738 {
01739     value = BidRequestJS::toJS(br);
01740 }
01741 
01742 
01743 std::shared_ptr<BidRequest>
01744 getBidRequestSharedPointer(const JS::JSValue & value)
01745 {
01746     if(BidRequestJS::tmpl->HasInstance(value))
01747     {
01748         std::shared_ptr<BidRequest> br = BidRequestJS::getSharedPtr(value);
01749         return br;
01750     }
01751     std::shared_ptr<BidRequest> br;
01752     return br;
01753 }
01754 
01755 
01756 extern "C" void
01757 init(Handle<v8::Object> target)
01758 {
01759     Datacratic::JS::registry.init(target, bidRequestModule);
01760 }
01761 
01762 } // namespace JS
01763 } // namespace Datacratic
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator