/* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NODEJS_ADAPTER_INCLUDE_ASYNCMETHODCALL_H #define NODEJS_ADAPTER_INCLUDE_ASYNCMETHODCALL_H #include #include "JsConverter.h" #include "async_common.h" using v8::Function; using v8::Value; using v8::Handle; using v8::Isolate; /** These classes wrap various sorts of C and C++ functions for use as either * synchronous or asynchronous JavaScript methods. * * There are two class hierarchies declared here. * The first hierarchy is: * AsyncCall * -> Call_Returning template over return type * -> NativeMethodCall template over class * * The second hierarchy is a set of alternate classes * Call_1_ , Call_2_ , .. Call_8_ * expressing the set of arguments to a function or method call * * The base class AsyncCall wraps the worker-thread run() routine and the * main thread (post-run) doAsyncCallback() needed for async execution. * * The run() method, declared void run(void), will be scheduled to run in a * uv worker thread. * * The doAsyncCallback() method will take a JavaScript context. It is expected * to prepare the result and call the user's callback function. * * The templated class AsyncCall_Returning inherits from AsyncCall and * adds a return type, which is initialized at 0. * * OTHER NOTES: * The standard AsyncCall constructor allocates a persistent V8 object, so * it can only run in the main JavaScript thread. * However, the constructor for AsyncAsyncCall runs in a UV worker thread, * so there is an alternative chain of protected constructors from * AsyncAsyncCall up to AsyncCall. */ /** Base class **/ class AsyncCall { protected: /* Member variables */ Persistent callback; Isolate * isolate; /* Protected constructor chain from AsyncAsyncCall */ AsyncCall(Isolate * i, Handle cb) : isolate(i) { callback.Reset(isolate, cb); }; public: AsyncCall(Isolate * i, Local callbackFunc) : isolate(i) { callback.Reset(isolate, Local::Cast(callbackFunc)); } /* Destructor */ virtual ~AsyncCall() { callback.Reset(); } /* Methods (Pure virtual) */ virtual void run(void) = 0; virtual void doAsyncCallback(Local) = 0; /* Base Class Virtual Methods */ virtual void handleErrors(void) { } /* Base Class Fixed Methods */ void runAsync() { // if (callback->IsCallable()) { uv_work_t * req = new uv_work_t; req->data = (void *) this; uv_queue_work(uv_default_loop(), req, work_thd_run, main_thd_complete); // } //else { // isolate->ThrowException( // Exception::TypeError(String::New("Uncallable Callback"))); //} } }; /** ReturnValueHandler is a base class for AsyncCall_Returning with one specialization for wrapped pointers to native objects, and another specialization for everything else. **/ // Primary Template: ReturnValueHandler for all non-pointer types template class ReturnValueHandler { public: ReturnValueHandler() {} void wrapReturnValueAs(Envelope * e) { assert(false); } Local getJsValue(Isolate * isolate, T value) { return toJS(isolate, value); } }; // ReturnValueHandler specialization for wrapped pointer types template class ReturnValueHandler { private: Envelope * envelope; public: ReturnValueHandler() : envelope(0) {} void wrapReturnValueAs(Envelope * e) { envelope = e; } Local getJsValue(Isolate * isolate, T * objPtr) { return envelope->wrap(objPtr); } }; /** First-level template class; templated over return types **/ template class AsyncCall_Returning : public AsyncCall, public ReturnValueHandler { protected: /* Protected Constructor Chain */ AsyncCall_Returning(Isolate * isol, Handle callback) : AsyncCall(isol, callback), ReturnValueHandler(), error(0) {} public: /* Member variables */ NativeCodeError *error; RETURN_TYPE return_val; /* Constructors */ AsyncCall_Returning(Isolate * isol, Local callback) : AsyncCall(isol, callback), ReturnValueHandler(), error(0) {} AsyncCall_Returning(Isolate * isol, Local callback, RETURN_TYPE rv) : AsyncCall(isol, callback), ReturnValueHandler(), error(0), return_val(rv) {} /* Destructor */ virtual ~AsyncCall_Returning() { if(error) delete error; } /* Methods */ Local jsReturnVal() { EscapableHandleScope scope(AsyncCall::isolate); return scope.Escape(this->getJsValue(AsyncCall::isolate, return_val)); } /* doAsyncCallback() is an async callback, run by main_thread_complete(). */ void doAsyncCallback(Local context) { EscapableHandleScope scope(AsyncCall::isolate); Handle cb_args[2]; if(error) cb_args[0] = error->toJS(); else cb_args[0] = Null(AsyncCall::isolate); cb_args[1] = this->getJsValue(AsyncCall::isolate, return_val); ToLocal(& callback)->Call(context, 2, cb_args); } }; /** Second-level template class for C++ method calls: templated over class of native object. This class is the home of error handling for C++ code. **/ template class NativeMethodCall : public AsyncCall_Returning { public: /* Member variables */ typedef NativeCodeError * (*errorHandler_fn_t)(R, C *); C * native_obj; errorHandler_fn_t errorHandler; /* Constructor */ NativeMethodCall(const Arguments &args, int callback_idx) : AsyncCall_Returning(args.GetIsolate(), args[callback_idx]), errorHandler(0) { native_obj = unwrapPointer(args.Holder()); DEBUG_ASSERT(native_obj != NULL); } /* Methods */ void handleErrors(void) { if(errorHandler) AsyncCall_Returning::error = errorHandler(AsyncCall_Returning::return_val, native_obj); } protected: /* Alternative constructor used only by AsyncAsyncCall */ NativeMethodCall(C * obj, Handle callback, errorHandler_fn_t errHandler) : AsyncCall_Returning(v8::Isolate::GetCurrent(), callback), native_obj(obj), errorHandler(errHandler) {}; }; /** AsyncAsyncCall is used to wrap returns from NDB Asynchronoous APIs. **/ template class AsyncAsyncCall : public NativeMethodCall { public: typedef NativeCodeError * (*errorHandler_fn_t)(R, C *); /* Constructor */ AsyncAsyncCall(C * obj, Handle callback, errorHandler_fn_t errHandler) : NativeMethodCall(obj, callback, errHandler) {}; /* Methods */ void run(void) {}; }; /** Alternate second-level template class for calls returning void. No error handling here. **/ template class NativeVoidMethodCall : public AsyncCall_Returning { public: /* Member variables */ C * native_obj; /* Constructor */ NativeVoidMethodCall(const Arguments &args, int callback_idx) : AsyncCall_Returning(args.GetIsolate(), args[callback_idx], 1) /*callback*/ { native_obj = unwrapPointer(args.Holder()); DEBUG_ASSERT(native_obj != NULL); } }; /** Base class templated over arguments */ template class Call_8_ { public: /* Member variables */ JsValueConverter arg0converter; A0 arg0; JsValueConverter arg1converter; A1 arg1; JsValueConverter arg2converter; A2 arg2; JsValueConverter arg3converter; A3 arg3; JsValueConverter arg4converter; A4 arg4; JsValueConverter arg5converter; A5 arg5; JsValueConverter arg6converter; A6 arg6; JsValueConverter arg7converter; A7 arg7; /* Constructor */ Call_8_(const Arguments &args) : arg0converter(args[0]), arg1converter(args[1]), arg2converter(args[2]), arg3converter(args[3]), arg4converter(args[4]), arg5converter(args[5]), arg6converter(args[6]), arg7converter(args[7]) { arg0 = arg0converter.toC(); arg1 = arg1converter.toC(); arg2 = arg2converter.toC(); arg3 = arg3converter.toC(); arg4 = arg4converter.toC(); arg5 = arg5converter.toC(); arg6 = arg6converter.toC(); arg7 = arg7converter.toC(); } }; template class Call_7_ { public: /* Member variables */ JsValueConverter arg0converter; A0 arg0; JsValueConverter arg1converter; A1 arg1; JsValueConverter arg2converter; A2 arg2; JsValueConverter arg3converter; A3 arg3; JsValueConverter arg4converter; A4 arg4; JsValueConverter arg5converter; A5 arg5; JsValueConverter arg6converter; A6 arg6; /* Constructor */ Call_7_(const Arguments &args) : arg0converter(args[0]), arg1converter(args[1]), arg2converter(args[2]), arg3converter(args[3]), arg4converter(args[4]), arg5converter(args[5]), arg6converter(args[6]) { arg0 = arg0converter.toC(); arg1 = arg1converter.toC(); arg2 = arg2converter.toC(); arg3 = arg3converter.toC(); arg4 = arg4converter.toC(); arg5 = arg5converter.toC(); arg6 = arg6converter.toC(); } }; template class Call_6_ { public: /* Member variables */ JsValueConverter arg0converter; A0 arg0; JsValueConverter arg1converter; A1 arg1; JsValueConverter arg2converter; A2 arg2; JsValueConverter arg3converter; A3 arg3; JsValueConverter arg4converter; A4 arg4; JsValueConverter arg5converter; A5 arg5; /* Constructor */ Call_6_(const Arguments &args) : arg0converter(args[0]), arg1converter(args[1]), arg2converter(args[2]), arg3converter(args[3]), arg4converter(args[4]), arg5converter(args[5]) { arg0 = arg0converter.toC(); arg1 = arg1converter.toC(); arg2 = arg2converter.toC(); arg3 = arg3converter.toC(); arg4 = arg4converter.toC(); arg5 = arg5converter.toC(); } }; template class Call_5_ { public: /* Member variables */ JsValueConverter arg0converter; A0 arg0; JsValueConverter arg1converter; A1 arg1; JsValueConverter arg2converter; A2 arg2; JsValueConverter arg3converter; A3 arg3; JsValueConverter arg4converter; A4 arg4; /* Constructor */ Call_5_(const Arguments &args) : arg0converter(args[0]), arg1converter(args[1]), arg2converter(args[2]), arg3converter(args[3]), arg4converter(args[4]) { arg0 = arg0converter.toC(); arg1 = arg1converter.toC(); arg2 = arg2converter.toC(); arg3 = arg3converter.toC(); arg4 = arg4converter.toC(); } }; template class Call_4_ { public: /* Member variables */ JsValueConverter arg0converter; A0 arg0; JsValueConverter arg1converter; A1 arg1; JsValueConverter arg2converter; A2 arg2; JsValueConverter arg3converter; A3 arg3; /* Constructor */ Call_4_(const Arguments &args) : arg0converter(args[0]), arg1converter(args[1]), arg2converter(args[2]), arg3converter(args[3]) { arg0 = arg0converter.toC(); arg1 = arg1converter.toC(); arg2 = arg2converter.toC(); arg3 = arg3converter.toC(); } }; template class Call_3_ { public: /* Member variables */ JsValueConverter arg0converter; A0 arg0; JsValueConverter arg1converter; A1 arg1; JsValueConverter arg2converter; A2 arg2; /* Constructor */ Call_3_(const Arguments &args) : arg0converter(args[0]), arg1converter(args[1]), arg2converter(args[2]) { arg0 = arg0converter.toC(); arg1 = arg1converter.toC(); arg2 = arg2converter.toC(); } }; template class Call_2_ { public: /* Member variables */ JsValueConverter arg0converter; A0 arg0; JsValueConverter arg1converter; A1 arg1; /* Constructor */ Call_2_(const Arguments &args) : arg0converter(args[0]), arg1converter(args[1]) { arg0 = arg0converter.toC(); arg1 = arg1converter.toC(); } }; template class Call_1_ { public: /* Member variables */ JsValueConverter arg0converter; A0 arg0; /* Constructor */ Call_1_(const Arguments &args) : arg0converter(args[0]) { arg0 = arg0converter.toC(); } }; #endif