javaembed.cpp 142 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "platform.h"
  14. #include <jni.h>
  15. #include "jexcept.hpp"
  16. #include "jthread.hpp"
  17. #include "junicode.hpp"
  18. #include "hqlplugins.hpp"
  19. #include "deftype.hpp"
  20. #include "eclhelper.hpp"
  21. #include "eclrtl.hpp"
  22. #include "eclrtl_imp.hpp"
  23. #include "rtlfield.hpp"
  24. #include "rtlds_imp.hpp"
  25. #include "jprop.hpp"
  26. #include "build-config.h"
  27. #include "roxiemem.hpp"
  28. #include "nbcd.hpp"
  29. #include "rtlformat.hpp"
  30. #include "esdl_def.hpp"
  31. #ifndef _WIN32
  32. #include <sys/resource.h>
  33. #endif
  34. static const char * compatibleVersions[] = {
  35. "Java Embed Helper 1.0.0",
  36. NULL };
  37. static const char *version = "Java Embed Helper 1.0.0";
  38. #ifdef _DEBUG
  39. //#define TRACE_CLASSFILE
  40. #endif
  41. extern "C" DECL_EXPORT bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
  42. {
  43. if (pb->size == sizeof(ECLPluginDefinitionBlockEx))
  44. {
  45. ECLPluginDefinitionBlockEx * pbx = (ECLPluginDefinitionBlockEx *) pb;
  46. pbx->compatibleVersions = compatibleVersions;
  47. }
  48. else if (pb->size != sizeof(ECLPluginDefinitionBlock))
  49. return false;
  50. pb->magicVersion = PLUGIN_VERSION;
  51. pb->version = version;
  52. pb->moduleName = "java";
  53. pb->ECL = NULL;
  54. pb->flags = PLUGIN_MULTIPLE_VERSIONS;
  55. pb->description = "Java Embed Helper";
  56. return true;
  57. }
  58. __declspec(noreturn) static void UNSUPPORTED(const char *feature) __attribute__((noreturn));
  59. static void UNSUPPORTED(const char *feature)
  60. {
  61. throw MakeStringException(-1, "UNSUPPORTED feature: %s not supported in java plugin", feature);
  62. }
  63. namespace javaembed {
  64. // Use a global object to ensure that the Java VM is initialized once only.
  65. // We would like to create it lazily for two reasons:
  66. // 1. So that we only get a JVM if we need one (even if we have loaded the plugin)
  67. // 2. It's important for the JVM to be initialized AFTER we have set up signal handlers, as it
  68. // likes to set its own (in particular, it seems to intercept and ignore some SIGSEGV during the
  69. // garbage collection).
  70. // Unfortunately, it seems that the design of the JNI interface is such that JNI_CreateJavaVM has to be called on the 'main thread'.
  71. // So we can't achieve 1, and 2 requires that we create via the INIT_MODULE mechanism (rather than just a static object), and that
  72. // any engines that call InitModuleObjects() or load plugins dynamically do so AFTER setting any signal handlers or calling
  73. // EnableSEHtoExceptionMapping
  74. //
  75. static class JavaGlobalState
  76. {
  77. public:
  78. JavaGlobalState()
  79. {
  80. JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
  81. StringArray optionStrings;
  82. const char* origPath = getenv("CLASSPATH");
  83. StringBuffer newPath;
  84. newPath.append("-Djava.class.path=");
  85. if (origPath && *origPath)
  86. {
  87. newPath.append(origPath).append(ENVSEPCHAR);
  88. }
  89. const IProperties &conf = queryEnvironmentConf();
  90. if (conf.hasProp("classpath"))
  91. {
  92. conf.getProp("classpath", newPath);
  93. newPath.append(ENVSEPCHAR);
  94. }
  95. else
  96. {
  97. newPath.append(INSTALL_DIR).append(PATHSEPCHAR).append("classes").append(ENVSEPCHAR);
  98. }
  99. newPath.append(".");
  100. optionStrings.append(newPath);
  101. if (conf.hasProp("jvmlibpath"))
  102. {
  103. StringBuffer libPath;
  104. libPath.append("-Djava.library.path=");
  105. conf.getProp("jvmlibpath", libPath);
  106. optionStrings.append(libPath);
  107. }
  108. // Options we should set (but allow for override with jvmoptions below)
  109. optionStrings.append("-XX:-UseLargePages");
  110. if (conf.hasProp("jvmoptions"))
  111. {
  112. // Use space as field sep as ':' and ';' are valid
  113. optionStrings.appendList(conf.queryProp("jvmoptions"), " ");
  114. }
  115. // Options we know we always want set
  116. optionStrings.append("-Xrs");
  117. //optionStrings.append("-XX:+TraceClassLoading");
  118. #ifdef RLIMIT_STACK
  119. // JVM has a habit of reducing the stack limit on main thread to 1M - probably dates back to when it was actually an increase...
  120. StringBuffer stackOption("-Xss");
  121. struct rlimit limit;
  122. rlim_t slim = 0;
  123. if (getrlimit (RLIMIT_STACK, &limit)==0)
  124. slim = limit.rlim_cur;
  125. if (!slim)
  126. slim = 8*1024*1024;
  127. if (slim >= 1*1024*1024)
  128. {
  129. stackOption.append((__uint64) slim);
  130. optionStrings.append(stackOption);
  131. }
  132. #endif
  133. // These may be useful for debugging
  134. #ifdef _DEBUG
  135. // optionStrings.append("-Xcheck:jni");
  136. // optionStrings.append("-verbose:jni");
  137. #endif
  138. JavaVMOption* options = new JavaVMOption[optionStrings.length()];
  139. ForEachItemIn(idx, optionStrings)
  140. {
  141. // DBGLOG("javaembed: Setting JVM option: %s",(char *)optionStrings.item(idx));
  142. options[idx].optionString = (char *) optionStrings.item(idx);
  143. options[idx].extraInfo = NULL;
  144. }
  145. vm_args.nOptions = optionStrings.length();
  146. vm_args.options = options;
  147. vm_args.ignoreUnrecognized = true;
  148. vm_args.version = JNI_VERSION_1_8;
  149. /* load and initialize a Java VM, return a JNI interface pointer in env */
  150. JNIEnv *env; /* receives pointer to native method interface */
  151. int createResult = JNI_CreateJavaVM(&javaVM, (void**)&env, &vm_args);
  152. delete [] options;
  153. if (createResult != 0)
  154. throw MakeStringException(0, "javaembed: Unable to initialize JVM (%d)",createResult);
  155. // DBGLOG("JNI environment version %x loaded", env->GetVersion()); // Comes out a bit too early
  156. }
  157. ~JavaGlobalState()
  158. {
  159. // We don't attempt to destroy the Java VM, as it's buggy...
  160. }
  161. JavaVM *javaVM; /* denotes a Java VM */
  162. } *globalState;
  163. static StringBuffer helperLibraryName;
  164. #ifdef _WIN32
  165. EXTERN_C IMAGE_DOS_HEADER __ImageBase;
  166. #endif
  167. MODULE_INIT(INIT_PRIORITY_STANDARD)
  168. {
  169. globalState = new JavaGlobalState;
  170. // Make sure we are never unloaded (as JVM does not support it)
  171. // we do this by doing a dynamic load of the javaembed library
  172. #ifdef _WIN32
  173. char ln[_MAX_PATH];
  174. ::GetModuleFileName((HINSTANCE)&__ImageBase, ln, _MAX_PATH);
  175. if (strstr(path, "javaembed"))
  176. {
  177. HINSTANCE h = LoadSharedObject(ln, false, false);
  178. helperLibraryName.set(ln);
  179. DBGLOG("LoadSharedObject returned %p", h);
  180. }
  181. #else
  182. if (findLoadedModule(helperLibraryName, "javaembed"))
  183. {
  184. HINSTANCE h = LoadSharedObject(helperLibraryName, false, false);
  185. // Deliberately leak this handle
  186. }
  187. #endif
  188. return true;
  189. }
  190. MODULE_EXIT()
  191. {
  192. // We don't attempt to destroy the Java VM, as it's buggy...
  193. // delete globalState;
  194. // globalState = NULL;
  195. }
  196. static void checkType(type_t javatype, size32_t javasize, type_t ecltype, size32_t eclsize)
  197. {
  198. if (javatype != ecltype || javasize != eclsize)
  199. throw MakeStringException(0, "javaembed: Type mismatch"); // MORE - could provide some details!
  200. }
  201. static void checkException(JNIEnv *JNIenv, bool fail=true)
  202. {
  203. if (JNIenv->ExceptionCheck())
  204. {
  205. jthrowable exception = JNIenv->ExceptionOccurred();
  206. JNIenv->ExceptionClear();
  207. jclass throwableClass = JNIenv->FindClass("java/lang/Throwable");
  208. jmethodID throwableToString = JNIenv->GetMethodID(throwableClass, "toString", "()Ljava/lang/String;");
  209. jstring cause = (jstring) JNIenv->CallObjectMethod(exception, throwableToString);
  210. const char *text = JNIenv->GetStringUTFChars(cause, 0);
  211. VStringBuffer message("javaembed: %s", text);
  212. JNIenv->ReleaseStringUTFChars(cause, text);
  213. if (fail)
  214. rtlFail(0, message);
  215. throw MakeStringExceptionDirect(0, message);
  216. }
  217. }
  218. //-------------------------------------------
  219. // A JavaObject accessor has common functionality shared by both the builders below (Java-> ECL and ECL->Java)
  220. class JavaObjectAccessor : public CInterface
  221. {
  222. protected:
  223. JavaObjectAccessor(JNIEnv *_JNIenv, const RtlFieldInfo *_outerRow, jobject _row)
  224. : JNIenv(_JNIenv), row(_row), outerRow(_outerRow), idx(0), limit(0), inSet(false), inDataSet(false)
  225. {
  226. Class = (jclass) JNIenv->NewGlobalRef(JNIenv->GetObjectClass(row));
  227. }
  228. JavaObjectAccessor(JNIEnv *_JNIenv, const RtlFieldInfo *_outerRow, jclass _Class)
  229. : JNIenv(_JNIenv), outerRow(_outerRow), idx(0), limit(0), inSet(false), inDataSet(false)
  230. {
  231. row = NULL;
  232. Class = (jclass) JNIenv->NewGlobalRef(_Class);
  233. }
  234. ~JavaObjectAccessor()
  235. {
  236. // Unwind anything left on the stack (in case we had exceptions), to make sure the Class we release is the global one
  237. if (stack.length())
  238. Class = (jclass) stack.item(0);
  239. if (Class)
  240. JNIenv->DeleteGlobalRef(Class);
  241. }
  242. void push()
  243. {
  244. stack.append(Class);
  245. stack.append(row);
  246. }
  247. void pop()
  248. {
  249. row = (jobject) stack.popGet();
  250. Class = (jclass) stack.popGet();
  251. }
  252. jfieldID getFieldId(const RtlFieldInfo * field, const char *sig, const char *expected)
  253. {
  254. // MORE - if we are going to stream a dataset we really should be caching these somehow
  255. JNIenv->ExceptionClear();
  256. jfieldID fieldId = 0;
  257. if (sig)
  258. {
  259. if (inSet)
  260. {
  261. VStringBuffer arraySig("[%s", sig);
  262. fieldId = JNIenv->GetFieldID(Class, field->name, arraySig.str());
  263. }
  264. else
  265. fieldId = JNIenv->GetFieldID(Class, field->name, sig);
  266. }
  267. else
  268. {
  269. // Do it the hard way via reflection API
  270. // Equivalent java:
  271. // Field field = object.getClass().getDeclaredField(fieldName);
  272. jclass classClass =JNIenv->GetObjectClass(Class);
  273. checkException();
  274. jmethodID getDeclaredField = JNIenv->GetMethodID(classClass, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;" );
  275. checkException();
  276. jstring fieldName = JNIenv->NewStringUTF(field->name);
  277. checkException();
  278. jobject reflectedField = JNIenv->CallObjectMethod(Class, getDeclaredField, fieldName);
  279. checkException();
  280. fieldId = JNIenv->FromReflectedField(reflectedField);
  281. }
  282. if (!fieldId && expected)
  283. throw MakeStringException(0, "javaembed: Unable to retrieve field %s of type %s", field->name, expected);
  284. if (expected)
  285. checkException();
  286. else
  287. JNIenv->ExceptionClear();
  288. return fieldId;
  289. }
  290. void checkException()
  291. {
  292. javaembed::checkException(JNIenv);
  293. }
  294. JNIEnv *JNIenv;
  295. jobject row;
  296. const RtlFieldInfo *outerRow;
  297. jclass Class;
  298. ConstPointerArray stack;
  299. unsigned idx;
  300. UnsignedArray idxStack;
  301. unsigned limit;
  302. bool inSet;
  303. bool inDataSet;
  304. };
  305. // A JavaRowBuilder object is used to construct an ECL row from a Java object
  306. class JavaRowBuilder : public JavaObjectAccessor, implements IFieldSource
  307. {
  308. public:
  309. IMPLEMENT_IINTERFACE;
  310. JavaRowBuilder(JNIEnv *_JNIenv, const RtlFieldInfo *_outerRow, jobject _row)
  311. : JavaObjectAccessor(_JNIenv, _outerRow, _row)
  312. {
  313. }
  314. virtual bool getBooleanResult(const RtlFieldInfo *field)
  315. {
  316. jboolean b;
  317. if (inSet)
  318. {
  319. JNIenv->GetBooleanArrayRegion((jbooleanArray) row, idx, 1, &b);
  320. }
  321. else
  322. {
  323. jfieldID fieldId = getFieldId(field, "Z", "boolean");
  324. b = JNIenv->GetBooleanField(row, fieldId);
  325. }
  326. checkException();
  327. return b;
  328. }
  329. virtual void getDataResult(const RtlFieldInfo *field, size32_t &__len, void * &__result)
  330. {
  331. jbyteArray array;
  332. if (inSet)
  333. {
  334. array = (jbyteArray) JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  335. }
  336. else
  337. {
  338. jfieldID fieldId = getFieldId(field, "[B", "DATA");
  339. array = (jbyteArray) JNIenv->GetObjectField(row, fieldId);
  340. }
  341. checkException();
  342. __len = (array != NULL ? JNIenv->GetArrayLength(array) : 0);
  343. __result = (__len > 0 ? rtlMalloc(__len) : NULL);
  344. if (__result)
  345. JNIenv->GetByteArrayRegion(array, 0, __len, (jbyte *) __result);
  346. checkException();
  347. }
  348. virtual double getRealResult(const RtlFieldInfo *field)
  349. {
  350. double d;
  351. if (inSet)
  352. {
  353. float f;
  354. switch (field->size(NULL, NULL))
  355. {
  356. case 4:
  357. JNIenv->GetFloatArrayRegion((jfloatArray) row, idx, 1, &f);
  358. d = f;
  359. break;
  360. case 8:
  361. JNIenv->GetDoubleArrayRegion((jdoubleArray) row, idx, 1, &d);
  362. break;
  363. default:
  364. throwUnexpected();
  365. }
  366. }
  367. else
  368. {
  369. jfieldID fieldId;
  370. switch (field->size(NULL, NULL))
  371. {
  372. case 4:
  373. fieldId = getFieldId(field, "F", "float");
  374. d = JNIenv->GetFloatField(row, fieldId);
  375. break;
  376. case 8:
  377. fieldId = getFieldId(field, "D", "double");
  378. d = JNIenv->GetDoubleField(row, fieldId);
  379. break;
  380. default:
  381. throwUnexpected();
  382. }
  383. }
  384. checkException();
  385. return d;
  386. }
  387. virtual __int64 getSignedResult(const RtlFieldInfo *field)
  388. {
  389. __int64 ret;
  390. if (inSet)
  391. {
  392. jbyte b;
  393. jshort s;
  394. jint i;
  395. jlong l;
  396. switch (field->size(NULL, NULL))
  397. {
  398. case 1:
  399. JNIenv->GetByteArrayRegion((jbyteArray) row, idx, 1, &b);
  400. ret = b;
  401. break;
  402. case 2:
  403. JNIenv->GetShortArrayRegion((jshortArray) row, idx, 1, &s);
  404. ret = s;
  405. break;
  406. case 4:
  407. JNIenv->GetIntArrayRegion((jintArray) row, idx, 1, &i);
  408. ret = i;
  409. break;
  410. case 8:
  411. JNIenv->GetLongArrayRegion((jlongArray) row, idx, 1, &l);
  412. ret = l;
  413. break;
  414. default:
  415. UNSUPPORTED("non-standard integer sizes");
  416. }
  417. }
  418. else
  419. {
  420. jfieldID fieldId;
  421. switch (field->size(NULL, NULL))
  422. {
  423. case 1:
  424. fieldId = getFieldId(field, "B", "byte");
  425. ret = JNIenv->GetByteField(row, fieldId);
  426. break;
  427. case 2:
  428. fieldId = getFieldId(field, "S", "short");
  429. ret = JNIenv->GetShortField(row, fieldId);
  430. break;
  431. case 4:
  432. fieldId = getFieldId(field, "I", "int");
  433. ret = JNIenv->GetIntField(row, fieldId);
  434. break;
  435. case 8:
  436. fieldId = getFieldId(field, "J", "long");
  437. ret = JNIenv->GetLongField(row, fieldId);
  438. break;
  439. default:
  440. UNSUPPORTED("non-standard integer sizes");
  441. }
  442. }
  443. checkException();
  444. return ret;
  445. }
  446. virtual unsigned __int64 getUnsignedResult(const RtlFieldInfo *field)
  447. {
  448. UNSUPPORTED("unsigned fields"); // No unsigned types in Java
  449. }
  450. virtual void getStringResult(const RtlFieldInfo *field, size32_t &__len, char * &__result)
  451. {
  452. jstring result;
  453. if (inSet)
  454. {
  455. // MORE - set of string1 mapping to Java array of char ? Not sure it's worth it.
  456. result = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  457. }
  458. else
  459. {
  460. if (field->isFixedSize() && field->size(NULL, NULL)==1)
  461. {
  462. // See if there's a char field
  463. jfieldID charFieldId = getFieldId(field, "C", NULL);
  464. if (charFieldId)
  465. {
  466. jchar resultChar = JNIenv->GetCharField(row, charFieldId);
  467. rtlUnicodeToStrX(__len, __result, 1, &resultChar);
  468. return;
  469. }
  470. }
  471. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  472. result = (jstring) JNIenv->GetObjectField(row, fieldId);
  473. }
  474. if (!result)
  475. {
  476. __len = 0;
  477. __result = NULL;
  478. return;
  479. }
  480. size_t size = JNIenv->GetStringUTFLength(result); // in bytes
  481. const char *text = JNIenv->GetStringUTFChars(result, NULL);
  482. size32_t chars = rtlUtf8Length(size, text);
  483. rtlUtf8ToStrX(__len, __result, chars, text);
  484. JNIenv->ReleaseStringUTFChars(result, text);
  485. JNIenv->DeleteLocalRef(result);
  486. }
  487. virtual void getUTF8Result(const RtlFieldInfo *field, size32_t &__len, char * &__result)
  488. {
  489. jstring result;
  490. if (inSet)
  491. {
  492. // MORE - set of string1 mapping to Java array of char ? Not sure it's worth it.
  493. result = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  494. }
  495. else
  496. {
  497. if (field->isFixedSize() && field->size(NULL, NULL)==1)
  498. {
  499. // See if there's a char field
  500. jfieldID charFieldId = getFieldId(field, "C", NULL);
  501. if (charFieldId)
  502. {
  503. jchar resultChar = JNIenv->GetCharField(row, charFieldId);
  504. rtlUnicodeToUtf8X(__len, __result, 1, &resultChar);
  505. return;
  506. }
  507. }
  508. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  509. result = (jstring) JNIenv->GetObjectField(row, fieldId);
  510. }
  511. if (!result)
  512. {
  513. __len = 0;
  514. __result = NULL;
  515. return;
  516. }
  517. size_t size = JNIenv->GetStringUTFLength(result); // in bytes
  518. const char *text = JNIenv->GetStringUTFChars(result, NULL);
  519. size32_t chars = rtlUtf8Length(size, text);
  520. rtlUtf8ToUtf8X(__len, __result, chars, text);
  521. JNIenv->ReleaseStringUTFChars(result, text);
  522. JNIenv->DeleteLocalRef(result);
  523. }
  524. virtual void getUnicodeResult(const RtlFieldInfo *field, size32_t &__len, UChar * &__result)
  525. {
  526. jstring result;
  527. if (inSet)
  528. {
  529. // MORE - set of string1 mapping to Java array of char ? Not sure it's worth it.
  530. result = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  531. }
  532. else
  533. {
  534. if (field->isFixedSize() && field->size(NULL, NULL)==1)
  535. {
  536. // See if there's a char field
  537. jfieldID charFieldId = getFieldId(field, "C", NULL);
  538. if (charFieldId)
  539. {
  540. jchar resultChar = JNIenv->GetCharField(row, charFieldId);
  541. rtlUnicodeToUnicodeX(__len, __result, 1, &resultChar);
  542. return;
  543. }
  544. }
  545. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  546. result = (jstring) JNIenv->GetObjectField(row, fieldId);
  547. }
  548. if (!result)
  549. {
  550. __len = 0;
  551. __result = NULL;
  552. return;
  553. }
  554. size_t size = JNIenv->GetStringUTFLength(result); // in bytes
  555. const char *text = JNIenv->GetStringUTFChars(result, NULL);
  556. size32_t chars = rtlUtf8Length(size, text);
  557. rtlUtf8ToUnicodeX(__len, __result, chars, text);
  558. JNIenv->ReleaseStringUTFChars(result, text);
  559. JNIenv->DeleteLocalRef(result);
  560. }
  561. virtual void getDecimalResult(const RtlFieldInfo *field, Decimal &value)
  562. {
  563. double ret = getRealResult(field);
  564. value.setReal(ret);
  565. }
  566. virtual void processBeginSet(const RtlFieldInfo * field, bool &isAll)
  567. {
  568. isAll = false; // No concept of an 'all' set in Java
  569. push();
  570. jfieldID fieldId = getFieldId(field, NULL, "object"); // We assume it will be an array, but not sure of what...
  571. row = JNIenv->GetObjectField(row, fieldId);
  572. inSet = true;
  573. idx = -1; // First call to next() increments it to 0
  574. limit = row != NULL ? JNIenv->GetArrayLength((jarray) row) : 0;
  575. checkException();
  576. }
  577. virtual bool processNextSet(const RtlFieldInfo * field)
  578. {
  579. assertex(inSet);
  580. idx++;
  581. return idx < limit;
  582. }
  583. virtual void processBeginDataset(const RtlFieldInfo * field)
  584. {
  585. push();
  586. jfieldID fieldId = getFieldId(field, NULL, "object"); // We assume it will be an array, but not sure of what...
  587. row = JNIenv->GetObjectField(row, fieldId);
  588. inDataSet = true;
  589. idx = -1; // First call to next() increments it to 0
  590. limit = row != NULL ? JNIenv->GetArrayLength((jarray) row) : 0;
  591. checkException();
  592. }
  593. virtual void processBeginRow(const RtlFieldInfo * field)
  594. {
  595. if (field != outerRow)
  596. {
  597. push();
  598. if (inDataSet)
  599. {
  600. row = JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  601. }
  602. else
  603. {
  604. jfieldID fieldId = getFieldId(field, NULL, "object");
  605. row = JNIenv->GetObjectField(row, fieldId);
  606. }
  607. if (!row)
  608. rtlFail(0, "javaembed: child dataset object should not be NULL");
  609. Class = JNIenv->GetObjectClass(row);
  610. }
  611. }
  612. virtual bool processNextRow(const RtlFieldInfo * field)
  613. {
  614. assertex(inDataSet);
  615. idx++;
  616. return idx < limit;
  617. }
  618. virtual void processEndSet(const RtlFieldInfo * field)
  619. {
  620. inSet = false;
  621. JNIenv->DeleteLocalRef(row);
  622. pop();
  623. }
  624. virtual void processEndDataset(const RtlFieldInfo * field)
  625. {
  626. inDataSet = false;
  627. JNIenv->DeleteLocalRef(row);
  628. pop();
  629. }
  630. virtual void processEndRow(const RtlFieldInfo * field)
  631. {
  632. if (field != outerRow)
  633. {
  634. JNIenv->DeleteLocalRef(row);
  635. JNIenv->DeleteLocalRef(Class);
  636. pop();
  637. }
  638. }
  639. };
  640. //-------------------------------------------
  641. // A JavaObjectBuilder object is used to construct a Java object from an ECL row
  642. class JavaObjectBuilder : public JavaObjectAccessor, implements IFieldProcessor
  643. {
  644. public:
  645. IMPLEMENT_IINTERFACE;
  646. JavaObjectBuilder(JNIEnv *_JNIenv, const RtlFieldInfo *_outerRow, jclass _Class)
  647. : JavaObjectAccessor(_JNIenv, _outerRow, _Class)
  648. {
  649. setConstructor();
  650. }
  651. virtual void processString(unsigned numchars, const char *text, const RtlFieldInfo * field)
  652. {
  653. if (field->isFixedSize() && field->size(NULL, NULL)==1 && !inSet) // SET OF STRING1 is not mapped to array of char...
  654. {
  655. // See if there's a char field
  656. jfieldID charFieldId = getFieldId(field, "C", NULL);
  657. if (charFieldId)
  658. {
  659. assertex(numchars==1);
  660. jchar c;
  661. rtlStrToUnicode(1, &c, 1, text);
  662. JNIenv->SetCharField(row, charFieldId, c);
  663. checkException();
  664. return;
  665. }
  666. }
  667. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  668. size32_t numchars16;
  669. rtlDataAttr unicode16;
  670. rtlStrToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
  671. jstring value = JNIenv->NewString(unicode16.getustr(), numchars16);
  672. checkException();
  673. if (inSet)
  674. JNIenv->SetObjectArrayElement((jobjectArray) row, idx, value);
  675. else
  676. JNIenv->SetObjectField(row, fieldId, value);
  677. JNIenv->DeleteLocalRef(value);
  678. checkException();
  679. }
  680. virtual void processBool(bool value, const RtlFieldInfo * field)
  681. {
  682. jfieldID fieldId = getFieldId(field, "Z", "boolean");
  683. JNIenv->SetBooleanField(row, fieldId, value);
  684. checkException();
  685. }
  686. virtual void processData(unsigned len, const void *value, const RtlFieldInfo * field)
  687. {
  688. jfieldID fieldId = getFieldId(field, "[B", "data");
  689. jbyteArray javaData = JNIenv->NewByteArray(len);
  690. JNIenv->SetByteArrayRegion(javaData, 0, len, (jbyte *) value);
  691. checkException();
  692. if (inSet)
  693. JNIenv->SetObjectArrayElement((jobjectArray) row, idx, javaData);
  694. else
  695. JNIenv->SetObjectField(row, fieldId, javaData);
  696. checkException();
  697. }
  698. virtual void processInt(__int64 value, const RtlFieldInfo * field)
  699. {
  700. jfieldID fieldId;
  701. switch (field->size(NULL, NULL))
  702. {
  703. case 1:
  704. fieldId = getFieldId(field, "B", "byte");
  705. JNIenv->SetByteField(row, fieldId, value);
  706. break;
  707. case 2:
  708. fieldId = getFieldId(field, "S", "short");
  709. JNIenv->SetShortField(row, fieldId, value);
  710. break;
  711. case 4:
  712. fieldId = getFieldId(field, "I", "int");
  713. JNIenv->SetIntField(row, fieldId, value);
  714. break;
  715. case 8:
  716. fieldId = getFieldId(field, "J", "long");
  717. JNIenv->SetLongField(row, fieldId, value);
  718. break;
  719. default:
  720. UNSUPPORTED("non-standard integer sizes");
  721. break;
  722. }
  723. checkException();
  724. }
  725. virtual void processUInt(unsigned __int64 value, const RtlFieldInfo * field)
  726. {
  727. UNSUPPORTED("unsigned fields"); // No unsigned types in Java
  728. }
  729. virtual void processReal(double value, const RtlFieldInfo * field)
  730. {
  731. jfieldID fieldId;
  732. switch (field->size(NULL, NULL))
  733. {
  734. case 4:
  735. fieldId = getFieldId(field, "F", "float");
  736. JNIenv->SetFloatField(row, fieldId, (float) value);
  737. break;
  738. case 8:
  739. fieldId = getFieldId(field, "D", "double");
  740. JNIenv->SetDoubleField(row, fieldId, value);
  741. break;
  742. default:
  743. throwUnexpected();
  744. }
  745. checkException();
  746. }
  747. virtual void processDecimal(const void *value, unsigned digits, unsigned precision, const RtlFieldInfo * field)
  748. {
  749. // we could map to doubles, but probably better to let the ECL programmer do that themselves
  750. UNSUPPORTED("DECIMAL fields");
  751. }
  752. virtual void processUDecimal(const void *value, unsigned digits, unsigned precision, const RtlFieldInfo * field)
  753. {
  754. UNSUPPORTED("UDECIMAL fields");
  755. }
  756. virtual void processUnicode(unsigned numchars, const UChar *text, const RtlFieldInfo * field)
  757. {
  758. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  759. jstring value = JNIenv->NewString(text, numchars);
  760. checkException();
  761. if (inSet)
  762. JNIenv->SetObjectArrayElement((jobjectArray) row, idx, value);
  763. else
  764. JNIenv->SetObjectField(row, fieldId, value);
  765. JNIenv->DeleteLocalRef(value);
  766. checkException();
  767. }
  768. virtual void processQString(unsigned len, const char *value, const RtlFieldInfo * field)
  769. {
  770. size32_t charCount;
  771. rtlDataAttr text;
  772. rtlQStrToStrX(charCount, text.refstr(), len, value);
  773. processString(charCount, text.getstr(), field);
  774. }
  775. virtual void processUtf8(unsigned numchars, const char *text, const RtlFieldInfo * field)
  776. {
  777. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  778. size32_t numchars16;
  779. rtlDataAttr unicode16;
  780. rtlUtf8ToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
  781. jstring value = JNIenv->NewString(unicode16.getustr(), numchars16);
  782. checkException();
  783. if (inSet)
  784. JNIenv->SetObjectArrayElement((jobjectArray) row, idx, value);
  785. else
  786. JNIenv->SetObjectField(row, fieldId, value);
  787. JNIenv->DeleteLocalRef(value);
  788. checkException();
  789. }
  790. virtual bool processBeginSet(const RtlFieldInfo * field, unsigned numElems, bool isAll, const byte *data)
  791. {
  792. push();
  793. idx = 0;
  794. limit = numElems;
  795. const char *javaTypeSignature = NULL;
  796. bool processElements = false;
  797. // row needs to be created as an array of <whatever>
  798. if (isAll)
  799. UNSUPPORTED("ALL sets");
  800. const RtlTypeInfo *childType = field->type->queryChildType();
  801. jobject newRow;
  802. switch(childType->fieldType & RFTMkind)
  803. {
  804. case type_boolean:
  805. newRow = JNIenv->NewBooleanArray(numElems);
  806. JNIenv->SetBooleanArrayRegion((jbooleanArray) newRow, 0, numElems, (jboolean *) data);
  807. javaTypeSignature = "[Z";
  808. break;
  809. case type_int:
  810. if (childType->fieldType & RFTMunsigned)
  811. UNSUPPORTED("unsigned integers");
  812. switch (childType->length)
  813. {
  814. case 1:
  815. newRow = JNIenv->NewByteArray(numElems);
  816. JNIenv->SetByteArrayRegion((jbyteArray) newRow, 0, numElems, (jbyte *) data);
  817. javaTypeSignature = "[B";
  818. break;
  819. case 2:
  820. newRow = JNIenv->NewShortArray(numElems);
  821. JNIenv->SetShortArrayRegion((jshortArray) newRow, 0, numElems, (jshort *) data);
  822. javaTypeSignature = "[S";
  823. break;
  824. case 4:
  825. newRow = JNIenv->NewIntArray(numElems);
  826. JNIenv->SetIntArrayRegion((jintArray) newRow, 0, numElems, (jint *) data);
  827. javaTypeSignature = "[I";
  828. break;
  829. case 8:
  830. newRow = JNIenv->NewLongArray(numElems);
  831. JNIenv->SetLongArrayRegion((jlongArray) newRow, 0, numElems, (jlong *) data);
  832. javaTypeSignature = "[J";
  833. break;
  834. default:
  835. UNSUPPORTED("non-standard integer sizes");
  836. break;
  837. }
  838. break;
  839. case type_real:
  840. switch (childType->length)
  841. {
  842. case 4:
  843. newRow = JNIenv->NewFloatArray(numElems);
  844. JNIenv->SetFloatArrayRegion((jfloatArray) newRow, 0, numElems, (float *) data);
  845. javaTypeSignature = "[F";
  846. break;
  847. case 8:
  848. newRow = JNIenv->NewDoubleArray(numElems);
  849. JNIenv->SetDoubleArrayRegion((jdoubleArray) newRow, 0, numElems, (double *) data);
  850. javaTypeSignature = "[D";
  851. break;
  852. default:
  853. throwUnexpected();
  854. break;
  855. }
  856. break;
  857. case type_string:
  858. case type_varstring:
  859. case type_unicode:
  860. case type_utf8:
  861. newRow = JNIenv->NewObjectArray(numElems, JNIenv->FindClass("java/lang/String"), NULL);
  862. javaTypeSignature = "[Ljava/lang/String;";
  863. processElements = true;
  864. break;
  865. case type_data:
  866. newRow = JNIenv->NewObjectArray(numElems, JNIenv->FindClass("[B"), NULL);
  867. javaTypeSignature = "[[B";
  868. processElements = true;
  869. break;
  870. default:
  871. throwUnexpected();
  872. }
  873. checkException();
  874. jfieldID fieldId = getFieldId(field, javaTypeSignature, "Array");
  875. JNIenv->SetObjectField(row, fieldId, newRow);
  876. row = newRow;
  877. inSet = true;
  878. return processElements;
  879. }
  880. virtual bool processBeginDataset(const RtlFieldInfo * field, unsigned numRows)
  881. {
  882. push();
  883. idxStack.append(idx);
  884. idx = 0;
  885. inDataSet = true;
  886. // Create an empty array
  887. jfieldID childId = getFieldId( field, NULL, "RECORD");
  888. jobject newRow = NULL;
  889. if (numRows)
  890. {
  891. jclass arrayClass = getClassForChild(childId);
  892. jmethodID isArrayMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(arrayClass), "isArray", "()Z" );
  893. checkException();
  894. if (!JNIenv->CallBooleanMethod(arrayClass, isArrayMethod))
  895. {
  896. JNIenv->ExceptionClear();
  897. VStringBuffer message("javaembed: Array expected for field %s", field->name);
  898. rtlFail(0, message.str());
  899. }
  900. // Set up constructor etc for the child rows, so we don't do it per row
  901. jmethodID getTypeMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(arrayClass), "getComponentType", "()Ljava/lang/Class;" );
  902. checkException();
  903. Class = (jclass) JNIenv->CallObjectMethod(arrayClass, getTypeMethod);
  904. checkException();
  905. setConstructor();
  906. // Now we need to create the array
  907. newRow = JNIenv->NewObjectArray(numRows, Class, NULL);
  908. checkException();
  909. }
  910. JNIenv->SetObjectField(row, childId, newRow);
  911. checkException();
  912. row = newRow;
  913. return true;
  914. }
  915. virtual bool processBeginRow(const RtlFieldInfo * field)
  916. {
  917. if (field == outerRow)
  918. row = JNIenv->NewObject(Class, constructor);
  919. else
  920. {
  921. push();
  922. stack.append(constructor);
  923. // Now we have to create the child object
  924. jobject newRow = NULL;
  925. if (inDataSet)
  926. {
  927. newRow = JNIenv->NewObject(Class, constructor);
  928. checkException();
  929. JNIenv->SetObjectArrayElement((jobjectArray) row, idx++, newRow);
  930. }
  931. else
  932. {
  933. // All this is done once per dataset in the nested dataset case. But for embedded record case we have to do it here
  934. jfieldID childId = getFieldId( field, NULL, "RECORD");
  935. Class = getClassForChild(childId);
  936. setConstructor();
  937. newRow = JNIenv->NewObject(Class, constructor);
  938. checkException();
  939. JNIenv->SetObjectField(row, childId, newRow);
  940. }
  941. row = newRow;
  942. }
  943. checkException();
  944. return true;
  945. }
  946. virtual void processEndSet(const RtlFieldInfo * field)
  947. {
  948. JNIenv->DeleteLocalRef(row);
  949. pop();
  950. inSet = false;
  951. }
  952. virtual void processEndDataset(const RtlFieldInfo * field)
  953. {
  954. inDataSet = false;
  955. idx = idxStack.popGet();
  956. pop();
  957. }
  958. virtual void processEndRow(const RtlFieldInfo * field)
  959. {
  960. if (field != outerRow)
  961. {
  962. constructor = (jmethodID) stack.popGet();
  963. JNIenv->DeleteLocalRef(row);
  964. pop();
  965. }
  966. }
  967. inline jobject getObject()
  968. {
  969. return row;
  970. }
  971. protected:
  972. jclass getClassForChild(jfieldID childId)
  973. {
  974. jobject reflectedField = JNIenv->ToReflectedField(Class, childId, false);
  975. checkException();
  976. jclass fieldClass =JNIenv->GetObjectClass(reflectedField);
  977. checkException();
  978. jmethodID getTypeMethod = JNIenv->GetMethodID(fieldClass, "getType", "()Ljava/lang/Class;" );
  979. checkException();
  980. jclass result = (jclass) JNIenv->CallObjectMethod(reflectedField, getTypeMethod);
  981. checkException();
  982. JNIenv->DeleteLocalRef(reflectedField);
  983. JNIenv->DeleteLocalRef(fieldClass);
  984. return result;
  985. }
  986. void setConstructor()
  987. {
  988. constructor = JNIenv->GetMethodID(Class, "<init>", "()V");
  989. if (!constructor)
  990. {
  991. JNIenv->ExceptionClear();
  992. jmethodID getNameMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(Class), "getName", "()Ljava/lang/String;" );
  993. checkException();
  994. jstring name = (jstring) JNIenv->CallObjectMethod(Class, getNameMethod);
  995. checkException();
  996. const char *nameText = JNIenv->GetStringUTFChars(name, NULL);
  997. VStringBuffer message("javaembed: no suitable constructor for class %s", nameText);
  998. JNIenv->ReleaseStringUTFChars(name, nameText);
  999. rtlFail(0, message.str());
  1000. }
  1001. }
  1002. jmethodID constructor;
  1003. };
  1004. //----------------------------------------------------------------------
  1005. // Wrap an IRowStream into a Java Iterator
  1006. class ECLDatasetIterator : public CInterfaceOf<IInterface>
  1007. {
  1008. public:
  1009. ECLDatasetIterator(JNIEnv *JNIenv, const RtlTypeInfo *_typeInfo, jclass className, IRowStream * _val)
  1010. : typeInfo(_typeInfo), val(_val),
  1011. dummyField("<row>", NULL, typeInfo),
  1012. javaBuilder(JNIenv, &dummyField, className)
  1013. {
  1014. nextRead = false;
  1015. nextPending = NULL;
  1016. }
  1017. bool hasNext()
  1018. {
  1019. if (!nextRead)
  1020. {
  1021. nextPending = (const byte *) val->ungroupedNextRow();
  1022. nextRead = true;
  1023. if (!nextPending)
  1024. val->stop();
  1025. }
  1026. return nextPending != NULL;
  1027. }
  1028. jobject next()
  1029. {
  1030. if (!hasNext())
  1031. return NULL;
  1032. typeInfo->process(nextPending, nextPending, &dummyField, javaBuilder); // Creates a java object from the incoming ECL row
  1033. nextRead = false;
  1034. return javaBuilder.getObject();
  1035. }
  1036. protected:
  1037. const RtlTypeInfo *typeInfo; // Not linked (or linkable)
  1038. Linked<IRowStream> val;
  1039. RtlFieldStrInfo dummyField;
  1040. JavaObjectBuilder javaBuilder;
  1041. const byte *nextPending;
  1042. bool nextRead;
  1043. };
  1044. //-------------------------------------------
  1045. // A Java function that returns a dataset will return a JavaRowStream object that can be
  1046. // interrogated to return each row of the result in turn
  1047. static JNIEnv *queryJNIEnv();
  1048. class JavaLocalFrame
  1049. {
  1050. public:
  1051. JavaLocalFrame(JNIEnv *_JNIenv, unsigned size = 16) : JNIenv(_JNIenv)
  1052. {
  1053. JNIenv->PushLocalFrame(size);
  1054. }
  1055. ~JavaLocalFrame()
  1056. {
  1057. JNIenv->PopLocalFrame(NULL);
  1058. }
  1059. private:
  1060. JNIEnv *JNIenv;
  1061. };
  1062. class JavaRowStream : public CInterfaceOf<IRowStream>
  1063. {
  1064. public:
  1065. JavaRowStream(jobject _iterator, IEngineRowAllocator *_resultAllocator)
  1066. : resultAllocator(_resultAllocator)
  1067. {
  1068. JNIEnv *JNIenv = queryJNIEnv();
  1069. iterator = JNIenv->NewGlobalRef(_iterator);
  1070. // Note that we can't cache the JNIEnv, iterClass, or methodIds here - calls may be made on different threads (though not at the same time).
  1071. }
  1072. ~JavaRowStream()
  1073. {
  1074. stop();
  1075. }
  1076. virtual const void *nextRow()
  1077. {
  1078. if (!iterator)
  1079. return NULL;
  1080. JNIEnv *JNIenv = queryJNIEnv();
  1081. JavaLocalFrame lf(JNIenv);
  1082. // Java code would be
  1083. // if (!iterator.hasNext)
  1084. // {
  1085. // stop();
  1086. // return NULL;
  1087. // }
  1088. // result = iterator.next();
  1089. jclass iterClass =JNIenv->GetObjectClass(iterator);
  1090. javaembed::checkException(JNIenv);
  1091. jmethodID hasNextMethod = JNIenv->GetMethodID(iterClass, "hasNext", "()Z" );
  1092. javaembed::checkException(JNIenv);
  1093. jboolean hasNext = JNIenv->CallBooleanMethod(iterator, hasNextMethod);
  1094. if (!hasNext)
  1095. {
  1096. stop();
  1097. return NULL;
  1098. }
  1099. jmethodID nextMethod = JNIenv->GetMethodID(iterClass, "next", "()Ljava/lang/Object;" );
  1100. javaembed::checkException(JNIenv);
  1101. jobject result = JNIenv->CallObjectMethod(iterator, nextMethod);
  1102. RtlDynamicRowBuilder rowBuilder(resultAllocator);
  1103. const RtlTypeInfo *typeInfo = resultAllocator->queryOutputMeta()->queryTypeInfo();
  1104. assertex(typeInfo);
  1105. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  1106. JavaRowBuilder javaRowBuilder(queryJNIEnv(), &dummyField, result);
  1107. size32_t len = typeInfo->build(rowBuilder, 0, &dummyField, javaRowBuilder);
  1108. return rowBuilder.finalizeRowClear(len);
  1109. }
  1110. virtual void stop()
  1111. {
  1112. resultAllocator.clear();
  1113. JNIEnv *JNIenv = queryJNIEnv();
  1114. if (JNIenv)
  1115. {
  1116. if (iterator)
  1117. {
  1118. JNIenv->DeleteGlobalRef(iterator);
  1119. iterator = NULL;
  1120. }
  1121. }
  1122. }
  1123. protected:
  1124. Linked<IEngineRowAllocator> resultAllocator;
  1125. jobject iterator;
  1126. };
  1127. const char *esdl2JavaSig(IEsdlDefinition &esdl, const char *esdlType)
  1128. {
  1129. EsdlBasicElementType t = esdl.translateSimpleType(esdlType);
  1130. switch (t)
  1131. {
  1132. case ESDLT_INT16:
  1133. case ESDLT_UINT16:
  1134. return "Ljava/lang/Short;";
  1135. case ESDLT_INT32:
  1136. case ESDLT_UINT32:
  1137. return "Ljava/lang/Integer;";
  1138. case ESDLT_INT64:
  1139. case ESDLT_UINT64:
  1140. return "Ljava/math/BigInteger;";
  1141. case ESDLT_BOOL:
  1142. return "Ljava/lang/Boolean;";
  1143. case ESDLT_FLOAT:
  1144. return "Ljava/lang/Float;";
  1145. case ESDLT_DOUBLE:
  1146. return "Ljava/lang/Double;";
  1147. case ESDLT_INT8:
  1148. case ESDLT_UINT8:
  1149. case ESDLT_BYTE:
  1150. case ESDLT_UBYTE:
  1151. return "Ljava/lang/Byte;";
  1152. case ESDLT_STRING:
  1153. return "Ljava/lang/String;";
  1154. case ESDLT_UNKOWN:
  1155. case ESDLT_STRUCT:
  1156. case ESDLT_REQUEST:
  1157. case ESDLT_RESPONSE:
  1158. case ESDLT_COMPLEX:
  1159. default:
  1160. return NULL;
  1161. }
  1162. }
  1163. const char *esdl2JavaFullClassName(IEsdlDefinition &esdl, const char *esdlType)
  1164. {
  1165. EsdlBasicElementType t = esdl.translateSimpleType(esdlType);
  1166. switch (t)
  1167. {
  1168. case ESDLT_INT16:
  1169. case ESDLT_UINT16:
  1170. return "java/lang/Short";
  1171. case ESDLT_INT32:
  1172. case ESDLT_UINT32:
  1173. return "java/lang/Integer";
  1174. case ESDLT_INT64:
  1175. case ESDLT_UINT64:
  1176. return "java/math/BigInteger";
  1177. case ESDLT_BOOL:
  1178. return "java/lang/Boolean";
  1179. case ESDLT_FLOAT:
  1180. return "java/lang/Float";
  1181. case ESDLT_DOUBLE:
  1182. return "java/lang/Double";
  1183. case ESDLT_INT8:
  1184. case ESDLT_UINT8:
  1185. case ESDLT_BYTE:
  1186. case ESDLT_UBYTE:
  1187. return "java/lang/Byte";
  1188. case ESDLT_STRING:
  1189. return "java/lang/String";
  1190. case ESDLT_UNKOWN:
  1191. case ESDLT_STRUCT:
  1192. case ESDLT_REQUEST:
  1193. case ESDLT_RESPONSE:
  1194. case ESDLT_COMPLEX:
  1195. default:
  1196. return NULL;
  1197. }
  1198. }
  1199. class JavaObjectXmlWriter : public CInterface
  1200. {
  1201. public:
  1202. JavaObjectXmlWriter(JNIEnv *_JNIenv, jobject _obj, const char *_reqType, IEsdlDefinition &_esdl, const char *_esdlService, IXmlWriter &_writer)
  1203. : JNIenv(_JNIenv), obj(_obj), writer(_writer), esdl(_esdl), esdlService(_esdlService), reqType(_reqType)
  1204. {
  1205. Class = (jclass) JNIenv->NewGlobalRef(JNIenv->GetObjectClass(obj));
  1206. langObjectClass = FindClass("java/lang/Object");
  1207. objToString = JNIenv->GetMethodID(langObjectClass, "toString", "()Ljava/lang/String;");
  1208. }
  1209. ~JavaObjectXmlWriter()
  1210. {
  1211. if (Class)
  1212. JNIenv->DeleteGlobalRef(Class);
  1213. HashIterator it(javaClasses);
  1214. ForEach(it)
  1215. {
  1216. IMapping &entry = it.query();
  1217. jclass *pClass = javaClasses.mapToValue(&entry);
  1218. if (pClass)
  1219. JNIenv->DeleteGlobalRef(*pClass);
  1220. }
  1221. }
  1222. void writeSimpleType(const char *fieldname, jobject fieldObj)
  1223. {
  1224. jstring fieldStr = (jstring) JNIenv->CallObjectMethod(fieldObj, objToString);
  1225. if (!fieldStr)
  1226. return;
  1227. const char *text = JNIenv->GetStringUTFChars(fieldStr, NULL);
  1228. if (text)
  1229. writer.outputCString(text, fieldname);
  1230. JNIenv->DeleteLocalRef(fieldStr);
  1231. }
  1232. void writeSimpleType(jclass parentClass, jobject parentObject, const char *fieldname, const char *javaSig)
  1233. {
  1234. if (!fieldname || !*fieldname)
  1235. return;
  1236. if (!javaSig || !*javaSig)
  1237. return;
  1238. jfieldID fieldId = JNIenv->GetFieldID(parentClass, fieldname, javaSig);
  1239. if (!fieldId)
  1240. return;
  1241. jobject fieldObj = (jobject) JNIenv->GetObjectField(parentObject, fieldId);
  1242. if (!fieldObj)
  1243. return;
  1244. writeSimpleType(fieldname, fieldObj);
  1245. JNIenv->DeleteLocalRef(fieldObj);
  1246. }
  1247. void writeSimpleType(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
  1248. {
  1249. const char *fieldname = defObject.queryName();
  1250. const char *javaSig = esdl2JavaSig(esdl, defObject.queryProp("type"));
  1251. writeSimpleType(parentClass, parentObject, fieldname, javaSig);
  1252. }
  1253. void writeEnumType(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
  1254. {
  1255. const char *fieldname = defObject.queryName();
  1256. VStringBuffer javaSig("L%s/%s;", esdlService.str(), defObject.queryProp("enum_type"));
  1257. jfieldID fieldId = JNIenv->GetFieldID(parentClass, fieldname, javaSig);
  1258. if (!fieldId)
  1259. return;
  1260. jobject fieldObj = (jobject) JNIenv->GetObjectField(parentObject, fieldId);
  1261. if (!fieldObj)
  1262. return;
  1263. jstring fieldStr = (jstring) JNIenv->CallObjectMethod(fieldObj, objToString);
  1264. const char *text = JNIenv->GetStringUTFChars(fieldStr, NULL);
  1265. if (!text)
  1266. return;
  1267. writer.outputCString(text, defObject.queryName());
  1268. }
  1269. void writeComplexType(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
  1270. {
  1271. IEsdlDefStruct *defStruct = esdl.queryStruct(defObject.queryProp("complex_type"));
  1272. if (!defStruct)
  1273. return;
  1274. const char *fieldname = defObject.queryName();
  1275. VStringBuffer javaSig("L%s/%s;", esdlService.str(), defObject.queryProp("complex_type"));
  1276. jfieldID fieldId = JNIenv->GetFieldID(parentClass, fieldname, javaSig); //tbd cache this
  1277. if (!fieldId)
  1278. return;
  1279. jobject fieldObj = (jobject) JNIenv->GetObjectField(parentObject, fieldId);
  1280. if (!fieldObj)
  1281. return;
  1282. writer.outputBeginNested(fieldname, true);
  1283. writeChildren(JNIenv->GetObjectClass(fieldObj), fieldObj, defStruct);
  1284. writer.outputEndNested(fieldname);
  1285. }
  1286. void writeSimpleArray(jobjectArray arrayObj, jint count, const char *name, const char *item_tag)
  1287. {
  1288. writer.outputBeginNested(name, true);
  1289. writer.outputBeginArray(item_tag);
  1290. for (jint i=0; i < count; i++)
  1291. {
  1292. jobject elementObj = JNIenv->GetObjectArrayElement(arrayObj, i);
  1293. if(JNIenv->ExceptionOccurred())
  1294. break;
  1295. writeSimpleType(item_tag, elementObj);
  1296. JNIenv->DeleteLocalRef(elementObj);
  1297. }
  1298. writer.outputEndArray(item_tag);
  1299. writer.outputEndNested(name);
  1300. }
  1301. void writeComplexArray(jobjectArray arrayObj, jint count, const char *name, const char *item_tag, const char *itemTypeName)
  1302. {
  1303. writer.outputBeginNested(name, true);
  1304. writer.outputBeginArray(item_tag);
  1305. {
  1306. VStringBuffer javaClassName("%s/%s", esdlService.str(), itemTypeName);
  1307. jclass elementClass = FindClass(javaClassName);
  1308. if (!elementClass)
  1309. return;
  1310. IEsdlDefStruct *defStruct = esdl.queryStruct(itemTypeName);
  1311. if (!defStruct)
  1312. return;
  1313. for (jint i=0; i < count; i++)
  1314. {
  1315. jobject elementObj = JNIenv->GetObjectArrayElement(arrayObj, i);
  1316. javaembed::checkException(JNIenv, false);
  1317. writer.outputBeginNested(item_tag, true);
  1318. writeChildren(elementClass, elementObj, defStruct);
  1319. writer.outputEndNested(item_tag);
  1320. JNIenv->DeleteLocalRef(elementObj);
  1321. }
  1322. }
  1323. writer.outputEndArray(item_tag);
  1324. writer.outputEndNested(name);
  1325. }
  1326. void writeArray(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
  1327. {
  1328. const char *itemTypeName = defObject.queryProp("type");
  1329. if (!itemTypeName)
  1330. return;
  1331. const char *item_tag = defObject.queryProp("item_tag");
  1332. if (!item_tag)
  1333. return;
  1334. const char *fieldname = defObject.queryName();
  1335. jclass arrayListClass = FindClass("java/util/ArrayList");
  1336. if (!arrayListClass)
  1337. return;
  1338. jmethodID toArrayMethod = JNIenv->GetMethodID(arrayListClass, "toArray", "()[Ljava/lang/Object;" );
  1339. if (!toArrayMethod)
  1340. return;
  1341. jfieldID fieldId = JNIenv->GetFieldID(parentClass, fieldname, "Ljava/util/ArrayList;");
  1342. if (!fieldId)
  1343. return;
  1344. jobject arrayListObj = (jobject) JNIenv->GetObjectField(parentObject, fieldId);
  1345. if (!arrayListObj)
  1346. return;
  1347. javaembed::checkException(JNIenv, false);
  1348. jobjectArray arrayObj = (jobjectArray) JNIenv->CallObjectMethod(arrayListObj, toArrayMethod);
  1349. if (arrayObj)
  1350. {
  1351. jint count = JNIenv->GetArrayLength(arrayObj);
  1352. if (count)
  1353. {
  1354. if (esdl2JavaSig(esdl, itemTypeName))
  1355. writeSimpleArray(arrayObj, count, defObject.queryName(), item_tag);
  1356. else
  1357. writeComplexArray(arrayObj, count, defObject.queryName(), item_tag, itemTypeName);
  1358. }
  1359. JNIenv->DeleteLocalRef(arrayObj);
  1360. }
  1361. JNIenv->DeleteLocalRef(arrayListObj);
  1362. }
  1363. void writeChildren(jclass javaClass, jobject javaObject, IEsdlDefStruct *defStruct)
  1364. {
  1365. Owned<IEsdlDefObjectIterator> children = defStruct->getChildren();
  1366. ForEach (*children)
  1367. {
  1368. IEsdlDefObject &child = children->query();
  1369. if (child.getEsdlType()==EsdlTypeElement)
  1370. {
  1371. if (child.hasProp("type"))
  1372. writeSimpleType(javaClass, javaObject, child);
  1373. else if (child.hasProp("complex_type"))
  1374. writeComplexType(javaClass, javaObject, child);
  1375. }
  1376. else if (child.getEsdlType()==EsdlTypeEnumRef)
  1377. {
  1378. writeEnumType(javaClass, javaObject, child);
  1379. }
  1380. else if (child.getEsdlType()==EsdlTypeArray)
  1381. {
  1382. writeArray(javaClass, javaObject, child);
  1383. }
  1384. }
  1385. }
  1386. void write()
  1387. {
  1388. IEsdlDefStruct *reqStruct = esdl.queryStruct(reqType);
  1389. const char *name = reqStruct->queryName();
  1390. writer.outputBeginNested("Response", true);
  1391. writer.outputBeginNested("Results", true);
  1392. writer.outputBeginNested("Result", true);
  1393. writer.outputBeginDataset(name, true);
  1394. writer.outputBeginArray("Row");
  1395. writer.outputBeginNested("Row", true);
  1396. writeChildren(Class, obj, reqStruct);
  1397. writer.outputEndNested("Row");
  1398. writer.outputEndArray("Row");
  1399. writer.outputEndDataset(name);
  1400. writer.outputEndNested("Result");
  1401. writer.outputEndNested("Results");
  1402. writer.outputEndNested("Response");
  1403. }
  1404. void checkException()
  1405. {
  1406. javaembed::checkException(JNIenv, false);
  1407. }
  1408. jclass FindClass(const char *name)
  1409. {
  1410. jclass *pClass = javaClasses.getValue(name);
  1411. if (pClass)
  1412. return *pClass;
  1413. jclass localClass = JNIenv->FindClass(name);
  1414. if (!localClass)
  1415. return 0;
  1416. jclass Class = (jclass) JNIenv->NewGlobalRef(localClass);
  1417. javaClasses.setValue(name, Class);
  1418. JNIenv->DeleteLocalRef(localClass);
  1419. return Class;
  1420. }
  1421. JNIEnv *JNIenv;
  1422. MapStringTo<jclass> javaClasses;
  1423. jclass Class;
  1424. jobject obj;
  1425. jclass langObjectClass;
  1426. jmethodID objToString;
  1427. IXmlWriter &writer;
  1428. IEsdlDefinition &esdl;
  1429. StringAttr reqType;
  1430. StringAttr esdlService;
  1431. };
  1432. //-------------------------------------------
  1433. // There is a singleton JavaThreadContext per thread. This allows us to
  1434. // ensure that we can make repeated calls to a Java function efficiently.
  1435. class JavaThreadContext
  1436. {
  1437. public:
  1438. JNIEnv *JNIenv; /* receives pointer to native method interface */
  1439. public:
  1440. JavaThreadContext()
  1441. {
  1442. jint res = globalState->javaVM->AttachCurrentThread((void **) &JNIenv, NULL);
  1443. assertex(res >= 0);
  1444. javaClass = NULL;
  1445. javaMethodID = NULL;
  1446. prevClassPath.set("dummy"); // Forces the call below to actually do something...
  1447. setThreadClassLoader("", 0, nullptr);
  1448. }
  1449. ~JavaThreadContext()
  1450. {
  1451. if (javaClass)
  1452. JNIenv->DeleteGlobalRef(javaClass);
  1453. // According to the Java VM 1.7 docs, "A native thread attached to
  1454. // the VM must call DetachCurrentThread() to detach itself before
  1455. // exiting."
  1456. globalState->javaVM->DetachCurrentThread();
  1457. }
  1458. void checkException()
  1459. {
  1460. if (JNIenv->ExceptionCheck())
  1461. {
  1462. jthrowable exception = JNIenv->ExceptionOccurred();
  1463. JNIenv->ExceptionClear();
  1464. jclass throwableClass = JNIenv->FindClass("java/lang/Throwable");
  1465. jmethodID throwableToString = JNIenv->GetMethodID(throwableClass, "toString", "()Ljava/lang/String;");
  1466. jstring cause = (jstring) JNIenv->CallObjectMethod(exception, throwableToString);
  1467. const char *text = JNIenv->GetStringUTFChars(cause, 0);
  1468. VStringBuffer message("javaembed: In method %s: %s", prevtext.get(), text);
  1469. JNIenv->ReleaseStringUTFChars(cause, text);
  1470. rtlFail(0, message.str());
  1471. }
  1472. }
  1473. bool checkException(StringBuffer &message)
  1474. {
  1475. if (JNIenv->ExceptionCheck())
  1476. {
  1477. jthrowable exception = JNIenv->ExceptionOccurred();
  1478. JNIenv->ExceptionClear();
  1479. jclass throwableClass = JNIenv->FindClass("java/lang/Throwable");
  1480. jmethodID throwableToString = JNIenv->GetMethodID(throwableClass, "toString", "()Ljava/lang/String;");
  1481. jstring cause = (jstring) JNIenv->CallObjectMethod(exception, throwableToString);
  1482. const char *text = JNIenv->GetStringUTFChars(cause, 0);
  1483. message.append(text);
  1484. JNIenv->ReleaseStringUTFChars(cause, text);
  1485. return true;
  1486. }
  1487. return false;
  1488. }
  1489. jobject getSystemClassLoader()
  1490. {
  1491. JNIenv->ExceptionClear();
  1492. jclass javaLangClassLoaderClass = JNIenv->FindClass("java/lang/ClassLoader");
  1493. checkException();
  1494. jmethodID getSystemClassLoaderMethod = JNIenv->GetStaticMethodID(javaLangClassLoaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
  1495. checkException();
  1496. jobject systemClassLoaderObj = JNIenv->CallStaticObjectMethod(javaLangClassLoaderClass, getSystemClassLoaderMethod);
  1497. checkException();
  1498. assertex(systemClassLoaderObj);
  1499. return systemClassLoaderObj;
  1500. }
  1501. void setThreadClassLoader(jobject classLoader, size32_t bytecodeLen, const byte *bytecode)
  1502. {
  1503. JNIenv->ExceptionClear();
  1504. jclass javaLangThreadClass = JNIenv->FindClass("java/lang/Thread");
  1505. checkException();
  1506. jmethodID currentThreadMethod = JNIenv->GetStaticMethodID(javaLangThreadClass, "currentThread", "()Ljava/lang/Thread;");
  1507. checkException();
  1508. jobject threadObj = JNIenv->CallStaticObjectMethod(javaLangThreadClass, currentThreadMethod);
  1509. checkException();
  1510. jmethodID setContextClassLoaderMethod = JNIenv->GetMethodID(javaLangThreadClass, "setContextClassLoader", "(Ljava/lang/ClassLoader;)V");
  1511. checkException();
  1512. JNIenv->CallObjectMethod(threadObj, setContextClassLoaderMethod, classLoader);
  1513. checkException();
  1514. }
  1515. jobject getThreadClassLoader()
  1516. {
  1517. JNIenv->ExceptionClear();
  1518. jclass javaLangThreadClass = JNIenv->FindClass("java/lang/Thread");
  1519. checkException();
  1520. jmethodID currentThreadMethod = JNIenv->GetStaticMethodID(javaLangThreadClass, "currentThread", "()Ljava/lang/Thread;");
  1521. checkException();
  1522. jobject threadObj = JNIenv->CallStaticObjectMethod(javaLangThreadClass, currentThreadMethod);
  1523. checkException();
  1524. jmethodID getContextClassLoaderMethod = JNIenv->GetMethodID(javaLangThreadClass, "getContextClassLoader", "()Ljava/lang/ClassLoader;");
  1525. checkException();
  1526. jobject contextClassLoaderObj = JNIenv->CallObjectMethod(threadObj, getContextClassLoaderMethod);
  1527. checkException();
  1528. assertex(contextClassLoaderObj);
  1529. return contextClassLoaderObj;
  1530. }
  1531. void setThreadClassLoader(const char *classPath, size32_t bytecodeLen, const byte *bytecode)
  1532. {
  1533. if (bytecodeLen || (classPath && *classPath))
  1534. {
  1535. if (!bytecodeLen && prevClassPath && classPath && strcmp(classPath, prevClassPath) == 0) // MORE - caching of inline classes is important too...
  1536. return;
  1537. jclass URLcls = JNIenv->FindClass("java/net/URL");
  1538. checkException();
  1539. jmethodID URLclsMid = JNIenv->GetMethodID(URLcls, "<init>","(Ljava/lang/String;)V");
  1540. checkException();
  1541. StringArray paths;
  1542. paths.appendList(classPath, ";"); // NOTE - as we need to be able to include : in the urls, we can't use ENVSEP here
  1543. jobjectArray URLArray = JNIenv->NewObjectArray(paths.length(), URLcls, NULL);
  1544. ForEachItemIn(idx, paths)
  1545. {
  1546. StringBuffer usepath;
  1547. const char *path = paths.item(idx);
  1548. if (!strchr(path, ':'))
  1549. usepath.append("file:");
  1550. usepath.append(path);
  1551. jstring jstr = JNIenv->NewStringUTF(usepath.str());
  1552. checkException();
  1553. jobject URLobj = JNIenv->NewObject(URLcls, URLclsMid, jstr);
  1554. checkException();
  1555. JNIenv->SetObjectArrayElement(URLArray, idx, URLobj);
  1556. JNIenv->DeleteLocalRef(URLobj);
  1557. JNIenv->DeleteLocalRef(jstr);
  1558. }
  1559. checkException();
  1560. jclass customLoaderClass = JNIenv->FindClass("com/HPCCSystems/HpccClassLoader");
  1561. checkException();
  1562. jmethodID newInstance = JNIenv->GetStaticMethodID(customLoaderClass, "newInstance","([Ljava/net/URL;Ljava/lang/ClassLoader;IJLjava/lang/String;)Lcom/HPCCSystems/HpccClassLoader;");
  1563. checkException();
  1564. jobject contextClassLoaderObj = JNIenv->NewGlobalRef(JNIenv->CallStaticObjectMethod(customLoaderClass, newInstance, URLArray, getSystemClassLoader(), bytecodeLen, (uint64_t) bytecode, JNIenv->NewStringUTF(helperLibraryName)));
  1565. checkException();
  1566. assertex(contextClassLoaderObj);
  1567. setThreadClassLoader(contextClassLoaderObj, bytecodeLen, bytecode);
  1568. prevClassPath.set(classPath);
  1569. }
  1570. else
  1571. {
  1572. if (prevClassPath)
  1573. setThreadClassLoader(getSystemClassLoader(), 0, nullptr);
  1574. prevClassPath.clear();
  1575. }
  1576. }
  1577. jclass loadClass(const char *className)
  1578. {
  1579. JNIenv->ExceptionClear();
  1580. jobject classLoader = getThreadClassLoader();
  1581. jmethodID loadClassMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(classLoader), "loadClass","(Ljava/lang/String;)Ljava/lang/Class;");
  1582. jstring classNameString = JNIenv->NewStringUTF(className);
  1583. jclass Class = (jclass) JNIenv->CallObjectMethod(classLoader, loadClassMethod, classNameString);
  1584. checkException();
  1585. return Class;
  1586. }
  1587. inline void importFunction(size32_t lenChars, const char *utf, const char *classpath, size32_t bytecodeLen, const byte *bytecode, jobject instance)
  1588. {
  1589. size32_t bytes = rtlUtf8Size(lenChars, utf);
  1590. StringBuffer text(bytes, utf);
  1591. setThreadClassLoader(classpath, bytecodeLen, bytecode);
  1592. if (!prevtext || strcmp(text, prevtext) != 0)
  1593. {
  1594. prevtext.clear();
  1595. // Name should be in the form class.method:signature
  1596. const char *funcname = strrchr(text, '.');
  1597. if (!funcname)
  1598. throw MakeStringException(MSGAUD_user, 0, "javaembed: Invalid import name %s - Expected classname.methodname:signature", text.str());
  1599. const char *signature = strchr(funcname, ':');
  1600. if (!signature)
  1601. throw MakeStringException(MSGAUD_user, 0, "javaembed: Invalid import name %s - Expected classname.methodname:signature", text.str());
  1602. StringBuffer classname(funcname-text, text);
  1603. // While it's probably preferred for people to use . as the separator in nested classes (to match java import statement),
  1604. // we accept / too (to match what you would see in the jar)
  1605. classname.replace('/', '.');
  1606. funcname++; // skip the '.'
  1607. StringBuffer methodname(signature-funcname, funcname);
  1608. signature++; // skip the ':'
  1609. // We need to patch up the provided signature - any instances of <classname; need to be replaced by Ljava.utils.iterator
  1610. StringBuffer javaSignature;
  1611. const char *finger = signature;
  1612. while (*finger)
  1613. {
  1614. if (*finger == '<')
  1615. {
  1616. // If there is a corresponding >, assume it's the 'extended' form and just strip out the bit from < to >
  1617. const char *close = strchr(finger, '>');
  1618. if (close)
  1619. finger = close;
  1620. else
  1621. {
  1622. javaSignature.append("Ljava/util/Iterator;");
  1623. finger = strchr(finger, ';');
  1624. if (!finger)
  1625. throw MakeStringException(MSGAUD_user, 0, "javaembed: Invalid java function signature %s", signature);
  1626. }
  1627. }
  1628. else
  1629. javaSignature.append(*finger);
  1630. finger++;
  1631. }
  1632. if (javaClass)
  1633. JNIenv->DeleteGlobalRef(javaClass);
  1634. jobject classLoader = getThreadClassLoader();
  1635. jmethodID loadClassMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(classLoader), "loadClass","(Ljava/lang/String;)Ljava/lang/Class;");
  1636. jstring methodString = JNIenv->NewStringUTF(classname);
  1637. javaClass = (jclass) JNIenv->CallObjectMethod(classLoader, loadClassMethod, methodString);
  1638. StringBuffer message;
  1639. if (checkException(message) || !javaClass)
  1640. throw MakeStringException(MSGAUD_user, 0, "javaembed: Failed to resolve class name %s: %s", classname.str(), message.str());
  1641. javaClass = (jclass) JNIenv->NewGlobalRef(javaClass);
  1642. if (instance)
  1643. javaMethodID = JNIenv->GetMethodID(javaClass, methodname, javaSignature);
  1644. else
  1645. javaMethodID = JNIenv->GetStaticMethodID(javaClass, methodname, javaSignature);
  1646. if (checkException(message) || !javaMethodID)
  1647. throw MakeStringException(MSGAUD_user, 0, "javaembed: Failed to resolve method name %s with signature %s: %s", methodname.str(), signature, message.str());
  1648. const char *returnSig = strrchr(signature, ')');
  1649. assertex(returnSig); // Otherwise how did Java accept it??
  1650. returnSig++;
  1651. returnType.set(returnSig);
  1652. argsig.set(signature);
  1653. prevtext.set(text);
  1654. }
  1655. }
  1656. inline void callFunction(jvalue &result, const jvalue * args, jobject instance)
  1657. {
  1658. JNIenv->ExceptionClear();
  1659. switch (returnType.get()[0])
  1660. {
  1661. case 'C': result.c = JNIenv->CallCharMethodA(instance, javaMethodID, args); break;
  1662. case 'Z': result.z = JNIenv->CallBooleanMethodA(instance, javaMethodID, args); break;
  1663. case 'J': result.j = JNIenv->CallLongMethodA(instance, javaMethodID, args); break;
  1664. case 'F': result.f = JNIenv->CallFloatMethodA(instance, javaMethodID, args); break;
  1665. case 'D': result.d = JNIenv->CallDoubleMethodA(instance, javaMethodID, args); break;
  1666. case 'I': result.i = JNIenv->CallIntMethodA(instance, javaMethodID, args); break;
  1667. case 'S': result.s = JNIenv->CallShortMethodA(instance, javaMethodID, args); break;
  1668. case 'B': result.s = JNIenv->CallByteMethodA(instance, javaMethodID, args); break;
  1669. case '[':
  1670. case 'L': result.l = JNIenv->CallObjectMethodA(instance, javaMethodID, args); break;
  1671. default: throwUnexpected();
  1672. }
  1673. checkException();
  1674. }
  1675. inline void callFunction(jvalue &result, const jvalue * args)
  1676. {
  1677. JNIenv->ExceptionClear();
  1678. switch (returnType.get()[0])
  1679. {
  1680. case 'C': result.c = JNIenv->CallStaticCharMethodA(javaClass, javaMethodID, args); break;
  1681. case 'Z': result.z = JNIenv->CallStaticBooleanMethodA(javaClass, javaMethodID, args); break;
  1682. case 'J': result.j = JNIenv->CallStaticLongMethodA(javaClass, javaMethodID, args); break;
  1683. case 'F': result.f = JNIenv->CallStaticFloatMethodA(javaClass, javaMethodID, args); break;
  1684. case 'D': result.d = JNIenv->CallStaticDoubleMethodA(javaClass, javaMethodID, args); break;
  1685. case 'I': result.i = JNIenv->CallStaticIntMethodA(javaClass, javaMethodID, args); break;
  1686. case 'S': result.s = JNIenv->CallStaticShortMethodA(javaClass, javaMethodID, args); break;
  1687. case 'B': result.s = JNIenv->CallStaticByteMethodA(javaClass, javaMethodID, args); break;
  1688. case '[':
  1689. case 'L': result.l = JNIenv->CallStaticObjectMethodA(javaClass, javaMethodID, args); break;
  1690. default: throwUnexpected();
  1691. }
  1692. checkException();
  1693. }
  1694. inline __int64 getSignedResult(jvalue & result)
  1695. {
  1696. switch (returnType.get()[0])
  1697. {
  1698. case 'B': return result.b;
  1699. case 'S': return result.s;
  1700. case 'I': return result.i;
  1701. case 'J': return result.j;
  1702. case 'L':
  1703. {
  1704. // Result should be of class 'Number'
  1705. if (!result.l)
  1706. return 0;
  1707. jmethodID getVal = JNIenv->GetMethodID(JNIenv->GetObjectClass(result.l), "longValue", "()J");
  1708. if (!getVal)
  1709. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1710. return JNIenv->CallLongMethod(result.l, getVal);
  1711. }
  1712. default:
  1713. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1714. }
  1715. }
  1716. inline double getDoubleResult(jvalue &result)
  1717. {
  1718. switch (returnType.get()[0])
  1719. {
  1720. case 'D': return result.d;
  1721. case 'F': return result.f;
  1722. case 'L':
  1723. {
  1724. // Result should be of class 'Number'
  1725. if (!result.l)
  1726. return 0;
  1727. jmethodID getVal = JNIenv->GetMethodID(JNIenv->GetObjectClass(result.l), "doubleValue", "()D");
  1728. if (!getVal)
  1729. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1730. return JNIenv->CallDoubleMethod(result.l, getVal);
  1731. }
  1732. default:
  1733. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1734. }
  1735. }
  1736. bool getBooleanResult(jvalue &result)
  1737. {
  1738. switch (returnType.get()[0])
  1739. {
  1740. case 'Z': return result.z;
  1741. case 'L':
  1742. {
  1743. // Result should be of class 'Boolean'
  1744. if (!result.l)
  1745. return false;
  1746. jmethodID getVal = JNIenv->GetMethodID(JNIenv->GetObjectClass(result.l), "booleanValue", "()Z");
  1747. if (!getVal)
  1748. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1749. return JNIenv->CallBooleanMethod(result.l, getVal);
  1750. }
  1751. default:
  1752. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1753. }
  1754. }
  1755. inline void getDataResult(jvalue &result, size32_t &__len, void * &__result)
  1756. {
  1757. if (strcmp(returnType, "[B")!=0)
  1758. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1759. jbyteArray array = (jbyteArray) result.l;
  1760. __len = (array != NULL ? JNIenv->GetArrayLength(array) : 0);
  1761. __result = (__len > 0 ? rtlMalloc(__len) : NULL);
  1762. if (__result)
  1763. JNIenv->GetByteArrayRegion(array, 0, __len, (jbyte *) __result);
  1764. }
  1765. inline void getStringResult(jvalue &result, size32_t &__len, char * &__result)
  1766. {
  1767. switch (returnType.get()[0])
  1768. {
  1769. case 'C': // Single char returned, prototyped as STRING or STRING1 in ECL
  1770. rtlUnicodeToStrX(__len, __result, 1, &result.c);
  1771. break;
  1772. case 'L':
  1773. {
  1774. jstring sresult = (jstring) result.l;
  1775. if (sresult)
  1776. {
  1777. size_t size = JNIenv->GetStringUTFLength(sresult); // in bytes
  1778. const char *text = JNIenv->GetStringUTFChars(sresult, NULL);
  1779. size32_t chars = rtlUtf8Length(size, text);
  1780. rtlUtf8ToStrX(__len, __result, chars, text);
  1781. JNIenv->ReleaseStringUTFChars(sresult, text);
  1782. }
  1783. else
  1784. {
  1785. __len = 0;
  1786. __result = NULL;
  1787. }
  1788. break;
  1789. }
  1790. default:
  1791. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1792. }
  1793. }
  1794. inline void getUTF8Result(jvalue &result, size32_t &__chars, char * &__result)
  1795. {
  1796. switch (returnType.get()[0])
  1797. {
  1798. case 'C': // Single jchar returned, prototyped as UTF8 in ECL
  1799. rtlUnicodeToUtf8X(__chars, __result, 1, &result.c);
  1800. break;
  1801. case 'L':
  1802. {
  1803. jstring sresult = (jstring) result.l;
  1804. if (sresult)
  1805. {
  1806. size_t size = JNIenv->GetStringUTFLength(sresult); // Returns length in bytes (not chars)
  1807. const char * text = JNIenv->GetStringUTFChars(sresult, NULL);
  1808. rtlUtf8ToUtf8X(__chars, __result, rtlUtf8Length(size, text), text);
  1809. JNIenv->ReleaseStringUTFChars(sresult, text);
  1810. }
  1811. else
  1812. {
  1813. __chars = 0;
  1814. __result = NULL;
  1815. }
  1816. break;
  1817. }
  1818. default:
  1819. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1820. }
  1821. }
  1822. inline void getUnicodeResult(jvalue &result, size32_t &__chars, UChar * &__result)
  1823. {
  1824. switch (returnType.get()[0])
  1825. {
  1826. case 'C': // Single jchar returned, prototyped as UNICODE or UNICODE1 in ECL
  1827. rtlUnicodeToUnicodeX(__chars, __result, 1, &result.c);
  1828. break;
  1829. case 'L':
  1830. {
  1831. jstring sresult = (jstring) result.l;
  1832. if (sresult)
  1833. {
  1834. size_t size = JNIenv->GetStringUTFLength(sresult); // in bytes
  1835. const char *text = JNIenv->GetStringUTFChars(sresult, NULL);
  1836. size32_t chars = rtlUtf8Length(size, text);
  1837. rtlUtf8ToUnicodeX(__chars, __result, chars, text);
  1838. JNIenv->ReleaseStringUTFChars(sresult, text);
  1839. }
  1840. else
  1841. {
  1842. __chars = 0;
  1843. __result = NULL;
  1844. }
  1845. break;
  1846. }
  1847. default:
  1848. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  1849. }
  1850. }
  1851. inline void getSetResult(jvalue &result, bool & __isAllResult, size32_t & __resultBytes, void * & __result, int _elemType, size32_t elemSize)
  1852. {
  1853. if (returnType.get()[0] != '[')
  1854. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result (array expected)");
  1855. type_t elemType = (type_t) _elemType;
  1856. jarray array = (jarray) result.l;
  1857. int numResults = (array != NULL ? JNIenv->GetArrayLength(array) : 0);
  1858. rtlRowBuilder out;
  1859. byte *outData = NULL;
  1860. size32_t outBytes = 0;
  1861. if (numResults > 0)
  1862. {
  1863. if (elemSize != UNKNOWN_LENGTH)
  1864. {
  1865. out.ensureAvailable(numResults * elemSize); // MORE - check for overflow?
  1866. outData = out.getbytes();
  1867. }
  1868. switch(returnType.get()[1])
  1869. {
  1870. case 'Z':
  1871. checkType(type_boolean, sizeof(jboolean), elemType, elemSize);
  1872. JNIenv->GetBooleanArrayRegion((jbooleanArray) array, 0, numResults, (jboolean *) outData);
  1873. break;
  1874. case 'B':
  1875. checkType(type_int, sizeof(jbyte), elemType, elemSize);
  1876. JNIenv->GetByteArrayRegion((jbyteArray) array, 0, numResults, (jbyte *) outData);
  1877. break;
  1878. case 'C':
  1879. // we COULD map to a set of string1, but is there any point?
  1880. throw MakeStringException(0, "javaembed: Return type mismatch (char[] not supported)");
  1881. break;
  1882. case 'S':
  1883. checkType(type_int, sizeof(jshort), elemType, elemSize);
  1884. JNIenv->GetShortArrayRegion((jshortArray) array, 0, numResults, (jshort *) outData);
  1885. break;
  1886. case 'I':
  1887. checkType(type_int, sizeof(jint), elemType, elemSize);
  1888. JNIenv->GetIntArrayRegion((jintArray) array, 0, numResults, (jint *) outData);
  1889. break;
  1890. case 'J':
  1891. checkType(type_int, sizeof(jlong), elemType, elemSize);
  1892. JNIenv->GetLongArrayRegion((jlongArray) array, 0, numResults, (jlong *) outData);
  1893. break;
  1894. case 'F':
  1895. checkType(type_real, sizeof(jfloat), elemType, elemSize);
  1896. JNIenv->GetFloatArrayRegion((jfloatArray) array, 0, numResults, (jfloat *) outData);
  1897. break;
  1898. case 'D':
  1899. checkType(type_real, sizeof(jdouble), elemType, elemSize);
  1900. JNIenv->GetDoubleArrayRegion((jdoubleArray) array, 0, numResults, (jdouble *) outData);
  1901. break;
  1902. case 'L':
  1903. if (strcmp(returnType, "[Ljava/lang/String;") == 0)
  1904. {
  1905. for (int i = 0; i < numResults; i++)
  1906. {
  1907. jstring elem = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) array, i);
  1908. size_t lenBytes = JNIenv->GetStringUTFLength(elem); // in bytes
  1909. const char *text = JNIenv->GetStringUTFChars(elem, NULL);
  1910. switch (elemType)
  1911. {
  1912. case type_string:
  1913. if (elemSize == UNKNOWN_LENGTH)
  1914. {
  1915. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  1916. outData = out.getbytes() + outBytes;
  1917. * (size32_t *) outData = lenBytes;
  1918. rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
  1919. outBytes += lenBytes + sizeof(size32_t);
  1920. }
  1921. else
  1922. rtlStrToStr(elemSize, outData, lenBytes, text);
  1923. break;
  1924. case type_varstring:
  1925. if (elemSize == UNKNOWN_LENGTH)
  1926. {
  1927. out.ensureAvailable(outBytes + lenBytes + 1);
  1928. outData = out.getbytes() + outBytes;
  1929. rtlStrToVStr(0, outData, lenBytes, text);
  1930. outBytes += lenBytes + 1;
  1931. }
  1932. else
  1933. rtlStrToVStr(elemSize, outData, lenBytes, text); // Fixed size null terminated strings... weird.
  1934. break;
  1935. case type_utf8:
  1936. case type_unicode:
  1937. {
  1938. size32_t numchars = rtlUtf8Length(lenBytes, text);
  1939. if (elemType == type_utf8)
  1940. {
  1941. assertex (elemSize == UNKNOWN_LENGTH);
  1942. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  1943. outData = out.getbytes() + outBytes;
  1944. * (size32_t *) outData = numchars;
  1945. rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
  1946. outBytes += lenBytes + sizeof(size32_t);
  1947. }
  1948. else
  1949. {
  1950. if (elemSize == UNKNOWN_LENGTH)
  1951. {
  1952. // You can't assume that number of chars in utf8 matches number in unicode16 ...
  1953. size32_t numchars16;
  1954. rtlDataAttr unicode16;
  1955. rtlUtf8ToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
  1956. out.ensureAvailable(outBytes + numchars16*sizeof(UChar) + sizeof(size32_t));
  1957. outData = out.getbytes() + outBytes;
  1958. * (size32_t *) outData = numchars16;
  1959. rtlUnicodeToUnicode(numchars16, (UChar *) (outData+sizeof(size32_t)), numchars16, unicode16.getustr());
  1960. outBytes += numchars16*sizeof(UChar) + sizeof(size32_t);
  1961. }
  1962. else
  1963. rtlUtf8ToUnicode(elemSize / sizeof(UChar), (UChar *) outData, numchars, text);
  1964. }
  1965. break;
  1966. }
  1967. default:
  1968. JNIenv->ReleaseStringUTFChars(elem, text);
  1969. throw MakeStringException(0, "javaembed: Return type mismatch (ECL string type expected)");
  1970. }
  1971. JNIenv->ReleaseStringUTFChars(elem, text);
  1972. JNIenv->DeleteLocalRef(elem);
  1973. if (elemSize != UNKNOWN_LENGTH)
  1974. outData += elemSize;
  1975. }
  1976. }
  1977. else
  1978. throw MakeStringException(0, "javaembed: Return type mismatch (%s[] not supported)", returnType.get()+2);
  1979. break;
  1980. }
  1981. }
  1982. __isAllResult = false;
  1983. __resultBytes = elemSize == UNKNOWN_LENGTH ? outBytes : elemSize * numResults;
  1984. __result = out.detachdata();
  1985. }
  1986. inline const char *querySignature()
  1987. {
  1988. return argsig.get();
  1989. }
  1990. size32_t getRowResult(jobject result, ARowBuilder &builder)
  1991. {
  1992. const RtlTypeInfo *typeInfo = builder.queryAllocator()->queryOutputMeta()->queryTypeInfo();
  1993. assertex(typeInfo);
  1994. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  1995. JavaRowBuilder javaRowBuilder(JNIenv, &dummyField, result);
  1996. return typeInfo->build(builder, 0, &dummyField, javaRowBuilder);
  1997. }
  1998. void writeObjectResult(jobject result, IEsdlDefinition *esdl, const char *esdlservice, const char *name, IXmlWriter *writer)
  1999. {
  2000. JavaObjectXmlWriter x(JNIenv, result, name, *esdl, esdlservice, *writer);
  2001. x.write();
  2002. }
  2003. private:
  2004. StringAttr returnType;
  2005. StringAttr argsig;
  2006. StringAttr prevtext;
  2007. StringAttr prevClassPath;
  2008. jclass javaClass;
  2009. jmethodID javaMethodID;
  2010. };
  2011. class JavaXmlBuilder : implements IXmlWriterExt, public CInterface
  2012. {
  2013. public:
  2014. IMPLEMENT_IINTERFACE;
  2015. JavaXmlBuilder(JNIEnv *_JNIenv, IEsdlDefinition *esdl_, const char *esdlservice, const char *esdltype_)
  2016. : JNIenv(_JNIenv), esdl(esdl_), javaPackage(esdlservice), esdlType(esdltype_)
  2017. {
  2018. }
  2019. ~JavaXmlBuilder()
  2020. {
  2021. while (defStack.length())
  2022. popDefStackEntry(JNIenv);
  2023. HashIterator it(javaClasses);
  2024. ForEach(it)
  2025. {
  2026. IMapping &entry = it.query();
  2027. jclass *pClass = javaClasses.mapToValue(&entry);
  2028. if (pClass)
  2029. JNIenv->DeleteGlobalRef(*pClass);
  2030. }
  2031. }
  2032. void checkException()
  2033. {
  2034. javaembed::checkException(JNIenv, false);
  2035. }
  2036. void initWriter()
  2037. {
  2038. }
  2039. IXmlWriterExt & clear()
  2040. {
  2041. throwUnexpected();
  2042. }
  2043. virtual size32_t length() const
  2044. {
  2045. return 0;
  2046. }
  2047. virtual const char *str() const
  2048. {
  2049. throwUnexpected();
  2050. }
  2051. virtual void finalize() override
  2052. {
  2053. }
  2054. virtual IInterface *saveLocation() const {return nullptr;}
  2055. virtual void rewindTo(IInterface *loc)
  2056. {
  2057. //needs to be a no-op because it is used, but the way its used to trim empty xml sections I think we're fairly safe.
  2058. //revisit cleaning up any empty objects later.
  2059. }
  2060. inline IEsdlDefStruct *queryCurrentEsdlStruct()
  2061. {
  2062. if (!defStack.length() || !defStack.tos().defType)
  2063. return NULL;
  2064. return dynamic_cast<IEsdlDefStruct*>(defStack.tos().defType.get());
  2065. }
  2066. inline jobject getObject()
  2067. {
  2068. if (!defStack.length())
  2069. return 0;
  2070. return defStack.item(0).obj;
  2071. }
  2072. inline jobject getCurJavaObject()
  2073. {
  2074. if (!defStack.length())
  2075. return 0;
  2076. return defStack.tos().obj;
  2077. }
  2078. inline jclass getCurJavaClass()
  2079. {
  2080. if (!defStack.length())
  2081. return 0;
  2082. return defStack.tos().Class;
  2083. }
  2084. inline jmethodID getCurJavaConstructor()
  2085. {
  2086. if (!defStack.length())
  2087. return 0;
  2088. return defStack.tos().constructor;
  2089. }
  2090. virtual void outputEnumString(unsigned size, const char *text, const char *fieldname, IEsdlDefObject *defField)
  2091. {
  2092. const char *enum_type = defField->queryProp("enum_type");
  2093. if (!enum_type || !*enum_type)
  2094. return;
  2095. VStringBuffer enumClassName("%s/%s", javaPackage.str(), enum_type);
  2096. VStringBuffer enumSig("L%s;", enumClassName.str());
  2097. jfieldID fieldId = JNIenv->GetFieldID(getCurJavaClass(), fieldname, enumSig);
  2098. if (!fieldId)
  2099. return;
  2100. jclass enumClass = FindClass(enumClassName);
  2101. jmethodID fromString = JNIenv->GetStaticMethodID(enumClass, "fromString", "(Ljava/lang/String;)LEsdlExample/AddressType;"); //All types currently used for ESDL mapping have string constructors
  2102. StringAttr s(text, size);
  2103. jstring strvalue = JNIenv->NewStringUTF(s);
  2104. jobject value = JNIenv->CallStaticObjectMethod(enumClass, fromString, strvalue);
  2105. JNIenv->DeleteLocalRef(strvalue);
  2106. checkException();
  2107. JNIenv->SetObjectField(getCurJavaObject(), fieldId, value);
  2108. JNIenv->DeleteLocalRef(value);
  2109. checkException();
  2110. }
  2111. virtual void outputString(unsigned size, const char *text, const char *fieldname)
  2112. {
  2113. DefStackEntry *parent = defStack.length() ? &defStack.tos() : NULL;
  2114. if (!parent)
  2115. return;
  2116. const char *defTypeName = NULL;
  2117. bool isArray = (parent->defObj && parent->defObj->getEsdlType()==EsdlTypeArray);
  2118. if (isArray)
  2119. defTypeName = parent->defObj->queryProp("type");
  2120. else
  2121. {
  2122. IEsdlDefStruct *defStruct = queryCurrentEsdlStruct();
  2123. if (!defStruct)
  2124. return;
  2125. IEsdlDefObject *defField = defStruct->queryChild(fieldname);
  2126. if (!defField)
  2127. return;
  2128. if (defField->getEsdlType()==EsdlTypeEnumRef)
  2129. return outputEnumString(size, text, fieldname, defField);
  2130. defTypeName = defField->queryProp("type");
  2131. }
  2132. if (!defTypeName)
  2133. return;
  2134. const char *javaSig = esdl2JavaSig(*esdl, defTypeName);
  2135. if (!javaSig)
  2136. return;
  2137. const char *fieldClassName = esdl2JavaFullClassName(*esdl, defTypeName);
  2138. jclass typeClass = FindClass(fieldClassName);
  2139. jmethodID typeStringConstructor = JNIenv->GetMethodID(typeClass, "<init>", "(Ljava/lang/String;)V"); //All types currently used for ESDL mapping have string constructors
  2140. StringAttr s(text, size);
  2141. jstring strvalue = JNIenv->NewStringUTF(s);
  2142. jobject value = JNIenv->NewObject(typeClass, typeStringConstructor, strvalue);
  2143. JNIenv->DeleteLocalRef(strvalue);
  2144. checkException();
  2145. if (!value)
  2146. return;
  2147. if (isArray)
  2148. JNIenv->CallObjectMethod(parent->obj, parent->append, value);
  2149. else
  2150. {
  2151. jfieldID fieldId = JNIenv->GetFieldID(getCurJavaClass(), fieldname, javaSig);
  2152. if (fieldId)
  2153. JNIenv->SetObjectField(getCurJavaObject(), fieldId, value);
  2154. }
  2155. JNIenv->DeleteLocalRef(value);
  2156. checkException();
  2157. }
  2158. void outputString(const char *text, const char *fieldname)
  2159. {
  2160. outputString((unsigned)strlen(text), text, fieldname);
  2161. }
  2162. virtual void outputNumericString(const char *field, const char *fieldname)
  2163. {
  2164. outputString(field, fieldname);
  2165. }
  2166. virtual void outputBool(bool value, const char *fieldname)
  2167. {
  2168. outputString(value ? "true" : "false", fieldname);
  2169. }
  2170. virtual void outputUInt(unsigned __int64 field, unsigned size, const char *fieldname)
  2171. {
  2172. StringBuffer value;
  2173. value.append(field);
  2174. outputString(value.length(), value, fieldname);
  2175. }
  2176. virtual void outputInt(__int64 field, unsigned size, const char *fieldname)
  2177. {
  2178. StringBuffer value;
  2179. value.append(field);
  2180. outputString(value.length(), value, fieldname);
  2181. }
  2182. virtual void outputReal(double field, const char *fieldname)
  2183. {
  2184. StringBuffer value;
  2185. value.append(field);
  2186. outputString(value.length(), value, fieldname);
  2187. }
  2188. virtual void outputDecimal(const void *field, unsigned size, unsigned precision, const char *fieldname)
  2189. {
  2190. Decimal d;
  2191. d.setDecimal(size, precision, field);
  2192. outputString(d.getCString(), fieldname);
  2193. }
  2194. virtual void outputUDecimal(const void *field, unsigned size, unsigned precision, const char *fieldname)
  2195. {
  2196. Decimal d;
  2197. d.setUDecimal(size, precision, field);
  2198. outputString(d.getCString(), fieldname);
  2199. }
  2200. virtual void outputQString(unsigned len, const char *field, const char *fieldname)
  2201. {
  2202. MemoryAttr tempBuffer;
  2203. char * temp;
  2204. if (len <= 100)
  2205. temp = (char *)alloca(len);
  2206. else
  2207. temp = (char *)tempBuffer.allocate(len);
  2208. rtlQStrToStr(len, temp, len, field);
  2209. outputString(len, temp, fieldname);
  2210. }
  2211. virtual void outputUnicode(unsigned len, const UChar *field, const char *fieldname)
  2212. {
  2213. char * buff = 0;
  2214. unsigned bufflen = 0;
  2215. rtlUnicodeToCodepageX(bufflen, buff, len, field, "utf-8");
  2216. outputString(bufflen, buff, fieldname);
  2217. rtlFree(buff);
  2218. }
  2219. virtual void outputUtf8(unsigned len, const char *field, const char *fieldname)
  2220. {
  2221. outputString(len, field, fieldname);
  2222. }
  2223. virtual void outputData(unsigned len, const void *value, const char *fieldname)
  2224. {
  2225. }
  2226. virtual void outputQuoted(const char *text) //would have to let beginNested represent simple types with content set using this?
  2227. {
  2228. }
  2229. virtual void outputBeginDataset(const char *dsname, bool nestChildren) //not used by ESDL engine
  2230. {
  2231. }
  2232. virtual void outputEndDataset(const char *dsname)
  2233. {
  2234. }
  2235. inline IEsdlDefObject *queryChildStructDefObj(IEsdlDefObject *child)
  2236. {
  2237. if (child)
  2238. {
  2239. switch (child->getEsdlType())
  2240. {
  2241. case EsdlTypeArray:
  2242. {
  2243. const char *structType = child->queryProp("type");
  2244. if (structType)
  2245. return esdl->queryObj(structType);
  2246. break;
  2247. }
  2248. case EsdlTypeElement:
  2249. {
  2250. const char *structType = child->queryProp("complex_type");
  2251. if (structType)
  2252. return esdl->queryObj(structType);
  2253. break;
  2254. }
  2255. default:
  2256. break;
  2257. }
  2258. }
  2259. return NULL;
  2260. }
  2261. virtual void outputBeginNested(const char *fieldname, bool nestChildren)
  2262. {
  2263. IEsdlDefStruct *defStruct = NULL;
  2264. IEsdlDefObject *defField = NULL;
  2265. IEsdlDefObject *defType = NULL;
  2266. if (!defStack.length())
  2267. {
  2268. defType = esdl->queryObj(fieldname);
  2269. }
  2270. else
  2271. {
  2272. DefStackEntry &parent = defStack.tos();
  2273. if (parent.defObj && parent.defObj->getEsdlType()==EsdlTypeArray)
  2274. {
  2275. defType = parent.defType;
  2276. }
  2277. else
  2278. {
  2279. defStruct = queryCurrentEsdlStruct();
  2280. if (defStruct)
  2281. {
  2282. defField = defStruct->queryChild(fieldname);
  2283. if (defField)
  2284. defType = queryChildStructDefObj(defField);
  2285. }
  2286. }
  2287. }
  2288. pushDefStackEntry(JNIenv, javaPackage, fieldname, defType, defField);
  2289. }
  2290. virtual void outputEndNested(const char *fieldname)
  2291. {
  2292. if (defStack.length()<=1) //don't destroy root object yet
  2293. return;
  2294. if (!streq(fieldname, defStack.tos().name)) //should be exception? or forgive and forget?
  2295. return;
  2296. popDefStackEntry(JNIenv);
  2297. }
  2298. virtual void outputSetAll()
  2299. {
  2300. }
  2301. virtual void outputBeginArray(const char *fieldname)
  2302. {
  2303. }
  2304. virtual void outputEndArray(const char *fieldname)
  2305. {
  2306. }
  2307. virtual void outputInlineXml(const char *text)
  2308. {
  2309. }
  2310. virtual void outputXmlns(const char *name, const char *uri)
  2311. {
  2312. }
  2313. virtual void cutFrom(IInterface *location, StringBuffer& databuf)
  2314. {
  2315. }
  2316. virtual void outputInline(const char* text)
  2317. {
  2318. }
  2319. public:
  2320. JNIEnv *JNIenv;
  2321. Linked<IEsdlDefinition> esdl;
  2322. StringAttr javaPackage;
  2323. StringAttr esdlType;
  2324. class DefStackEntry : public CInterface
  2325. {
  2326. public:
  2327. DefStackEntry(const char *fieldname, IEsdlDefObject *_defType, IEsdlDefObject *_defObj) : name(fieldname), defType(_defType), defObj(_defObj), Class(0), obj(0), constructor(0), append(0), fieldId(0)
  2328. {
  2329. }
  2330. ~DefStackEntry()
  2331. {
  2332. }
  2333. public:
  2334. Linked<IEsdlDefObject> defType;
  2335. Linked<IEsdlDefObject> defObj;
  2336. StringAttr name;
  2337. jclass Class;
  2338. jmethodID constructor;
  2339. jmethodID append;
  2340. jfieldID fieldId;
  2341. jobject obj;
  2342. };
  2343. jobject MakeObjectGlobal(jobject local)
  2344. {
  2345. if (!local)
  2346. return 0;
  2347. jobject global = JNIenv->NewGlobalRef(local);
  2348. JNIenv->DeleteLocalRef(local);
  2349. return global;
  2350. }
  2351. jclass FindClass(const char *name)
  2352. {
  2353. jclass *pClass = javaClasses.getValue(name);
  2354. if (pClass)
  2355. return *pClass;
  2356. jclass Class = (jclass) MakeObjectGlobal(JNIenv->FindClass(name));
  2357. javaClasses.setValue(name, Class); //even save if result has no class
  2358. return Class;
  2359. }
  2360. void popDefStackEntry(JNIEnv *JNIenv)
  2361. {
  2362. if (!defStack.length())
  2363. return;
  2364. Owned<DefStackEntry> entry = &defStack.popGet();
  2365. if (entry->obj)
  2366. JNIenv->DeleteGlobalRef(entry->obj);
  2367. }
  2368. void pushDefStackEntry(JNIEnv *JNIenv, const char *package, const char *fieldname, IEsdlDefObject *defType, IEsdlDefObject *defObject)
  2369. {
  2370. DefStackEntry *parent = defStack.length() ? &defStack.tos() : NULL;
  2371. Owned<DefStackEntry> entry = new DefStackEntry(fieldname, defType, defObject);
  2372. JNIenv->ExceptionClear();
  2373. if (defObject && defObject->getEsdlType()==EsdlTypeArray)
  2374. {
  2375. const char *javaClassName = "java/util/ArrayList";
  2376. entry->Class = FindClass(javaClassName);
  2377. if (entry->Class)
  2378. {
  2379. entry->constructor = JNIenv->GetMethodID(entry->Class, "<init>", "()V");
  2380. entry->append = JNIenv->GetMethodID(entry->Class, "add", "(Ljava/lang/Object;)Z");
  2381. entry->obj = MakeObjectGlobal(JNIenv->NewObject(entry->Class, entry->constructor));
  2382. javaembed::checkException(JNIenv, false);
  2383. if (entry->obj)
  2384. {
  2385. if (parent && parent->Class)
  2386. {
  2387. VStringBuffer javaSig("L%s;", javaClassName);
  2388. entry->fieldId = JNIenv->GetFieldID(parent->Class, fieldname, javaSig);
  2389. if (parent->obj && entry->fieldId)
  2390. JNIenv->SetObjectField(parent->obj, entry->fieldId, entry->obj);
  2391. }
  2392. }
  2393. }
  2394. }
  2395. else if (defType)
  2396. {
  2397. VStringBuffer javaClassName("%s/%s", package, defType->queryName());
  2398. entry->Class = FindClass(javaClassName);
  2399. if (entry->Class)
  2400. {
  2401. entry->constructor = JNIenv->GetMethodID(entry->Class, "<init>", "()V");
  2402. entry->obj = MakeObjectGlobal(JNIenv->NewObject(entry->Class, entry->constructor));
  2403. javaembed::checkException(JNIenv, false);
  2404. if (entry->obj)
  2405. {
  2406. if (parent)
  2407. {
  2408. if (parent->defObj && parent->defObj->getEsdlType()==EsdlTypeArray)
  2409. JNIenv->CallObjectMethod(parent->obj, parent->append, entry->obj);
  2410. else if (parent->Class)
  2411. {
  2412. VStringBuffer javaSig("L%s;", javaClassName.str());
  2413. entry->fieldId = JNIenv->GetFieldID(parent->Class, fieldname, javaSig);
  2414. if (parent->obj && entry->fieldId)
  2415. JNIenv->SetObjectField(parent->obj, entry->fieldId, entry->obj);
  2416. }
  2417. javaembed::checkException(JNIenv, false);
  2418. }
  2419. }
  2420. }
  2421. }
  2422. defStack.append(*entry.getClear());
  2423. }
  2424. CIArrayOf<DefStackEntry> defStack;
  2425. MapStringTo<jclass> javaClasses;
  2426. };
  2427. // Each call to a Java function will use a new JavaEmbedScriptContext object
  2428. #define MAX_JNI_ARGS 10
  2429. class JavaClassReader
  2430. {
  2431. public:
  2432. JavaClassReader(const char *filename)
  2433. {
  2434. // Pull apart a class file to see its name and signature.
  2435. /* From https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1
  2436. ClassFile {
  2437. u4 magic;
  2438. u2 minor_version;
  2439. u2 major_version;
  2440. u2 constant_pool_count;
  2441. cp_info constant_pool[constant_pool_count-1];
  2442. u2 access_flags;
  2443. u2 this_class;
  2444. u2 super_class;
  2445. u2 interfaces_count;
  2446. u2 interfaces[interfaces_count];
  2447. u2 fields_count;
  2448. field_info fields[fields_count];
  2449. u2 methods_count;
  2450. method_info methods[methods_count];
  2451. u2 attributes_count;
  2452. attribute_info attributes[attributes_count];
  2453. }
  2454. */
  2455. #ifdef TRACE_CLASSFILE
  2456. DBGLOG("Reading class file created in %s", filename);
  2457. #endif
  2458. Owned<IFile> file = createIFile(filename);
  2459. OwnedIFileIO io = file->open(IFOread);
  2460. assertex(io);
  2461. read(io, 0, (size32_t)-1, b);
  2462. b.setEndian(__BIG_ENDIAN);
  2463. uint32_t magic;
  2464. b.read(magic);
  2465. if (magic != 0xcafebabe)
  2466. throwUnexpected();
  2467. uint16_t major, minor, cpc;
  2468. b.read(major);
  2469. b.read(minor);
  2470. b.read(cpc);
  2471. constOffsets = new unsigned[cpc];
  2472. constOffsets[0] = 0;
  2473. for (int i = 0; i < cpc-1; i++) // There are only cpc-1 entries, for reasons best known to the java designers
  2474. {
  2475. constOffsets[i+1] = b.getPos();
  2476. byte tag;
  2477. b.read(tag);
  2478. switch (tag)
  2479. {
  2480. case CONSTANT_Class:
  2481. uint16_t idx;
  2482. b.read(idx);
  2483. #ifdef TRACE_CLASSFILE
  2484. DBGLOG("%u: Class %u", i+1, idx);
  2485. #endif
  2486. break;
  2487. case CONSTANT_Fieldref:
  2488. case CONSTANT_Methodref:
  2489. case CONSTANT_InterfaceMethodref:
  2490. uint16_t classIdx;
  2491. uint16_t nametypeIdx;
  2492. b.read(classIdx);
  2493. b.read(nametypeIdx);
  2494. #ifdef TRACE_CLASSFILE
  2495. DBGLOG("%u: ref(%u) class %u nametype %u", i+1, tag, classIdx, nametypeIdx);
  2496. #endif
  2497. break;
  2498. case CONSTANT_String:
  2499. #ifdef TRACE_CLASSFILE
  2500. DBGLOG("%u: Tag %u", i+1, tag);
  2501. #endif
  2502. b.skip(2);
  2503. break;
  2504. case CONSTANT_Integer:
  2505. case CONSTANT_Float:
  2506. #ifdef TRACE_CLASSFILE
  2507. DBGLOG("%u: Tag %u", i+1, tag);
  2508. #endif
  2509. b.skip(4);
  2510. break;
  2511. case CONSTANT_Long:
  2512. case CONSTANT_Double:
  2513. #ifdef TRACE_CLASSFILE
  2514. DBGLOG("%u: Tag %u", i+1, tag);
  2515. #endif
  2516. b.skip(8);
  2517. break;
  2518. case CONSTANT_NameAndType:
  2519. uint16_t nameIdx;
  2520. uint16_t descIdx;
  2521. b.read(nameIdx);
  2522. b.read(descIdx);
  2523. #ifdef TRACE_CLASSFILE
  2524. DBGLOG("%u: NameAndType(%u) name %u desc %u", i+1, tag, nameIdx, descIdx);
  2525. #endif
  2526. break;
  2527. case CONSTANT_Utf8:
  2528. // length-prefixed
  2529. uint16_t length;
  2530. b.read(length);
  2531. const byte *val;
  2532. val = b.readDirect(length);
  2533. #ifdef TRACE_CLASSFILE
  2534. DBGLOG("%u: %.*s", i+1, length, val);
  2535. #endif
  2536. break;
  2537. case CONSTANT_MethodHandle:
  2538. #ifdef TRACE_CLASSFILE
  2539. DBGLOG("%u: Tag %u", i+1, tag);
  2540. #endif
  2541. b.skip(3);
  2542. break;
  2543. case CONSTANT_MethodType:
  2544. #ifdef TRACE_CLASSFILE
  2545. DBGLOG("%u: Tag %u", i+1, tag);
  2546. #endif
  2547. b.skip(2);
  2548. break;
  2549. case CONSTANT_InvokeDynamic:
  2550. #ifdef TRACE_CLASSFILE
  2551. DBGLOG("%u: Tag %u", i+1, tag);
  2552. #endif
  2553. b.skip(4);
  2554. break;
  2555. default:
  2556. DBGLOG("Unexpected tag %u reading bytecode file", tag);
  2557. throwUnexpected();
  2558. }
  2559. }
  2560. uint16_t access_flags; b.read(access_flags);
  2561. uint16_t this_class; b.read(this_class);
  2562. uint16_t super_class; b.read(super_class);
  2563. uint16_t interfaces_count; b.read(interfaces_count);
  2564. b.skip(interfaces_count*sizeof(uint16_t));
  2565. uint16_t fields_count; b.read(fields_count);
  2566. #ifdef TRACE_CLASSFILE
  2567. DBGLOG("Access flags %x this_class=%u super_class=%u interfaces_count=%u fields_count=%u", access_flags, this_class, super_class, interfaces_count, fields_count);
  2568. #endif
  2569. for (unsigned i = 0; i < fields_count; i++)
  2570. {
  2571. b.skip(6);
  2572. uint16_t attr_count;
  2573. b.read(attr_count);
  2574. for (unsigned j = 0; j < attr_count; j++)
  2575. {
  2576. b.skip(2);
  2577. uint32_t attr_length;
  2578. b.read(attr_length);
  2579. b.skip(attr_length);
  2580. }
  2581. }
  2582. uint16_t methods_count; b.read(methods_count);
  2583. #ifdef TRACE_CLASSFILE
  2584. DBGLOG("methods_count %u", methods_count);
  2585. #endif
  2586. for (unsigned i = 0; i < methods_count; i++)
  2587. {
  2588. uint16_t flags; b.read(flags);
  2589. uint16_t name; b.read(name);
  2590. uint16_t desc; b.read(desc);
  2591. #ifdef TRACE_CLASSFILE
  2592. DBGLOG("Method %u name %u desc %u flags %x", i, name, desc, flags);
  2593. #endif
  2594. if ((flags & (ACC_PUBLIC|ACC_STATIC)) == (ACC_PUBLIC|ACC_STATIC))
  2595. {
  2596. StringAttr thisName;
  2597. readUtf(thisName, name);
  2598. if (!streq(thisName, "<init>"))
  2599. {
  2600. StringAttr thisSig;
  2601. readUtf(thisSig, desc);
  2602. methodNames.append(thisName);
  2603. methodSigs.append(thisSig);
  2604. }
  2605. }
  2606. uint16_t attr_count;
  2607. b.read(attr_count);
  2608. for (unsigned j = 0; j < attr_count; j++)
  2609. {
  2610. b.skip(2);
  2611. uint32_t attr_length;
  2612. b.read(attr_length);
  2613. b.skip(attr_length);
  2614. }
  2615. }
  2616. /* Don't bother reading attributes as they are not really interesting to us
  2617. uint16_t attributes_count; b.read(attributes_count);
  2618. #ifdef TRACE_CLASSFILE
  2619. DBGLOG("attributes_count %u", attributes_count);
  2620. #endif
  2621. for (unsigned i = 0; i < attributes_count; i++)
  2622. {
  2623. b.skip(2);
  2624. uint32_t attr_length;
  2625. b.read(attr_length);
  2626. b.skip(attr_length);
  2627. }
  2628. #ifdef TRACE_CLASSFILE
  2629. DBGLOG("%u of %u bytes remaining", b.remaining(), b.length());
  2630. #endif
  2631. */
  2632. // Now we can find this class name
  2633. readTag(this_class, CONSTANT_Class);
  2634. readUtf(className, readIdx());
  2635. }
  2636. ~JavaClassReader()
  2637. {
  2638. delete [] constOffsets;
  2639. }
  2640. StringBuffer & getSignature(StringBuffer &ret, unsigned idx)
  2641. {
  2642. if (!methodNames.isItem(idx))
  2643. throw makeStringException(0, "No public static method found");
  2644. return ret.appendf("%s.%s:%s", className.get(), methodNames.item(idx), methodSigs.item(idx));
  2645. }
  2646. MemoryBuffer &getEmbedData(MemoryBuffer &result, bool mainClass)
  2647. {
  2648. if (mainClass && methodNames.length() != 1)
  2649. {
  2650. StringBuffer err;
  2651. ForEachItemIn(idx, methodNames)
  2652. {
  2653. if (idx)
  2654. err.append(", ");
  2655. if (idx == 5)
  2656. {
  2657. err.append("...");
  2658. break;
  2659. }
  2660. else
  2661. err.append(methodNames[idx]);
  2662. }
  2663. if (err)
  2664. throw makeStringExceptionV(0, "Embedded java should export exactly one public static method (%s seen)", err.str());
  2665. else
  2666. throw makeStringException(0, "Embedded java did not export any public static methods");
  2667. }
  2668. result.setEndian(__BIG_ENDIAN);
  2669. StringBuffer signature;
  2670. if (mainClass)
  2671. getSignature(signature, 0);
  2672. else
  2673. signature.set(className);
  2674. result.append((size32_t) signature.length());
  2675. result.append(signature.length(), signature.str());
  2676. result.append((size32_t) b.length());
  2677. result.append(b);
  2678. return result;
  2679. }
  2680. private:
  2681. uint16_t readIdx()
  2682. {
  2683. uint16_t idx;
  2684. b.read(idx);
  2685. return idx;
  2686. }
  2687. void readTag(unsigned idx, byte expected)
  2688. {
  2689. b.reset(constOffsets[idx]);
  2690. byte tag;
  2691. b.read(tag);
  2692. assertex(tag == expected);
  2693. }
  2694. void readUtf(StringAttr &dest, unsigned idx)
  2695. {
  2696. auto savepos = b.getPos();
  2697. readTag(idx, CONSTANT_Utf8);
  2698. uint16_t length;
  2699. b.read(length);
  2700. dest.set((const char *) b.readDirect(length), length);
  2701. b.reset(savepos);
  2702. }
  2703. enum const_type
  2704. {
  2705. CONSTANT_Class = 7,
  2706. CONSTANT_Fieldref = 9,
  2707. CONSTANT_Methodref = 10,
  2708. CONSTANT_InterfaceMethodref = 11,
  2709. CONSTANT_String = 8,
  2710. CONSTANT_Integer = 3,
  2711. CONSTANT_Float = 4,
  2712. CONSTANT_Long = 5,
  2713. CONSTANT_Double = 6,
  2714. CONSTANT_NameAndType = 12,
  2715. CONSTANT_Utf8 = 1,
  2716. CONSTANT_MethodHandle = 15,
  2717. CONSTANT_MethodType = 16,
  2718. CONSTANT_InvokeDynamic = 18
  2719. };
  2720. enum access_flag : uint16_t
  2721. {
  2722. ACC_PUBLIC = 0x0001, // Declared public; may be accessed from outside its package.
  2723. ACC_PRIVATE = 0x0002, // Declared private; accessible only within the defining class.
  2724. ACC_PROTECTED = 0x0004, // Declared protected; may be accessed within subclasses.
  2725. ACC_STATIC = 0x0008, // Declared static.
  2726. ACC_FINAL = 0x0010, // Declared final; must not be overridden (§5.4.5).
  2727. ACC_SYNCHRONIZED = 0x0020, // Declared synchronized; invocation is wrapped by a monitor use.
  2728. ACC_BRIDGE = 0x0040, // A bridge method, generated by the compiler.
  2729. ACC_VARARGS = 0x0080, // Declared with variable number of arguments.
  2730. ACC_NATIVE = 0x0100, // Declared native; implemented in a language other than Java.
  2731. ACC_ABSTRACT = 0x0400, // Declared abstract; no implementation is provided.
  2732. ACC_STRICT = 0x0800, // Declared strictfp; floating-point mode is FP-strict.
  2733. ACC_SYNTHETIC = 0x1000, // Declared synthetic; not present in the source code.
  2734. };
  2735. MemoryBuffer b;
  2736. unsigned *constOffsets = nullptr;
  2737. StringAttr className;
  2738. StringArray methodNames;
  2739. StringArray methodSigs;
  2740. };
  2741. class JavaEmbedImportContext : public CInterfaceOf<IEmbedFunctionContext>
  2742. {
  2743. public:
  2744. JavaEmbedImportContext(JavaThreadContext *_sharedCtx, jobject _instance, const char *options)
  2745. : sharedCtx(_sharedCtx), instance(_instance)
  2746. {
  2747. argcount = 0;
  2748. argsig = NULL;
  2749. StringArray opts;
  2750. opts.appendList(options, ",");
  2751. ForEachItemIn(idx, opts)
  2752. {
  2753. const char *opt = opts.item(idx);
  2754. const char *val = strchr(opt, '=');
  2755. if (val)
  2756. {
  2757. StringBuffer optName(val-opt, opt);
  2758. val++;
  2759. if (stricmp(optName, "classpath")==0)
  2760. classpath.set(val);
  2761. else
  2762. throw MakeStringException(0, "javaembed: Unknown option %s", optName.str());
  2763. }
  2764. }
  2765. // Create a new frame for local references and increase the capacity
  2766. // of those references to 64 (default is 16)
  2767. sharedCtx->JNIenv->PushLocalFrame(64);
  2768. }
  2769. ~JavaEmbedImportContext()
  2770. {
  2771. // Pop local reference frame; explicitly frees all local
  2772. // references made during that frame's lifetime
  2773. sharedCtx->JNIenv->PopLocalFrame(NULL);
  2774. }
  2775. virtual bool getBooleanResult()
  2776. {
  2777. return sharedCtx->getBooleanResult(result);
  2778. }
  2779. virtual void getDataResult(size32_t &__len, void * &__result)
  2780. {
  2781. sharedCtx->getDataResult(result, __len, __result);
  2782. }
  2783. virtual double getRealResult()
  2784. {
  2785. return sharedCtx->getDoubleResult(result);
  2786. }
  2787. virtual __int64 getSignedResult()
  2788. {
  2789. return sharedCtx->getSignedResult(result);
  2790. }
  2791. virtual unsigned __int64 getUnsignedResult()
  2792. {
  2793. throw MakeStringException(MSGAUD_user, 0, "javaembed: Unsigned results not supported"); // Java doesn't support unsigned
  2794. }
  2795. virtual void getStringResult(size32_t &__len, char * &__result)
  2796. {
  2797. sharedCtx->getStringResult(result, __len, __result);
  2798. }
  2799. virtual void getUTF8Result(size32_t &__chars, char * &__result)
  2800. {
  2801. sharedCtx->getUTF8Result(result, __chars, __result);
  2802. }
  2803. virtual void getUnicodeResult(size32_t &__chars, UChar * &__result)
  2804. {
  2805. sharedCtx->getUnicodeResult(result, __chars, __result);
  2806. }
  2807. virtual void getSetResult(bool & __isAllResult, size32_t & __resultBytes, void * & __result, int elemType, size32_t elemSize)
  2808. {
  2809. sharedCtx->getSetResult(result, __isAllResult, __resultBytes, __result, elemType, elemSize);
  2810. }
  2811. virtual IRowStream *getDatasetResult(IEngineRowAllocator * _resultAllocator)
  2812. {
  2813. return new JavaRowStream(result.l, _resultAllocator);
  2814. }
  2815. virtual byte * getRowResult(IEngineRowAllocator * _resultAllocator)
  2816. {
  2817. RtlDynamicRowBuilder rowBuilder(_resultAllocator);
  2818. size32_t len = sharedCtx->getRowResult(result.l, rowBuilder);
  2819. return (byte *) rowBuilder.finalizeRowClear(len);
  2820. }
  2821. virtual size32_t getTransformResult(ARowBuilder & builder)
  2822. {
  2823. return sharedCtx->getRowResult(result.l, builder);
  2824. }
  2825. virtual void bindBooleanParam(const char *name, bool val)
  2826. {
  2827. if (*argsig != 'Z')
  2828. typeError("BOOLEAN");
  2829. argsig++;
  2830. jvalue v;
  2831. v.z = val;
  2832. addArg(v);
  2833. }
  2834. virtual void bindDataParam(const char *name, size32_t len, const void *val)
  2835. {
  2836. if (argsig[0] != '[' || argsig[1] != 'B')
  2837. typeError("DATA");
  2838. argsig += 2;
  2839. jvalue v;
  2840. jbyteArray javaData = sharedCtx->JNIenv->NewByteArray(len);
  2841. sharedCtx->JNIenv->SetByteArrayRegion(javaData, 0, len, (jbyte *) val);
  2842. v.l = javaData;
  2843. addArg(v);
  2844. }
  2845. virtual void bindFloatParam(const char *name, float val)
  2846. {
  2847. // Could argue that the size should match...
  2848. jvalue v;
  2849. switch(*argsig)
  2850. {
  2851. case 'D':
  2852. v.d = val;
  2853. break;
  2854. case 'F':
  2855. v.f = val;
  2856. break;
  2857. default:
  2858. typeError("REAL");
  2859. break;
  2860. }
  2861. argsig++;
  2862. addArg(v);
  2863. }
  2864. virtual void bindRealParam(const char *name, double val)
  2865. {
  2866. jvalue v;
  2867. switch(*argsig)
  2868. {
  2869. case 'D':
  2870. v.d = val;
  2871. break;
  2872. case 'F':
  2873. v.f = val;
  2874. break;
  2875. default:
  2876. typeError("REAL");
  2877. break;
  2878. }
  2879. argsig++;
  2880. addArg(v);
  2881. }
  2882. virtual void bindSignedSizeParam(const char *name, int size, __int64 val)
  2883. {
  2884. bindSignedParam(name, val);
  2885. }
  2886. virtual void bindSignedParam(const char *name, __int64 val)
  2887. {
  2888. jvalue v;
  2889. switch(*argsig)
  2890. {
  2891. case 'I':
  2892. v.i = val;
  2893. break;
  2894. case 'J':
  2895. v.j = val;
  2896. break;
  2897. case 'S':
  2898. v.s = val;
  2899. break;
  2900. case 'B':
  2901. v.b = val;
  2902. break;
  2903. default:
  2904. typeError("INTEGER");
  2905. break;
  2906. }
  2907. argsig++;
  2908. addArg(v);
  2909. }
  2910. virtual void bindUnsignedSizeParam(const char *name, int size, unsigned __int64 val)
  2911. {
  2912. bindUnsignedParam(name, val);
  2913. }
  2914. virtual void bindUnsignedParam(const char *name, unsigned __int64 val)
  2915. {
  2916. throw MakeStringException(MSGAUD_user, 0, "javaembed: Unsigned parameters not supported"); // Java doesn't support unsigned
  2917. }
  2918. virtual void bindStringParam(const char *name, size32_t len, const char *val)
  2919. {
  2920. jvalue v;
  2921. switch(*argsig)
  2922. {
  2923. case 'C':
  2924. rtlStrToUnicode(1, &v.c, len, val);
  2925. argsig++;
  2926. break;
  2927. case 'L':
  2928. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  2929. {
  2930. argsig += 18;
  2931. unsigned unicodeChars;
  2932. UChar *unicode;
  2933. rtlStrToUnicodeX(unicodeChars, unicode, len, val);
  2934. v.l = sharedCtx->JNIenv->NewString(unicode, unicodeChars);
  2935. rtlFree(unicode);
  2936. break;
  2937. }
  2938. // fall into ...
  2939. default:
  2940. typeError("STRING");
  2941. break;
  2942. }
  2943. addArg(v);
  2944. }
  2945. virtual void bindVStringParam(const char *name, const char *val)
  2946. {
  2947. bindStringParam(name, strlen(val), val);
  2948. }
  2949. virtual void bindUTF8Param(const char *name, size32_t numchars, const char *val)
  2950. {
  2951. jvalue v;
  2952. switch(*argsig)
  2953. {
  2954. case 'C':
  2955. rtlUtf8ToUnicode(1, &v.c, numchars, val);
  2956. argsig++;
  2957. break;
  2958. case 'L':
  2959. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  2960. {
  2961. argsig += 18;
  2962. unsigned unicodeChars;
  2963. UChar *unicode;
  2964. rtlUtf8ToUnicodeX(unicodeChars, unicode, numchars, val);
  2965. v.l = sharedCtx->JNIenv->NewString(unicode, unicodeChars);
  2966. rtlFree(unicode);
  2967. break;
  2968. }
  2969. // fall into ...
  2970. default:
  2971. typeError("UTF8");
  2972. break;
  2973. }
  2974. addArg(v);
  2975. }
  2976. virtual void bindUnicodeParam(const char *name, size32_t numchars, const UChar *val)
  2977. {
  2978. jvalue v;
  2979. switch(*argsig)
  2980. {
  2981. case 'C':
  2982. rtlUnicodeToUnicode(1, &v.c, numchars, val);
  2983. argsig++;
  2984. break;
  2985. case 'L':
  2986. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  2987. {
  2988. argsig += 18;
  2989. v.l = sharedCtx->JNIenv->NewString(val, numchars);
  2990. break;
  2991. }
  2992. // fall into ...
  2993. default:
  2994. typeError("UNICODE");
  2995. break;
  2996. }
  2997. addArg(v);
  2998. }
  2999. virtual void bindSetParam(const char *name, int _elemType, size32_t elemSize, bool isAll, size32_t totalBytes, const void *setData)
  3000. {
  3001. jvalue v;
  3002. if (*argsig != '[')
  3003. typeError("SET");
  3004. argsig++;
  3005. type_t elemType = (type_t) _elemType;
  3006. int numElems = totalBytes / elemSize;
  3007. switch(*argsig)
  3008. {
  3009. case 'Z':
  3010. checkType(type_boolean, sizeof(jboolean), elemType, elemSize);
  3011. v.l = sharedCtx->JNIenv->NewBooleanArray(numElems);
  3012. sharedCtx->JNIenv->SetBooleanArrayRegion((jbooleanArray) v.l, 0, numElems, (jboolean *) setData);
  3013. break;
  3014. case 'B':
  3015. checkType(type_int, sizeof(jbyte), elemType, elemSize);
  3016. v.l = sharedCtx->JNIenv->NewByteArray(numElems);
  3017. sharedCtx->JNIenv->SetByteArrayRegion((jbyteArray) v.l, 0, numElems, (jbyte *) setData);
  3018. break;
  3019. case 'C':
  3020. // we COULD map to a set of string1, but is there any point?
  3021. typeError("");
  3022. break;
  3023. case 'S':
  3024. checkType(type_int, sizeof(jshort), elemType, elemSize);
  3025. v.l = sharedCtx->JNIenv->NewShortArray(numElems);
  3026. sharedCtx->JNIenv->SetShortArrayRegion((jshortArray) v.l, 0, numElems, (jshort *) setData);
  3027. break;
  3028. case 'I':
  3029. checkType(type_int, sizeof(jint), elemType, elemSize);
  3030. v.l = sharedCtx->JNIenv->NewIntArray(numElems);
  3031. sharedCtx->JNIenv->SetIntArrayRegion((jintArray) v.l, 0, numElems, (jint *) setData);
  3032. break;
  3033. case 'J':
  3034. checkType(type_int, sizeof(jlong), elemType, elemSize);
  3035. v.l = sharedCtx->JNIenv->NewLongArray(numElems);
  3036. sharedCtx->JNIenv->SetLongArrayRegion((jlongArray) v.l, 0, numElems, (jlong *) setData);
  3037. break;
  3038. case 'F':
  3039. checkType(type_real, sizeof(jfloat), elemType, elemSize);
  3040. v.l = sharedCtx->JNIenv->NewFloatArray(numElems);
  3041. sharedCtx->JNIenv->SetFloatArrayRegion((jfloatArray) v.l, 0, numElems, (jfloat *) setData);
  3042. break;
  3043. case 'D':
  3044. checkType(type_real, sizeof(jdouble), elemType, elemSize);
  3045. v.l = sharedCtx->JNIenv->NewDoubleArray(numElems);
  3046. sharedCtx->JNIenv->SetDoubleArrayRegion((jdoubleArray) v.l, 0, numElems, (jdouble *) setData);
  3047. break;
  3048. case 'L':
  3049. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  3050. {
  3051. argsig += 17; // Yes, 17, because we increment again at the end of the case
  3052. const byte *inData = (const byte *) setData;
  3053. const byte *endData = inData + totalBytes;
  3054. if (elemSize == UNKNOWN_LENGTH)
  3055. {
  3056. numElems = 0;
  3057. // Will need 2 passes to work out how many elements there are in the set :(
  3058. while (inData < endData)
  3059. {
  3060. int thisSize;
  3061. switch (elemType)
  3062. {
  3063. case type_varstring:
  3064. thisSize = strlen((const char *) inData) + 1;
  3065. break;
  3066. case type_string:
  3067. thisSize = * (size32_t *) inData + sizeof(size32_t);
  3068. break;
  3069. case type_unicode:
  3070. thisSize = (* (size32_t *) inData) * sizeof(UChar) + sizeof(size32_t);
  3071. break;
  3072. case type_utf8:
  3073. thisSize = rtlUtf8Size(* (size32_t *) inData, inData + sizeof(size32_t)) + sizeof(size32_t);;
  3074. break;
  3075. default:
  3076. typeError("STRING");
  3077. }
  3078. inData += thisSize;
  3079. numElems++;
  3080. }
  3081. inData = (const byte *) setData;
  3082. }
  3083. int idx = 0;
  3084. v.l = sharedCtx->JNIenv->NewObjectArray(numElems, sharedCtx->JNIenv->FindClass("java/lang/String"), NULL);
  3085. while (inData < endData)
  3086. {
  3087. jstring thisElem;
  3088. size32_t thisSize = elemSize;
  3089. switch (elemType)
  3090. {
  3091. case type_varstring:
  3092. {
  3093. size32_t numChars = strlen((const char *) inData);
  3094. unsigned unicodeChars;
  3095. rtlDataAttr unicode;
  3096. rtlStrToUnicodeX(unicodeChars, unicode.refustr(), numChars, (const char *) inData);
  3097. thisElem = sharedCtx->JNIenv->NewString(unicode.getustr(), unicodeChars);
  3098. if (elemSize == UNKNOWN_LENGTH)
  3099. thisSize = numChars + 1;
  3100. break;
  3101. }
  3102. case type_string:
  3103. {
  3104. if (elemSize == UNKNOWN_LENGTH)
  3105. {
  3106. thisSize = * (size32_t *) inData;
  3107. inData += sizeof(size32_t);
  3108. }
  3109. unsigned unicodeChars;
  3110. rtlDataAttr unicode;
  3111. rtlStrToUnicodeX(unicodeChars, unicode.refustr(), thisSize, (const char *) inData);
  3112. thisElem = sharedCtx->JNIenv->NewString(unicode.getustr(), unicodeChars);
  3113. break;
  3114. }
  3115. case type_unicode:
  3116. {
  3117. if (elemSize == UNKNOWN_LENGTH)
  3118. {
  3119. thisSize = (* (size32_t *) inData) * sizeof(UChar); // NOTE - it's in chars...
  3120. inData += sizeof(size32_t);
  3121. }
  3122. thisElem = sharedCtx->JNIenv->NewString((const UChar *) inData, thisSize / sizeof(UChar));
  3123. //checkJPythonError();
  3124. break;
  3125. }
  3126. case type_utf8:
  3127. {
  3128. assertex (elemSize == UNKNOWN_LENGTH);
  3129. size32_t numChars = * (size32_t *) inData;
  3130. inData += sizeof(size32_t);
  3131. unsigned unicodeChars;
  3132. rtlDataAttr unicode;
  3133. rtlUtf8ToUnicodeX(unicodeChars, unicode.refustr(), numChars, (const char *) inData);
  3134. thisElem = sharedCtx->JNIenv->NewString(unicode.getustr(), unicodeChars);
  3135. thisSize = rtlUtf8Size(numChars, inData);
  3136. break;
  3137. }
  3138. default:
  3139. typeError("STRING");
  3140. }
  3141. sharedCtx->checkException();
  3142. inData += thisSize;
  3143. sharedCtx->JNIenv->SetObjectArrayElement((jobjectArray) v.l, idx, thisElem);
  3144. sharedCtx->JNIenv->DeleteLocalRef(thisElem);
  3145. idx++;
  3146. }
  3147. }
  3148. else
  3149. typeError("");
  3150. break;
  3151. default:
  3152. throwUnexpected();
  3153. }
  3154. argsig++;
  3155. addArg(v);
  3156. }
  3157. virtual void bindRowParam(const char *name, IOutputMetaData & metaVal, const byte *val) override
  3158. {
  3159. if (*argsig != 'L') // should tell us the type of the object we need to create to pass in
  3160. typeError("RECORD");
  3161. // Class name is from the char after the L up to the first ;
  3162. const char *tail = strchr(argsig, ';');
  3163. if (!tail)
  3164. typeError("RECORD");
  3165. StringAttr className(argsig+1, tail - (argsig+1));
  3166. argsig = tail+1;
  3167. const RtlTypeInfo *typeInfo = metaVal.queryTypeInfo();
  3168. assertex(typeInfo);
  3169. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  3170. JavaObjectBuilder javaBuilder(sharedCtx->JNIenv, &dummyField, sharedCtx->loadClass(className));
  3171. typeInfo->process(val, val, &dummyField, javaBuilder); // Creates a java object from the incoming ECL row
  3172. jvalue v;
  3173. v.l = javaBuilder.getObject();
  3174. addArg(v);
  3175. }
  3176. virtual IInterface *bindParamWriter(IInterface *esdl, const char *esdlservice, const char *esdltype, const char *name)
  3177. {
  3178. if (*argsig != 'L') // should tell us the type of the object we need to create to pass in
  3179. typeError("OBJECT");
  3180. // Class name is from the char after the L up to the first ;
  3181. const char *tail = strchr(argsig, ';');
  3182. if (!tail)
  3183. typeError("OBJECT");
  3184. StringAttr className(argsig+1, tail - (argsig+1));
  3185. argsig = tail+1;
  3186. Owned<JavaXmlBuilder> writer = new JavaXmlBuilder(sharedCtx->JNIenv, dynamic_cast<IEsdlDefinition*>(esdl), esdlservice, esdltype);
  3187. writer->initWriter();
  3188. return (IXmlWriter*)writer.getClear();
  3189. }
  3190. virtual void paramWriterCommit(IInterface *writer)
  3191. {
  3192. JavaXmlBuilder *javaWriter = dynamic_cast<JavaXmlBuilder*>(writer);
  3193. if (!javaWriter)
  3194. throw MakeStringException(0, "javaembed: Invalid object writer for %s", sharedCtx->querySignature());
  3195. jvalue v;
  3196. v.l = javaWriter->getObject();
  3197. addArg(v);
  3198. }
  3199. virtual void bindDatasetParam(const char *name, IOutputMetaData & metaVal, IRowStream * val)
  3200. {
  3201. jvalue v;
  3202. char argsigStart = *argsig;
  3203. switch (argsigStart)
  3204. {
  3205. case '[':
  3206. case '<':
  3207. ++argsig;
  3208. break;
  3209. case 'L':
  3210. if (strncmp(argsig, "Ljava/util/Iterator<", 20) == 0)
  3211. {
  3212. argsig += 20;
  3213. break;
  3214. }
  3215. /* no break */
  3216. default:
  3217. typeError("DATASET");
  3218. }
  3219. if (*argsig != 'L') // should tell us the type of the object we need to create to pass in
  3220. typeError("DATASET");
  3221. // Class name is from the char after the L up to the first ;
  3222. const char *tail = strchr(argsig, ';');
  3223. if (!tail)
  3224. typeError("RECORD");
  3225. StringAttr className(argsig+1, tail - (argsig+1));
  3226. argsig = tail+1;
  3227. if (argsigStart=='L')
  3228. {
  3229. if (argsig[0] != '>' || argsig[1] != ';')
  3230. typeError("DATASET");
  3231. argsig += 2;
  3232. }
  3233. if (argsigStart=='[')
  3234. {
  3235. // Pass in an array of objects
  3236. PointerArrayOf<_jobject> allRows;
  3237. const RtlTypeInfo *typeInfo = metaVal.queryTypeInfo();
  3238. assertex(typeInfo);
  3239. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  3240. jclass Class = sharedCtx->loadClass(className);
  3241. JavaObjectBuilder javaBuilder(sharedCtx->JNIenv, &dummyField, Class);
  3242. for (;;)
  3243. {
  3244. roxiemem::OwnedConstRoxieRow thisRow = val->ungroupedNextRow();
  3245. if (!thisRow)
  3246. break;
  3247. const byte *brow = (const byte *) thisRow.get();
  3248. typeInfo->process(brow, brow, &dummyField, javaBuilder); // Creates a java object from the incoming ECL row
  3249. allRows.append(javaBuilder.getObject());
  3250. }
  3251. jobjectArray array = sharedCtx->JNIenv->NewObjectArray(allRows.length(), Class, NULL);
  3252. ForEachItemIn(idx, allRows)
  3253. {
  3254. sharedCtx->JNIenv->SetObjectArrayElement(array, idx, allRows.item(idx));
  3255. }
  3256. v.l = array;
  3257. }
  3258. else
  3259. {
  3260. // Pass in an iterator
  3261. // Create a java object of type com.HPCCSystems.HpccUtils - this acts as a proxy for the iterator
  3262. sharedCtx->JNIenv->ExceptionClear();
  3263. jclass proxyClass = sharedCtx->JNIenv->FindClass("com/HPCCSystems/HpccUtils");
  3264. sharedCtx->checkException();
  3265. jmethodID constructor = sharedCtx->JNIenv->GetMethodID(proxyClass, "<init>", "(JLjava/lang/String;)V");
  3266. sharedCtx->checkException();
  3267. jvalue param;
  3268. const RtlTypeInfo *typeInfo = metaVal.queryTypeInfo();
  3269. ECLDatasetIterator *iterator = new ECLDatasetIterator(sharedCtx->JNIenv, typeInfo, sharedCtx->loadClass(className), val);
  3270. param.j = (jlong) iterator;
  3271. iterators.append(*iterator);
  3272. jobject proxy = sharedCtx->JNIenv->NewObject(proxyClass, constructor, param, sharedCtx->JNIenv->NewStringUTF(helperLibraryName));
  3273. sharedCtx->checkException();
  3274. v.l = proxy;
  3275. }
  3276. addArg(v);
  3277. }
  3278. virtual void writeResult(IInterface *esdl, const char *esdlservice, const char *esdltype, IInterface *writer)
  3279. {
  3280. return sharedCtx->writeObjectResult(result.l, dynamic_cast<IEsdlDefinition*>(esdl), esdlservice, esdltype, dynamic_cast<IXmlWriter*>(writer));
  3281. }
  3282. virtual void importFunction(size32_t lenChars, const char *utf)
  3283. {
  3284. sharedCtx->importFunction(lenChars, utf, classpath, 0, nullptr, instance);
  3285. argsig = sharedCtx->querySignature();
  3286. assertex(*argsig == '(');
  3287. argsig++;
  3288. }
  3289. virtual void callFunction()
  3290. {
  3291. if (*argsig != ')')
  3292. throw MakeStringException(0, "javaembed: Too few ECL parameters passed for Java signature %s", sharedCtx->querySignature());
  3293. if (instance)
  3294. sharedCtx->callFunction(result, args, instance);
  3295. else
  3296. sharedCtx->callFunction(result, args);
  3297. }
  3298. virtual void compileEmbeddedScript(size32_t lenChars, const char *_script)
  3299. {
  3300. throwUnexpected();
  3301. }
  3302. virtual void loadCompiledScript(size32_t bytecodeLen, const void *bytecode) override
  3303. {
  3304. MemoryBuffer b;
  3305. b.setBuffer(bytecodeLen, (void *) bytecode, false);
  3306. b.setEndian(__BIG_ENDIAN);
  3307. uint32_t siglen; b.read(siglen);
  3308. const char *sig = (const char *) b.readDirect(siglen);
  3309. sharedCtx->importFunction(siglen, sig, classpath, bytecodeLen, (const byte *) bytecode, instance);
  3310. argsig = sharedCtx->querySignature();
  3311. assertex(*argsig == '(');
  3312. argsig++;
  3313. }
  3314. protected:
  3315. JavaThreadContext *sharedCtx;
  3316. jvalue result;
  3317. StringAttr classpath;
  3318. IArrayOf<ECLDatasetIterator> iterators; // to make sure they get freed
  3319. jobject instance; //instance of service object to call methods on
  3320. private:
  3321. __declspec(noreturn) void typeError(const char *ECLtype) __attribute__((noreturn))
  3322. {
  3323. const char *javaType;
  3324. int javaLen = 0;
  3325. switch (*argsig)
  3326. {
  3327. case 'Z': javaType = "boolean"; break;
  3328. case 'B': javaType = "byte"; break;
  3329. case 'C': javaType = "char"; break;
  3330. case 'S': javaType = "short"; break;
  3331. case 'I': javaType = "int"; break;
  3332. case 'J': javaType = "long"; break;
  3333. case 'F': javaType = "float"; break;
  3334. case 'D': javaType = "double"; break;
  3335. case '[': javaType = "array"; break;
  3336. case 'L':
  3337. {
  3338. javaType = argsig+1;
  3339. const char *semi = strchr(argsig, ';');
  3340. if (semi)
  3341. javaLen = semi - javaType;
  3342. break;
  3343. }
  3344. case ')':
  3345. throw MakeStringException(0, "javaembed: Too many ECL parameters passed for Java signature %s", sharedCtx->querySignature());
  3346. default:
  3347. throw MakeStringException(0, "javaembed: Unrecognized character %c in java signature %s", *argsig, sharedCtx->querySignature());
  3348. }
  3349. if (!javaLen)
  3350. javaLen = strlen(argsig);
  3351. throw MakeStringException(0, "javaembed: ECL type %s cannot be passed to Java type %.*s", ECLtype, javaLen, javaType);
  3352. }
  3353. void addArg(jvalue &arg)
  3354. {
  3355. assertex(argcount < MAX_JNI_ARGS);
  3356. args[argcount] = arg;
  3357. argcount++;
  3358. }
  3359. jvalue args[MAX_JNI_ARGS];
  3360. int argcount;
  3361. const char *argsig;
  3362. };
  3363. static __thread JavaThreadContext* threadContext; // We reuse per thread, for speed
  3364. static __thread ThreadTermFunc threadHookChain;
  3365. static void releaseContext()
  3366. {
  3367. if (threadContext)
  3368. {
  3369. delete threadContext;
  3370. threadContext = NULL;
  3371. }
  3372. if (threadHookChain)
  3373. {
  3374. (*threadHookChain)();
  3375. threadHookChain = NULL;
  3376. }
  3377. }
  3378. static JavaThreadContext *queryContext()
  3379. {
  3380. if (!threadContext)
  3381. {
  3382. threadContext = new JavaThreadContext;
  3383. threadHookChain = addThreadTermFunc(releaseContext);
  3384. }
  3385. return threadContext;
  3386. }
  3387. static JNIEnv *queryJNIEnv()
  3388. {
  3389. return queryContext()->JNIenv;
  3390. }
  3391. class JavaEmbedServiceContext : public CInterfaceOf<IEmbedServiceContext>
  3392. {
  3393. public:
  3394. JavaEmbedServiceContext(JavaThreadContext *_sharedCtx, const char *service, const char *_options)
  3395. : sharedCtx(_sharedCtx), Class(0), options(_options), className(service), object(0)
  3396. {
  3397. StringArray opts;
  3398. opts.appendList(options, ",");
  3399. ForEachItemIn(idx, opts)
  3400. {
  3401. const char *opt = opts.item(idx);
  3402. const char *val = strchr(opt, '=');
  3403. if (val)
  3404. {
  3405. StringBuffer optName(val-opt, opt);
  3406. val++;
  3407. if (stricmp(optName, "classpath")==0)
  3408. classpath.set(val);
  3409. else
  3410. throw MakeStringException(0, "javaembed: Unknown option %s", optName.str());
  3411. }
  3412. }
  3413. // Create a new frame for local references and increase the capacity
  3414. // of those references to 64 (default is 16)
  3415. sharedCtx->JNIenv->PushLocalFrame(64);
  3416. }
  3417. ~JavaEmbedServiceContext()
  3418. {
  3419. if (object)
  3420. sharedCtx->JNIenv->DeleteGlobalRef(object);
  3421. if (Class)
  3422. sharedCtx->JNIenv->DeleteGlobalRef(Class);
  3423. // Pop local reference frame; explicitly frees all local
  3424. // references made during that frame's lifetime
  3425. sharedCtx->JNIenv->PopLocalFrame(NULL);
  3426. }
  3427. void init()
  3428. {
  3429. jobject classLoader = sharedCtx->getThreadClassLoader();
  3430. checkException();
  3431. jmethodID loadClassMethod = sharedCtx->JNIenv->GetMethodID(sharedCtx->JNIenv->GetObjectClass(classLoader), "loadClass","(Ljava/lang/String;)Ljava/lang/Class;");
  3432. checkException();
  3433. jstring methodString = sharedCtx->JNIenv->NewStringUTF(className);
  3434. checkException();
  3435. Class = (jclass) sharedCtx->JNIenv->NewGlobalRef(sharedCtx->JNIenv->CallObjectMethod(classLoader, loadClassMethod, methodString));
  3436. checkException();
  3437. jmethodID constructor = sharedCtx->JNIenv->GetMethodID(Class, "<init>", "()V");
  3438. checkException();
  3439. object = sharedCtx->JNIenv->NewGlobalRef(sharedCtx->JNIenv->NewObject(Class, constructor));
  3440. checkException();
  3441. }
  3442. virtual IEmbedFunctionContext *createFunctionContext(const char *function)
  3443. {
  3444. if (!object)
  3445. return NULL;
  3446. Owned<JavaEmbedImportContext> fctx = new JavaEmbedImportContext(queryContext(), object, options);
  3447. fctx->importFunction(rtlUtf8Length(strlen(function), function), function);
  3448. return fctx.getClear();
  3449. }
  3450. void checkException()
  3451. {
  3452. javaembed::checkException(sharedCtx->JNIenv, false);
  3453. }
  3454. protected:
  3455. JavaThreadContext *sharedCtx;
  3456. StringBuffer className;
  3457. jclass Class;
  3458. jobject object;
  3459. StringAttr classpath;
  3460. StringAttr options;
  3461. };
  3462. class JavaEmbedContext : public CInterfaceOf<IEmbedContext>
  3463. {
  3464. public:
  3465. virtual IEmbedFunctionContext *createFunctionContext(unsigned flags, const char *options) override
  3466. {
  3467. return createFunctionContextEx(nullptr, nullptr, flags, options);
  3468. }
  3469. virtual IEmbedFunctionContext *createFunctionContextEx(ICodeContext * ctx, const IThorActivityContext *activityCtx, unsigned flags, const char *options) override
  3470. {
  3471. return new JavaEmbedImportContext(queryContext(), NULL, options);
  3472. }
  3473. virtual IEmbedServiceContext *createServiceContext(const char *service, unsigned flags, const char *options) override
  3474. {
  3475. Owned<JavaEmbedServiceContext> serviceContext = new JavaEmbedServiceContext(queryContext(), service, options);
  3476. serviceContext->init();
  3477. return serviceContext.getClear();
  3478. }
  3479. };
  3480. extern DECL_EXPORT IEmbedContext* getEmbedContext()
  3481. {
  3482. return new JavaEmbedContext;
  3483. }
  3484. static bool isValidIdentifier(const char *source)
  3485. {
  3486. return isalnum(*source) || *source=='_' || *source=='$' || ::readUtf8Size(source)>1; // This is not strictly accurate but probably good enough
  3487. }
  3488. static bool isFullClassFile(StringBuffer &className, bool &seenPublic, size32_t len, const char *source)
  3489. {
  3490. // A heuristic to determine whether the supplied embedded source is a full class file or just a single method
  3491. // Basically, if we see keyword "class" before we see { then we assume it's a full file
  3492. // Also track whether the public keyword has been supplied
  3493. bool inLineComment = false;
  3494. bool inBlockComment = false;
  3495. seenPublic = false;
  3496. while (len)
  3497. {
  3498. if (inLineComment)
  3499. {
  3500. if (*source=='\n')
  3501. inLineComment = false;
  3502. }
  3503. else if (inBlockComment)
  3504. {
  3505. if (*source=='*' && len > 1 && source[1]=='/')
  3506. {
  3507. inBlockComment = false;
  3508. len--;
  3509. source++;
  3510. }
  3511. }
  3512. else switch(*source)
  3513. {
  3514. case '/':
  3515. if (len > 1)
  3516. {
  3517. if (source[1]=='*')
  3518. {
  3519. inBlockComment = true;
  3520. len--;
  3521. source++;
  3522. }
  3523. else if (source[1]=='/')
  3524. inLineComment = true;
  3525. }
  3526. break;
  3527. case '{':
  3528. return false;
  3529. default:
  3530. if (isValidIdentifier(source))
  3531. {
  3532. const char *start = source;
  3533. while (len && isValidIdentifier(source))
  3534. {
  3535. source+=::readUtf8Size(source);
  3536. len--;
  3537. }
  3538. if (source-start == 5 && memcmp(start, "class", source-start)==0)
  3539. {
  3540. while (len && isspace(*source)) // MORE - a comment between the keyword and the classname will fail - tough.
  3541. {
  3542. source += ::readUtf8Size(source);
  3543. len--;
  3544. }
  3545. start = source;
  3546. while (len && isValidIdentifier(source))
  3547. {
  3548. source += ::readUtf8Size(source);
  3549. len--;
  3550. }
  3551. className.append(source-start, start);
  3552. return true;
  3553. }
  3554. else if (source-start == 6 && memcmp(start, "public", source-start)==0)
  3555. seenPublic = true;
  3556. }
  3557. break;
  3558. }
  3559. source += ::readUtf8Size(source);
  3560. len--;
  3561. }
  3562. // If we get here then it doesn't have a { at all - we COULD say it needs the prototype too but for now, who knows...
  3563. return false;
  3564. }
  3565. static StringBuffer & cleanupJavaError(StringBuffer &ret, const char *err, unsigned lineNumberOffset)
  3566. {
  3567. // Remove filename (as it's generated) and fix up line number. Skip errors that do not have line number in
  3568. const char *colon = strchr(err, ':');
  3569. if (colon && isdigit(colon[1]))
  3570. {
  3571. char *end;
  3572. unsigned lineno = strtoul(colon+1, &end, 10) - lineNumberOffset;
  3573. ret.appendf("(%u,1)%s", lineno, end);
  3574. }
  3575. return ret;
  3576. }
  3577. static void cleanupJavaErrors(StringBuffer &errors, unsigned lineNumberOffset)
  3578. {
  3579. StringArray errlines;
  3580. errlines.appendList(errors, "\n");
  3581. errors.clear();
  3582. ForEachItemIn(idx, errlines)
  3583. {
  3584. StringBuffer cleaned;
  3585. cleanupJavaError(cleaned, errlines.item(idx), lineNumberOffset);
  3586. if (cleaned.length())
  3587. errors.append(cleaned).append('\n');
  3588. }
  3589. }
  3590. static thread_local unsigned prevHash = 0;
  3591. static thread_local MemoryBuffer prevCompile;
  3592. extern DECL_EXPORT void precompile(size32_t & __lenResult,void * & __result,size32_t charsBody,const char * body)
  3593. {
  3594. unsigned sizeBody = rtlUtf8Size(charsBody, body); // size in bytes
  3595. unsigned hash = rtlHash32Data(sizeBody,body,0xcafebabe);
  3596. if (hash==prevHash) // Reusing result from the syntax check that normally immediately precedes a precompile
  3597. {
  3598. __lenResult = prevCompile.length();
  3599. __result = prevCompile.detachOwn();
  3600. prevHash = 0;
  3601. return;
  3602. }
  3603. StringBuffer tmpDirName;
  3604. getTempFilePath(tmpDirName, "javaembed", nullptr);
  3605. tmpDirName.append(PATHSEPCHAR).append("tmp.XXXXXX");
  3606. if (!mkdtemp((char *) tmpDirName.str()))
  3607. throw makeStringExceptionV(0, "Failed to create temporary directory %s (error %d)", tmpDirName.str(), errno);
  3608. Owned<IFile> tempDir = createIFile(tmpDirName);
  3609. StringBuffer classname;
  3610. bool seenPublic = false;
  3611. bool isFullClass = isFullClassFile(classname, seenPublic, charsBody, body); // note - we pass in length in characters, not bytes
  3612. if (!isFullClass)
  3613. classname.set("embed");
  3614. VStringBuffer javafile("%s" PATHSEPSTR "%s.java", tmpDirName.str(), classname.str());
  3615. FILE *source = fopen(javafile.str(), "wt");
  3616. fprintf(source, "package com.HPCCSystems.embed.x%x;\n", hash);
  3617. unsigned lineNumberOffset = 1; // for the /n above
  3618. if (isFullClass)
  3619. fprintf(source, "%.*s", sizeBody, body);
  3620. else
  3621. {
  3622. if (seenPublic)
  3623. fprintf(source, "public class embed\n{\n %.*s\n}", sizeBody, body);
  3624. else
  3625. fprintf(source, "public class embed\n{\n public static %.*s\n}", sizeBody, body);
  3626. lineNumberOffset += 2; // for the /n's above
  3627. }
  3628. fclose(source);
  3629. MemoryBuffer result;
  3630. Owned<IPipeProcess> pipe = createPipeProcess();
  3631. VStringBuffer javac("javac %s", javafile.str());
  3632. if (!pipe->run("javac", javac, tmpDirName, false, false, true, 0, false))
  3633. {
  3634. throw makeStringException(0, "Failed to run javac");
  3635. }
  3636. else
  3637. {
  3638. StringBuffer errors;
  3639. Owned<ISimpleReadStream> pipeReader = pipe->getErrorStream();
  3640. readSimpleStream(errors, *pipeReader);
  3641. pipe->closeError();
  3642. unsigned retcode = pipe->wait();
  3643. if (retcode)
  3644. {
  3645. if (errors.length())
  3646. {
  3647. DBGLOG("javaembed: %s", errors.str());
  3648. cleanupJavaErrors(errors, lineNumberOffset);
  3649. throw makeStringExceptionV(0, "%s", errors.str());
  3650. }
  3651. else
  3652. throw makeStringException(0, "Failed to precompile java code");
  3653. }
  3654. VStringBuffer mainfile("%s" PATHSEPSTR "%s.class", tmpDirName.str(), classname.str());
  3655. JavaClassReader reader(mainfile);
  3656. reader.getEmbedData(result, true);
  3657. removeFileTraceIfFail(mainfile);
  3658. // Now read nested classes
  3659. Owned<IDirectoryIterator> classFiles = tempDir->directoryFiles("*$*.class",false,false);
  3660. ForEach(*classFiles)
  3661. {
  3662. const char *thisFile = classFiles->query().queryFilename();
  3663. JavaClassReader reader(thisFile);
  3664. reader.getEmbedData(result, false);
  3665. removeFileTraceIfFail(thisFile);
  3666. }
  3667. }
  3668. removeFileTraceIfFail(javafile);
  3669. tempDir->remove();
  3670. __lenResult = result.length();
  3671. __result = result.detachOwn();
  3672. }
  3673. extern DECL_EXPORT void syntaxCheck(size32_t & __lenResult,char * & __result,size32_t charsBody,const char * body)
  3674. {
  3675. StringBuffer result;
  3676. try
  3677. {
  3678. size32_t ds;
  3679. rtlDataAttr d;
  3680. precompile(ds, d.refdata(), charsBody, body);
  3681. // Reuse result in the precompile that normally immediately follows
  3682. unsigned sizeBody = rtlUtf8Size(charsBody, body); // size in bytes
  3683. prevHash = rtlHash32Data(sizeBody,body,0xcafebabe);
  3684. prevCompile.setBuffer(ds, d.detachdata(), true);
  3685. }
  3686. catch (IException *E)
  3687. {
  3688. StringBuffer msg;
  3689. result.append(E->errorMessage(msg));
  3690. E->Release();
  3691. }
  3692. __lenResult = result.length();
  3693. __result = result.detach();
  3694. }
  3695. } // namespace
  3696. // Callbacks from java
  3697. extern "C" {
  3698. JNIEXPORT jboolean JNICALL Java_com_HPCCSystems_HpccUtils__1hasNext (JNIEnv *, jclass, jlong);
  3699. JNIEXPORT jobject JNICALL Java_com_HPCCSystems_HpccUtils__1next (JNIEnv *, jclass, jlong);
  3700. JNIEXPORT jclass JNICALL Java_com_HPCCSystems_HpccClassLoader_defineClassForEmbed(JNIEnv *env, jobject loader, jint bytecodeLen, jlong bytecode, jstring name);
  3701. }
  3702. JNIEXPORT jboolean JNICALL Java_com_HPCCSystems_HpccUtils__1hasNext (JNIEnv *JNIenv, jclass, jlong proxy)
  3703. {
  3704. try
  3705. {
  3706. javaembed::ECLDatasetIterator *e = (javaembed::ECLDatasetIterator *) proxy;
  3707. return e->hasNext();
  3708. }
  3709. catch (IException *E)
  3710. {
  3711. StringBuffer msg;
  3712. E->errorMessage(msg);
  3713. E->Release();
  3714. jclass eClass = JNIenv->FindClass("java/lang/IllegalArgumentException");
  3715. if (eClass)
  3716. JNIenv->ThrowNew(eClass, msg.str());
  3717. return false;
  3718. }
  3719. }
  3720. JNIEXPORT jobject JNICALL Java_com_HPCCSystems_HpccUtils__1next (JNIEnv *JNIenv, jclass, jlong proxy)
  3721. {
  3722. try
  3723. {
  3724. javaembed::ECLDatasetIterator *e = (javaembed::ECLDatasetIterator *) proxy;
  3725. return e->next();
  3726. }
  3727. catch (IException *E)
  3728. {
  3729. StringBuffer msg;
  3730. E->errorMessage(msg);
  3731. E->Release();
  3732. jclass eClass = JNIenv->FindClass("java/lang/IllegalArgumentException");
  3733. if (eClass)
  3734. JNIenv->ThrowNew(eClass, msg.str());
  3735. return NULL;
  3736. }
  3737. }
  3738. JNIEXPORT jclass JNICALL Java_com_HPCCSystems_HpccClassLoader_defineClassForEmbed(JNIEnv *env, jobject loader, jint datalen, jlong data, jstring name)
  3739. {
  3740. const char *nameChars = env->GetStringUTFChars(name, nullptr);
  3741. size32_t namelen = strlen(nameChars);
  3742. MemoryBuffer b;
  3743. b.setBuffer(datalen, (void *) data, false);
  3744. b.setEndian(__BIG_ENDIAN);
  3745. jclass ret = nullptr;
  3746. while (b.remaining())
  3747. {
  3748. uint32_t siglen; b.read(siglen);
  3749. const char *sig = (const char *) b.readDirect(siglen);
  3750. uint32_t bytecodeLen; b.read(bytecodeLen);
  3751. const jbyte * bytecode = (const jbyte *) b.readDirect(bytecodeLen);
  3752. if (siglen >= namelen && memcmp(sig, nameChars, namelen)==0 && (namelen == siglen || sig[namelen] == '.'))
  3753. {
  3754. #ifdef TRACE_CLASSFILE
  3755. DBGLOG("javaembed: loading class %s", nameChars);
  3756. #endif
  3757. ret = env->DefineClass(nameChars, loader, bytecode, bytecodeLen);
  3758. break;
  3759. }
  3760. }
  3761. env->ReleaseStringUTFChars(name, nameChars);
  3762. return ret;
  3763. }
  3764. // Used for dynamically loading in ESDL
  3765. extern "C" DECL_EXPORT IEmbedContext *getEmbedContextDynamic()
  3766. {
  3767. return javaembed::getEmbedContext();
  3768. }