javaembed.cpp 188 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171
  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. #include "enginecontext.hpp"
  32. #ifndef _WIN32
  33. #include <sys/resource.h>
  34. #endif
  35. static const char * compatibleVersions[] = {
  36. "Java Embed Helper 1.0.0",
  37. NULL };
  38. static const char *version = "Java Embed Helper 1.0.0";
  39. #ifdef _DEBUG
  40. //#define TRACE_GLOBALREF
  41. //#define TRACE_CLASSFILE
  42. //#define CHECK_JNI
  43. //#define FORCE_GC
  44. /* Note - if you enable CHECK_JNI and see output like:
  45. * WARNING in native method: JNI call made without checking exceptions when required to from CallObjectMethodV
  46. * where for 'from' may be any of several functions, then the cause is likely to be a missing call to checkException()
  47. * after a call to the named function. One way to find the responsible call is with a breakpoint on checked_jni_CallObjectMethodV
  48. * The last time that breakpoint is hit before the warning is given should have a stack trace that tells you all you need to know.
  49. */
  50. #endif
  51. extern "C" DECL_EXPORT bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
  52. {
  53. if (pb->size == sizeof(ECLPluginDefinitionBlockEx))
  54. {
  55. ECLPluginDefinitionBlockEx * pbx = (ECLPluginDefinitionBlockEx *) pb;
  56. pbx->compatibleVersions = compatibleVersions;
  57. }
  58. else if (pb->size != sizeof(ECLPluginDefinitionBlock))
  59. return false;
  60. pb->magicVersion = PLUGIN_VERSION;
  61. pb->version = version;
  62. pb->moduleName = "java";
  63. pb->ECL = NULL;
  64. pb->flags = PLUGIN_MULTIPLE_VERSIONS;
  65. pb->description = "Java Embed Helper";
  66. return true;
  67. }
  68. __declspec(noreturn) static void UNSUPPORTED(const char *feature) __attribute__((noreturn));
  69. static void UNSUPPORTED(const char *feature)
  70. {
  71. throw MakeStringException(-1, "UNSUPPORTED feature: %s not supported in java plugin", feature);
  72. }
  73. namespace javaembed {
  74. static jmethodID throwable_toString;
  75. static jmethodID throwable_getStackTrace;
  76. static jmethodID throwable_getCause;
  77. static jmethodID frame_toString;
  78. static void forceGC(class CheckedJNIEnv* JNIenv);
  79. /**
  80. * CheckedJNIEnv is a wrapper around JNIEnv that ensures that we check for exceptions after every call (and turn them into C++ exceptions).
  81. *
  82. * It should probably be refactored to have the JNIEnv pointer as a member rather than a base class. As it stands, it's possible to cast
  83. * safely between CheckedJNIEnv * and JNIEnv*
  84. *
  85. */
  86. class CheckedJNIEnv : private JNIEnv
  87. {
  88. template<typename T> T checkException(T a) { checkException(); return a; }
  89. public:
  90. StringBuffer &getString(StringBuffer &ret, jstring s)
  91. {
  92. if (s)
  93. {
  94. const char* str = GetStringUTFChars(s, NULL);
  95. ret.append(str);
  96. ReleaseStringUTFChars(s, str);
  97. }
  98. return ret;
  99. }
  100. StringAttr &getString(StringAttr &ret, jstring s)
  101. {
  102. if (s)
  103. {
  104. const char* str = GetStringUTFChars(s, NULL);
  105. ret.set(str);
  106. ReleaseStringUTFChars(s, str);
  107. }
  108. return ret;
  109. }
  110. void checkUnexpectedException()
  111. {
  112. if (JNIEnv::ExceptionCheck())
  113. {
  114. DBGLOG("javaembed: Uunexpected java exception while processing exception");
  115. JNIEnv::ExceptionDescribe();
  116. JNIEnv::ExceptionClear();
  117. throwUnexpected();
  118. }
  119. }
  120. void traceException(jthrowable exception)
  121. {
  122. // Don't use auto-checking in here, as we are already inside exception-checking code
  123. jstring msg = (jstring) JNIEnv::CallObjectMethod(exception, throwable_toString);
  124. checkUnexpectedException();
  125. const char *text = JNIEnv::GetStringUTFChars(msg, 0);
  126. DBGLOG("javaembed: exception: %s", text);
  127. JNIEnv::ReleaseStringUTFChars(msg, text);
  128. checkUnexpectedException();
  129. JNIEnv::DeleteLocalRef(msg);
  130. jobjectArray frames = (jobjectArray) JNIEnv::CallObjectMethod(exception, throwable_getStackTrace);
  131. checkUnexpectedException();
  132. jsize length = JNIEnv::GetArrayLength(frames);
  133. for (jsize i = 0; i < length; i++)
  134. {
  135. jobject frame = JNIEnv::GetObjectArrayElement(frames, i);
  136. checkUnexpectedException();
  137. msg = (jstring) JNIEnv::CallObjectMethod(frame, frame_toString);
  138. checkUnexpectedException();
  139. text = JNIEnv::GetStringUTFChars(msg, 0);
  140. DBGLOG("javaembed: exception: stack: %s", text);
  141. JNIEnv::ReleaseStringUTFChars(msg, text);
  142. checkUnexpectedException();
  143. JNIEnv::DeleteLocalRef(msg);
  144. JNIEnv::DeleteLocalRef(frame);
  145. }
  146. jthrowable cause = (jthrowable) JNIEnv::CallObjectMethod(exception, throwable_getCause);
  147. checkUnexpectedException();
  148. if (cause && cause != exception)
  149. {
  150. DBGLOG("javaembed: exception: Caused by:");
  151. traceException(cause);
  152. }
  153. }
  154. void checkException()
  155. {
  156. if (JNIEnv::ExceptionCheck())
  157. {
  158. jthrowable exception = JNIEnv::ExceptionOccurred();
  159. JNIEnv::ExceptionClear();
  160. traceException(exception);
  161. jstring cause = (jstring) JNIEnv::CallObjectMethod(exception, throwable_toString);
  162. JNIEnv::ExceptionClear();
  163. const char *text = JNIEnv::GetStringUTFChars(cause, 0);
  164. VStringBuffer message("javaembed: %s", text);
  165. JNIEnv::ReleaseStringUTFChars(cause, text);
  166. JNIEnv::ExceptionClear();
  167. rtlFail(0, message);
  168. }
  169. }
  170. jclass FindClass(const char *name)
  171. {
  172. return checkException(JNIEnv::FindClass(name));
  173. }
  174. jclass FindGlobalClass(const char *name)
  175. {
  176. return (jclass) NewGlobalRef(FindClass(name), "NewGlobalRef");
  177. }
  178. void GetBooleanArrayRegion(jbooleanArray array,
  179. jsize start, jsize len, jboolean *buf) {
  180. functions->GetBooleanArrayRegion(this,array,start,len,buf);
  181. checkException();
  182. }
  183. void GetByteArrayRegion(jbyteArray array,
  184. jsize start, jsize len, jbyte *buf) {
  185. functions->GetByteArrayRegion(this,array,start,len,buf);
  186. checkException();
  187. }
  188. void GetCharArrayRegion(jcharArray array,
  189. jsize start, jsize len, jchar *buf) {
  190. functions->GetCharArrayRegion(this,array,start,len,buf);
  191. checkException();
  192. }
  193. void GetShortArrayRegion(jshortArray array,
  194. jsize start, jsize len, jshort *buf) {
  195. functions->GetShortArrayRegion(this,array,start,len,buf);
  196. checkException();
  197. }
  198. void GetIntArrayRegion(jintArray array,
  199. jsize start, jsize len, jint *buf) {
  200. functions->GetIntArrayRegion(this,array,start,len,buf);
  201. checkException();
  202. }
  203. void GetLongArrayRegion(jlongArray array,
  204. jsize start, jsize len, jlong *buf) {
  205. functions->GetLongArrayRegion(this,array,start,len,buf);
  206. checkException();
  207. }
  208. void GetFloatArrayRegion(jfloatArray array,
  209. jsize start, jsize len, jfloat *buf) {
  210. functions->GetFloatArrayRegion(this,array,start,len,buf);
  211. checkException();
  212. }
  213. void GetDoubleArrayRegion(jdoubleArray array,
  214. jsize start, jsize len, jdouble *buf) {
  215. functions->GetDoubleArrayRegion(this,array,start,len,buf);
  216. checkException();
  217. }
  218. jsize GetArrayLength(jarray array) {
  219. return checkException(functions->GetArrayLength(this,array));
  220. }
  221. jsize GetStringUTFLength(jstring str) {
  222. return checkException(functions->GetStringUTFLength(this,str));
  223. }
  224. const char* GetStringUTFChars(jstring str, jboolean *isCopy) {
  225. return checkException(functions->GetStringUTFChars(this,str,isCopy));
  226. }
  227. void ReleaseStringUTFChars(jstring str, const char* chars) {
  228. functions->ReleaseStringUTFChars(this,str,chars);
  229. checkException();
  230. }
  231. jboolean GetBooleanField(jobject obj, jfieldID fieldID)
  232. {
  233. return checkException(JNIEnv::GetBooleanField(obj,fieldID));
  234. }
  235. jobject GetObjectField(jobject obj, jfieldID fieldID)
  236. {
  237. return checkException(JNIEnv::GetObjectField(obj,fieldID));
  238. }
  239. jbyte GetByteField(jobject obj, jfieldID fieldID) {
  240. return checkException(JNIEnv::GetByteField(obj,fieldID));
  241. }
  242. jchar GetCharField(jobject obj, jfieldID fieldID) {
  243. return checkException(JNIEnv::GetCharField(obj,fieldID));
  244. }
  245. jshort GetShortField(jobject obj, jfieldID fieldID) {
  246. return checkException(JNIEnv::GetShortField(obj,fieldID));
  247. }
  248. jint GetIntField(jobject obj, jfieldID fieldID) {
  249. return checkException(JNIEnv::GetIntField(obj,fieldID));
  250. }
  251. jlong GetLongField(jobject obj, jfieldID fieldID) {
  252. return checkException(JNIEnv::GetLongField(obj,fieldID));
  253. }
  254. jfloat GetFloatField(jobject obj, jfieldID fieldID)
  255. {
  256. return checkException(JNIEnv::GetFloatField(obj,fieldID));
  257. }
  258. jdouble GetDoubleField(jobject obj, jfieldID fieldID)
  259. {
  260. return checkException(JNIEnv::GetDoubleField(obj,fieldID));
  261. }
  262. jboolean IsSameObject(jobject obj1, jobject obj2)
  263. {
  264. return checkException(JNIEnv::IsSameObject(obj1, obj2));
  265. }
  266. void SetObjectField(jobject obj, jfieldID fieldID, jobject val) {
  267. functions->SetObjectField(this,obj,fieldID,val);
  268. checkException();
  269. }
  270. void SetBooleanField(jobject obj, jfieldID fieldID,
  271. jboolean val) {
  272. functions->SetBooleanField(this,obj,fieldID,val);
  273. checkException();
  274. }
  275. void SetByteField(jobject obj, jfieldID fieldID,
  276. jbyte val) {
  277. functions->SetByteField(this,obj,fieldID,val);
  278. checkException();
  279. }
  280. void SetCharField(jobject obj, jfieldID fieldID,
  281. jchar val) {
  282. functions->SetCharField(this,obj,fieldID,val);
  283. checkException();
  284. }
  285. void SetShortField(jobject obj, jfieldID fieldID,
  286. jshort val) {
  287. functions->SetShortField(this,obj,fieldID,val);
  288. checkException();
  289. }
  290. void SetIntField(jobject obj, jfieldID fieldID,
  291. jint val) {
  292. functions->SetIntField(this,obj,fieldID,val);
  293. checkException();
  294. }
  295. void SetLongField(jobject obj, jfieldID fieldID,
  296. jlong val) {
  297. functions->SetLongField(this,obj,fieldID,val);
  298. checkException();
  299. }
  300. void SetFloatField(jobject obj, jfieldID fieldID,
  301. jfloat val) {
  302. functions->SetFloatField(this,obj,fieldID,val);
  303. checkException();
  304. }
  305. void SetDoubleField(jobject obj, jfieldID fieldID,
  306. jdouble val) {
  307. functions->SetDoubleField(this,obj,fieldID,val);
  308. checkException();
  309. }
  310. jstring NewString(const jchar *unicode, jsize len) {
  311. return checkException(functions->NewString(this,unicode,len));
  312. }
  313. jbooleanArray NewBooleanArray(jsize len) {
  314. return checkException(functions->NewBooleanArray(this,len));
  315. }
  316. jbyteArray NewByteArray(jsize len) {
  317. return checkException(functions->NewByteArray(this,len));
  318. }
  319. jcharArray NewCharArray(jsize len) {
  320. return checkException(functions->NewCharArray(this,len));
  321. }
  322. jshortArray NewShortArray(jsize len) {
  323. return checkException(functions->NewShortArray(this,len));
  324. }
  325. jintArray NewIntArray(jsize len) {
  326. return checkException(functions->NewIntArray(this,len));
  327. }
  328. jlongArray NewLongArray(jsize len) {
  329. return checkException(functions->NewLongArray(this,len));
  330. }
  331. jfloatArray NewFloatArray(jsize len) {
  332. return checkException(functions->NewFloatArray(this,len));
  333. }
  334. jdoubleArray NewDoubleArray(jsize len) {
  335. return checkException(functions->NewDoubleArray(this,len));
  336. }
  337. void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
  338. const jboolean *buf) {
  339. functions->SetBooleanArrayRegion(this,array,start,len,buf);
  340. checkException();
  341. }
  342. void SetByteArrayRegion(jbyteArray array, jsize start, jsize len,
  343. const jbyte *buf) {
  344. functions->SetByteArrayRegion(this,array,start,len,buf);
  345. checkException();
  346. }
  347. void SetCharArrayRegion(jcharArray array, jsize start, jsize len,
  348. const jchar *buf) {
  349. functions->SetCharArrayRegion(this,array,start,len,buf);
  350. checkException();
  351. }
  352. void SetShortArrayRegion(jshortArray array, jsize start, jsize len,
  353. const jshort *buf) {
  354. functions->SetShortArrayRegion(this,array,start,len,buf);
  355. checkException();
  356. }
  357. void SetIntArrayRegion(jintArray array, jsize start, jsize len,
  358. const jint *buf) {
  359. functions->SetIntArrayRegion(this,array,start,len,buf);
  360. checkException();
  361. }
  362. void SetLongArrayRegion(jlongArray array, jsize start, jsize len,
  363. const jlong *buf) {
  364. functions->SetLongArrayRegion(this,array,start,len,buf);
  365. checkException();
  366. }
  367. void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
  368. const jfloat *buf) {
  369. functions->SetFloatArrayRegion(this,array,start,len,buf);
  370. checkException();
  371. }
  372. void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
  373. const jdouble *buf) {
  374. functions->SetDoubleArrayRegion(this,array,start,len,buf);
  375. checkException();
  376. }
  377. jobjectArray NewObjectArray(jsize len, jclass clazz,
  378. jobject init) {
  379. return checkException(functions->NewObjectArray(this,len,clazz,init));
  380. }
  381. jobject GetObjectArrayElement(jobjectArray array, jsize index) {
  382. return checkException(functions->GetObjectArrayElement(this,array,index));
  383. }
  384. void SetObjectArrayElement(jobjectArray array, jsize index,
  385. jobject val) {
  386. functions->SetObjectArrayElement(this,array,index,val);
  387. checkException();
  388. }
  389. jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) {
  390. return checkException(functions->ToReflectedField(this,cls,fieldID,isStatic));
  391. }
  392. jboolean IsAssignableFrom(jclass clazz1, jclass clazz2) {
  393. return checkException(functions->IsAssignableFrom(this,clazz1,clazz2));
  394. }
  395. jclass GetObjectClass(jobject obj)
  396. {
  397. return checkException(JNIEnv::GetObjectClass(obj));
  398. }
  399. jfieldID GetFieldID(jclass clazz, const char *name, const char *sig)
  400. {
  401. return checkException(JNIEnv::GetFieldID(clazz,name,sig));
  402. }
  403. jfieldID GetFieldIDUnchecked(jclass clazz, const char *name, const char *sig)
  404. {
  405. jfieldID ret = JNIEnv::GetFieldID(clazz,name,sig);
  406. ExceptionClear();
  407. return ret;
  408. }
  409. jmethodID GetMethodID(jclass clazz, const char *name, const char *sig)
  410. {
  411. return checkException(JNIEnv::GetMethodID(clazz, name, sig));
  412. }
  413. jmethodID GetMethodIDUnchecked(jclass clazz, const char *name, const char *sig)
  414. {
  415. jmethodID ret = JNIEnv::GetMethodID(clazz, name, sig);
  416. ExceptionClear();
  417. return ret;
  418. }
  419. jmethodID GetStaticMethodID(jclass clazz, const char *name, const char *sig)
  420. {
  421. return checkException(JNIEnv::GetStaticMethodID(clazz, name, sig));
  422. }
  423. void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) {
  424. va_list args;
  425. va_start(args,methodID);
  426. functions->CallStaticVoidMethodV(this,cls,methodID,args);
  427. va_end(args);
  428. checkException();
  429. }
  430. jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, ...)
  431. {
  432. va_list args;
  433. jobject result;
  434. va_start(args,methodID);
  435. result = JNIEnv::CallStaticObjectMethodV(clazz,methodID,args);
  436. va_end(args);
  437. checkException();
  438. return result;
  439. }
  440. jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) {
  441. va_list args;
  442. jdouble result;
  443. va_start(args,methodID);
  444. result = functions->CallDoubleMethodV(this,obj,methodID,args);
  445. va_end(args);
  446. checkException();
  447. return result;
  448. }
  449. jlong CallLongMethod(jobject obj, jmethodID methodID, ...) {
  450. va_list args;
  451. jlong result;
  452. va_start(args,methodID);
  453. result = functions->CallLongMethodV(this,obj,methodID,args);
  454. va_end(args);
  455. checkException();
  456. return result;
  457. }
  458. jboolean CallBooleanMethod(jobject obj, jmethodID methodID, ...)
  459. {
  460. va_list args;
  461. jboolean result;
  462. va_start(args,methodID);
  463. result = JNIEnv::CallBooleanMethodV(obj,methodID,args);
  464. va_end(args);
  465. checkException();
  466. return result;
  467. }
  468. jobject CallObjectMethod(jobject obj, jmethodID methodID, ...)
  469. {
  470. va_list args;
  471. jobject result;
  472. va_start(args,methodID);
  473. result = JNIEnv::CallObjectMethodV(obj,methodID,args);
  474. va_end(args);
  475. checkException();
  476. return result;
  477. }
  478. jchar CallCharMethodA(jobject obj, jmethodID methodID,
  479. const jvalue * args) {
  480. return checkException(functions->CallCharMethodA(this,obj,methodID,args));
  481. }
  482. jboolean CallBooleanMethodA(jobject obj, jmethodID methodID,
  483. const jvalue * args) {
  484. return checkException(functions->CallBooleanMethodA(this,obj,methodID, args));
  485. }
  486. jshort CallShortMethodA(jobject obj, jmethodID methodID,
  487. const jvalue * args) {
  488. return checkException(functions->CallShortMethodA(this,obj,methodID,args));
  489. }
  490. jlong CallLongMethodA(jobject obj, jmethodID methodID,
  491. const jvalue * args) {
  492. return checkException(functions->CallLongMethodA(this,obj,methodID,args));
  493. }
  494. jfloat CallFloatMethodA(jobject obj, jmethodID methodID,
  495. const jvalue * args) {
  496. return checkException(functions->CallFloatMethodA(this,obj,methodID,args));
  497. }
  498. jdouble CallDoubleMethodA(jobject obj, jmethodID methodID,
  499. const jvalue * args) {
  500. return checkException(functions->CallDoubleMethodA(this,obj,methodID,args));
  501. }
  502. jint CallIntMethodA(jobject obj, jmethodID methodID,
  503. const jvalue * args) {
  504. return checkException(functions->CallIntMethodA(this,obj,methodID,args));
  505. }
  506. jbyte CallByteMethodA(jobject obj, jmethodID methodID,
  507. const jvalue * args) {
  508. return checkException(functions->CallByteMethodA(this,obj,methodID,args));
  509. }
  510. jobject CallObjectMethodA(jobject obj, jmethodID methodID,
  511. const jvalue * args) {
  512. return checkException(functions->CallObjectMethodA(this,obj,methodID,args));
  513. }
  514. void CallVoidMethodA(jobject obj, jmethodID methodID,
  515. const jvalue * args) {
  516. functions->CallVoidMethodA(this,obj,methodID,args);
  517. return checkException();
  518. }
  519. jchar CallStaticCharMethodA(jclass clazz, jmethodID methodID,
  520. const jvalue * args) {
  521. return checkException(functions->CallStaticCharMethodA(this,clazz,methodID,args));
  522. }
  523. jboolean CallStaticBooleanMethodA(jclass clazz, jmethodID methodID,
  524. const jvalue * args) {
  525. return checkException(functions->CallStaticBooleanMethodA(this,clazz,methodID, args));
  526. }
  527. jshort CallStaticShortMethodA(jclass clazz, jmethodID methodID,
  528. const jvalue * args) {
  529. return checkException(functions->CallStaticShortMethodA(this,clazz,methodID,args));
  530. }
  531. jlong CallStaticLongMethodA(jclass clazz, jmethodID methodID,
  532. const jvalue * args) {
  533. return checkException(functions->CallStaticLongMethodA(this,clazz,methodID,args));
  534. }
  535. jfloat CallStaticFloatMethodA(jclass clazz, jmethodID methodID,
  536. const jvalue * args) {
  537. return checkException(functions->CallStaticFloatMethodA(this,clazz,methodID,args));
  538. }
  539. jdouble CallStaticDoubleMethodA(jclass clazz, jmethodID methodID,
  540. const jvalue * args) {
  541. return checkException(functions->CallStaticDoubleMethodA(this,clazz,methodID,args));
  542. }
  543. jint CallStaticIntMethodA(jclass clazz, jmethodID methodID,
  544. const jvalue * args) {
  545. return checkException(functions->CallStaticIntMethodA(this,clazz,methodID,args));
  546. }
  547. jbyte CallStaticByteMethodA(jclass clazz, jmethodID methodID,
  548. const jvalue * args) {
  549. return checkException(functions->CallStaticByteMethodA(this,clazz,methodID,args));
  550. }
  551. jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID,
  552. const jvalue * args) {
  553. return checkException(functions->CallStaticObjectMethodA(this,clazz,methodID,args));
  554. }
  555. void CallStaticVoidMethodA(jclass clazz, jmethodID methodID,
  556. const jvalue * args) {
  557. functions->CallStaticVoidMethodA(this,clazz,methodID,args);
  558. return checkException();
  559. }
  560. jobject NewObjectA(jclass clazz, jmethodID methodID, const jvalue *args)
  561. {
  562. return checkException(JNIEnv::NewObjectA(clazz,methodID,args));
  563. }
  564. jobject NewObject(jclass clazz, jmethodID methodID, ...)
  565. {
  566. va_list args;
  567. jobject result;
  568. va_start(args, methodID);
  569. result = JNIEnv::NewObjectV(clazz,methodID,args);
  570. va_end(args);
  571. checkException();
  572. return result;
  573. }
  574. jstring NewStringUTF(const char *utf)
  575. {
  576. return checkException(JNIEnv::NewStringUTF(utf));
  577. }
  578. jfieldID FromReflectedField(jobject field)
  579. {
  580. return checkException(JNIEnv::FromReflectedField(field));
  581. }
  582. using JNIEnv::PushLocalFrame;
  583. using JNIEnv::PopLocalFrame;
  584. using JNIEnv::DeleteLocalRef;
  585. using JNIEnv::ExceptionClear;
  586. using JNIEnv::ExceptionCheck;
  587. using JNIEnv::GetObjectRefType;
  588. #ifdef TRACE_GLOBALREF
  589. void DeleteGlobalRef(jobject val)
  590. {
  591. DBGLOG("DeleteGlobalRef %p", val);
  592. JNIEnv::DeleteGlobalRef(val);
  593. #ifdef FORCE_GC
  594. forceGC(this);
  595. #endif
  596. }
  597. jobject NewGlobalRef(jobject val, const char *why)
  598. {
  599. jobject ret = JNIEnv::NewGlobalRef(val);
  600. DBGLOG("NewGlobalRef %p (%s) returns %p", val, why, ret);
  601. return ret;
  602. }
  603. #else
  604. inline void DeleteGlobalRef(jobject val)
  605. {
  606. JNIEnv::DeleteGlobalRef(val);
  607. #ifdef FORCE_GC
  608. forceGC(this);
  609. #endif
  610. }
  611. inline jobject NewGlobalRef(jobject val, const char *)
  612. {
  613. return JNIEnv::NewGlobalRef(val);
  614. }
  615. #endif
  616. };
  617. static bool printNameForClass(CheckedJNIEnv *JNIenv, jobject clsObj)
  618. {
  619. if (!clsObj)
  620. {
  621. printf("Object %p is null\n", clsObj);
  622. return false;
  623. }
  624. jclass cls = JNIenv->GetObjectClass(clsObj);
  625. jmethodID mid = JNIenv->GetMethodID(cls, "getName", "()Ljava/lang/String;");
  626. jstring strObj = (jstring) JNIenv->CallObjectMethod(clsObj, mid);
  627. const char* str = JNIenv->GetStringUTFChars(strObj, NULL);
  628. printf("class %s\n", str);
  629. bool ret = streq(str, "java.lang.Class");
  630. JNIenv->ReleaseStringUTFChars(strObj, str);
  631. return ret;
  632. }
  633. static void printClassForObject(CheckedJNIEnv *JNIenv, jobject obj)
  634. {
  635. printf("Object %p ", obj);
  636. if (!obj)
  637. {
  638. printf("is null\n");
  639. return;
  640. }
  641. jclass objClass = JNIenv->GetObjectClass(obj);
  642. jmethodID mid = JNIenv->GetMethodID(objClass, "getClass", "()Ljava/lang/Class;");
  643. jobject clsObj = JNIenv->CallObjectMethod(obj, mid);
  644. if (printNameForClass(JNIenv, clsObj))
  645. {
  646. printf(" ");
  647. printNameForClass(JNIenv, obj);
  648. }
  649. }
  650. static StringBuffer &getClassNameForObject(CheckedJNIEnv *JNIenv, StringBuffer &ret, jobject obj)
  651. {
  652. if (obj)
  653. {
  654. jclass objClass = JNIenv->GetObjectClass(obj);
  655. jmethodID mid = JNIenv->GetMethodID(objClass, "getClass", "()Ljava/lang/Class;");
  656. jobject clsObj = JNIenv->CallObjectMethod(obj, mid);
  657. jclass cls = JNIenv->GetObjectClass(clsObj);
  658. mid = JNIenv->GetMethodID(cls, "getName", "()Ljava/lang/String;");
  659. jstring strObj = (jstring) JNIenv->CallObjectMethod(clsObj, mid);
  660. const char* str = JNIenv->GetStringUTFChars(strObj, NULL);
  661. ret.append(str);
  662. JNIenv->ReleaseStringUTFChars(strObj, str);
  663. }
  664. return ret;
  665. }
  666. static jobject getClassLoader(CheckedJNIEnv *JNIenv, jclass obj)
  667. {
  668. jclass objClass = JNIenv->GetObjectClass(obj);
  669. jmethodID mid = JNIenv->GetMethodID(objClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
  670. jobject classloader = JNIenv->CallObjectMethod(obj, mid);
  671. return classloader;
  672. }
  673. static StringBuffer helperLibraryName;
  674. static CheckedJNIEnv *queryJNIEnv();
  675. // Some global objects setup at load time for efficiency and code readability
  676. static jclass customLoaderClass;
  677. static jmethodID clc_newInstance;
  678. static jmethodID clc_getSignature;
  679. static jclass hpccIteratorClass;
  680. static jmethodID hi_constructor;
  681. static jmethodID hi_load;
  682. static jclass utilIteratorClass;
  683. static jclass langIterableClass;
  684. static jmethodID iterable_iterator;
  685. static jclass systemClass;
  686. static jmethodID system_gc;
  687. static jclass javaLangClassLoaderClass;
  688. static jmethodID cl_getSystemClassLoader;
  689. static jclass javaLangThreadClass;
  690. static jmethodID thread_currentThread;
  691. static jmethodID thread_getContextClassLoader;
  692. static jmethodID thread_setContextClassLoader;
  693. static jclass langObjectClass;
  694. static jmethodID object_toString;
  695. static jclass arrayListClass;
  696. static jmethodID arrayList_toArray;
  697. static jmethodID arrayList_constructor;
  698. static jmethodID arrayList_add;
  699. static jclass langStringClass;
  700. static jclass netURLClass;
  701. static jmethodID netURL_constructor;
  702. static jclass throwableClass;
  703. //static jmethodID throwable_toString; and others declared above
  704. static jclass stackTraceElementClass;
  705. static jclass langIllegalArgumentExceptionClass;
  706. static void forceGC(CheckedJNIEnv* JNIenv)
  707. {
  708. JNIenv->CallStaticVoidMethod(systemClass, system_gc);
  709. }
  710. static void setupGlobals(CheckedJNIEnv *J)
  711. {
  712. try
  713. {
  714. // Load this first as we can't report errors on the others sensibly if this one not loaded!
  715. throwableClass = J->FindGlobalClass("java/lang/Throwable");
  716. throwable_toString = J->GetMethodID(throwableClass, "toString", "()Ljava/lang/String;");
  717. throwable_getStackTrace = J->GetMethodID(throwableClass, "getStackTrace", "()[Ljava/lang/StackTraceElement;");
  718. throwable_getCause = J->GetMethodID(throwableClass, "getCause", "()Ljava/lang/Throwable;");
  719. stackTraceElementClass = J->FindGlobalClass("java/lang/StackTraceElement");
  720. frame_toString = J->GetMethodID(stackTraceElementClass, "toString", "()Ljava/lang/String;");
  721. systemClass = J->FindGlobalClass("java/lang/System");
  722. system_gc = J->GetStaticMethodID(systemClass, "gc", "()V");
  723. javaLangClassLoaderClass = J->FindGlobalClass("java/lang/ClassLoader");
  724. cl_getSystemClassLoader = J->GetStaticMethodID(javaLangClassLoaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
  725. javaLangThreadClass = J->FindGlobalClass("java/lang/Thread");
  726. thread_currentThread = J->GetStaticMethodID(javaLangThreadClass, "currentThread", "()Ljava/lang/Thread;");
  727. thread_getContextClassLoader = J->GetMethodID(javaLangThreadClass, "getContextClassLoader", "()Ljava/lang/ClassLoader;");
  728. thread_setContextClassLoader = J->GetMethodID(javaLangThreadClass, "setContextClassLoader", "(Ljava/lang/ClassLoader;)V");
  729. langObjectClass = J->FindGlobalClass("java/lang/Object");
  730. object_toString = J->GetMethodID(langObjectClass, "toString", "()Ljava/lang/String;");
  731. arrayListClass = J->FindGlobalClass("java/util/ArrayList");
  732. arrayList_constructor = J->GetMethodID(arrayListClass, "<init>", "()V");
  733. arrayList_add = J->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
  734. arrayList_toArray = J->GetMethodID(arrayListClass, "toArray", "()[Ljava/lang/Object;" );
  735. langStringClass = J->FindGlobalClass("java/lang/String");
  736. langIterableClass = J->FindGlobalClass("java/lang/Iterable");
  737. iterable_iterator = J->GetMethodID(langIterableClass, "iterator", "()Ljava/util/Iterator;");
  738. utilIteratorClass = J->FindGlobalClass("java/util/Iterator");
  739. langIllegalArgumentExceptionClass = J->FindGlobalClass("java/lang/IllegalArgumentException");
  740. }
  741. catch (IException *E)
  742. {
  743. Owned<IException> e = E;
  744. throw makeWrappedExceptionV(E, E->errorCode(), "javaembed: Unable to load Java system classes - is classpath set properly?");
  745. }
  746. try
  747. {
  748. customLoaderClass = J->FindGlobalClass("com/HPCCSystems/HpccClassLoader");
  749. clc_newInstance = J->GetStaticMethodID(customLoaderClass, "newInstance","(Ljava/lang/String;Ljava/lang/ClassLoader;IJLjava/lang/String;)Lcom/HPCCSystems/HpccClassLoader;");
  750. clc_getSignature = J->GetStaticMethodID(customLoaderClass, "getSignature","(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/String;");
  751. hpccIteratorClass = J->FindGlobalClass("com/HPCCSystems/HpccUtils");
  752. hi_constructor = J->GetMethodID(hpccIteratorClass, "<init>", "(JLjava/lang/String;)V");
  753. hi_load = J->GetStaticMethodID(hpccIteratorClass, "load", "(Ljava/lang/String;)V");
  754. J->CallStaticVoidMethod(hpccIteratorClass, hi_load, J->NewStringUTF(helperLibraryName));
  755. }
  756. catch (IException *E)
  757. {
  758. Owned<IException> e = E;
  759. throw makeWrappedExceptionV(E, E->errorCode(), "javaembed: Unable to find HPCC classes - is classpath set properly?");
  760. }
  761. }
  762. static StringAttr & getSignature(StringAttr &ret, CheckedJNIEnv *J, jclass clazz, const char *funcName)
  763. {
  764. StringBuffer sig;
  765. jstring result = (jstring) J->CallStaticObjectMethod(customLoaderClass, clc_getSignature, clazz, J->NewStringUTF(funcName));
  766. J->getString(sig, result);
  767. sig.replace('.', '/');
  768. ret.set(sig);
  769. return ret;
  770. }
  771. /**
  772. * The following classes are used to ensure that the code in loadFunction that creates an instance
  773. * that is shared between multiple callers is only called on one thread, while other threads will wait
  774. * and use the instance created by the first thread.
  775. *
  776. */
  777. class PersistedObject : public MappingBase
  778. {
  779. public:
  780. PersistedObject(const char *_name) : name(_name) {}
  781. ~PersistedObject()
  782. {
  783. if (instance)
  784. {
  785. #ifdef TRACE_GLOBALREF
  786. DBGLOG("DeleteGlobalRef(singleton): %p", instance);
  787. #endif
  788. queryJNIEnv()->DeleteGlobalRef(instance);
  789. }
  790. }
  791. CriticalSection crit;
  792. jobject instance = nullptr;
  793. StringAttr name;
  794. virtual const void * getKey() const { return name; }
  795. };
  796. class PersistedObjectCriticalBlock
  797. {
  798. PersistedObject *obj = nullptr;
  799. public:
  800. inline PersistedObjectCriticalBlock()
  801. {
  802. }
  803. inline ~PersistedObjectCriticalBlock()
  804. {
  805. if (obj)
  806. obj->crit.leave();
  807. }
  808. inline void enter(PersistedObject *_obj)
  809. {
  810. // Note that the object should be locked before we are called
  811. assertex(!obj);
  812. obj = _obj;
  813. }
  814. inline void leave(jobject instance = nullptr)
  815. {
  816. if (obj)
  817. {
  818. if (instance)
  819. obj->instance = instance;
  820. obj->crit.leave();
  821. obj = nullptr;
  822. }
  823. }
  824. inline bool locked()
  825. {
  826. return obj != nullptr;
  827. }
  828. jobject getInstance()
  829. {
  830. assertex(obj);
  831. return obj->instance;
  832. }
  833. };
  834. // Use a global object to ensure that the Java VM is initialized once only.
  835. // We would like to create it lazily for two reasons:
  836. // 1. So that we only get a JVM if we need one (even if we have loaded the plugin)
  837. // 2. It's important for the JVM to be initialized AFTER we have set up signal handlers, as it
  838. // likes to set its own (in particular, it seems to intercept and ignore some SIGSEGV during the
  839. // garbage collection).
  840. // Unfortunately, it seems that the design of the JNI interface is such that JNI_CreateJavaVM has to be called on the 'main thread'.
  841. // 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
  842. // any engines that call InitModuleObjects() or load plugins dynamically do so AFTER setting any signal handlers or calling
  843. // EnableSEHtoExceptionMapping
  844. //
  845. static StringBuffer &appendClassPath(StringBuffer &classPath)
  846. {
  847. const IProperties &conf = queryEnvironmentConf();
  848. if (conf.hasProp("classpath"))
  849. {
  850. conf.getProp("classpath", classPath);
  851. classPath.append(ENVSEPCHAR);
  852. }
  853. else
  854. {
  855. classPath.append(INSTALL_DIR).append(PATHSEPCHAR).append("classes").append(ENVSEPCHAR);
  856. }
  857. return classPath;
  858. }
  859. static class JavaGlobalState
  860. {
  861. public:
  862. JavaGlobalState() : persistedObjects(false)
  863. {
  864. JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
  865. StringArray optionStrings;
  866. const char* origPath = getenv("CLASSPATH");
  867. StringBuffer newPath;
  868. newPath.append("-Djava.class.path=");
  869. if (origPath && *origPath)
  870. {
  871. newPath.append(origPath).append(ENVSEPCHAR);
  872. }
  873. appendClassPath(newPath);
  874. newPath.append(".");
  875. optionStrings.append(newPath);
  876. const IProperties &conf = queryEnvironmentConf();
  877. if (conf.hasProp("jvmlibpath"))
  878. {
  879. StringBuffer libPath;
  880. libPath.append("-Djava.library.path=");
  881. conf.getProp("jvmlibpath", libPath);
  882. optionStrings.append(libPath);
  883. }
  884. // Options we should set (but allow for override with jvmoptions below)
  885. optionStrings.append("-XX:-UseLargePages");
  886. if (conf.hasProp("jvmoptions"))
  887. {
  888. // Use space as field sep as ':' and ';' are valid
  889. optionStrings.appendList(conf.queryProp("jvmoptions"), " ");
  890. }
  891. // Options we know we always want set
  892. optionStrings.append("-Xrs");
  893. #ifdef RLIMIT_STACK
  894. // JVM has a habit of reducing the stack limit on main thread to 1M - probably dates back to when it was actually an increase...
  895. StringBuffer stackOption("-Xss");
  896. struct rlimit limit;
  897. rlim_t slim = 0;
  898. if (getrlimit (RLIMIT_STACK, &limit)==0)
  899. slim = limit.rlim_cur;
  900. if (!slim)
  901. slim = 8*1024*1024;
  902. if (slim >= 1*1024*1024)
  903. {
  904. stackOption.append((__uint64) slim);
  905. optionStrings.append(stackOption);
  906. }
  907. #endif
  908. // These may be useful for debugging
  909. #ifdef CHECK_JNI
  910. optionStrings.append("-Xcheck:jni");
  911. optionStrings.append("-verbose:jni");
  912. optionStrings.append("-XX:+TraceClassLoading");
  913. #endif
  914. JavaVMOption* options = new JavaVMOption[optionStrings.length()];
  915. ForEachItemIn(idx, optionStrings)
  916. {
  917. // DBGLOG("javaembed: Setting JVM option: %s",(char *)optionStrings.item(idx));
  918. options[idx].optionString = (char *) optionStrings.item(idx);
  919. options[idx].extraInfo = NULL;
  920. }
  921. vm_args.nOptions = optionStrings.length();
  922. vm_args.options = options;
  923. vm_args.ignoreUnrecognized = true;
  924. vm_args.version = JNI_VERSION_1_8;
  925. /* load and initialize a Java VM, return a JNI interface pointer in env */
  926. JNIEnv *env; /* receives pointer to native method interface */
  927. int createResult = JNI_CreateJavaVM(&javaVM, (void**)&env, &vm_args);
  928. delete [] options;
  929. if (createResult != 0)
  930. throw MakeStringException(0, "javaembed: Unable to initialize JVM (%d)",createResult);
  931. setupGlobals((CheckedJNIEnv *) env);
  932. // DBGLOG("JNI environment version %x loaded", env->GetVersion()); // Comes out a bit too early
  933. }
  934. ~JavaGlobalState()
  935. {
  936. /* We could release global persisted classes here but not a lot of point. Code would look something like this:
  937. HashIterator it(persistedObjects);
  938. ForEach(it)
  939. {
  940. IMapping &entry = it.query();
  941. jobject *pObj = persistedObjects.mapToValue(&entry);
  942. if (pClass)
  943. queryJNIEnv()->DeleteGlobalRef(*pObj);
  944. }
  945. */
  946. // This function is never called anyway...
  947. // We don't attempt to destroy the Java VM, as it's buggy...
  948. }
  949. PersistedObject *getGlobalObject(CheckedJNIEnv *JNIenv, const char *name)
  950. {
  951. PersistedObject *p;
  952. {
  953. CriticalBlock b(hashCrit);
  954. p = persistedObjects.find(name);
  955. if (!p)
  956. {
  957. p = new PersistedObject(name);
  958. persistedObjects.replaceOwn(*p);
  959. }
  960. }
  961. p->crit.enter(); // outside the hashCrit block, otherwise I think there is a possibility of deadlock
  962. return p;
  963. }
  964. void doUnregister(const char *key)
  965. {
  966. CriticalBlock b(hashCrit);
  967. persistedObjects.remove(key);
  968. }
  969. static void unregister(const char *key);
  970. JavaVM *javaVM; /* denotes a Java VM */
  971. private:
  972. CriticalSection hashCrit;
  973. StringMapOf<PersistedObject> persistedObjects;
  974. } *globalState;
  975. void JavaGlobalState::unregister(const char *key)
  976. {
  977. // Remove a class that was persisted via : PERSIST options - it has come to the end of its life
  978. globalState->doUnregister(key);
  979. }
  980. #ifdef _WIN32
  981. EXTERN_C IMAGE_DOS_HEADER __ImageBase;
  982. #endif
  983. MODULE_INIT(INIT_PRIORITY_STANDARD)
  984. {
  985. // Make sure we are never unloaded (as JVM does not support it)
  986. // we do this by doing a dynamic load of the javaembed library
  987. #ifdef _WIN32
  988. char ln[_MAX_PATH];
  989. ::GetModuleFileName((HINSTANCE)&__ImageBase, ln, _MAX_PATH);
  990. if (strstr(path, "javaembed"))
  991. {
  992. HINSTANCE h = LoadSharedObject(ln, false, false);
  993. helperLibraryName.set(ln);
  994. DBGLOG("LoadSharedObject returned %p", h);
  995. }
  996. #else
  997. if (findLoadedModule(helperLibraryName, "javaembed"))
  998. {
  999. HINSTANCE h = LoadSharedObject(helperLibraryName, false, false);
  1000. // Deliberately leak this handle
  1001. }
  1002. #endif
  1003. globalState = new JavaGlobalState;
  1004. return true;
  1005. }
  1006. MODULE_EXIT()
  1007. {
  1008. // We don't attempt to destroy the Java VM, as it's buggy...
  1009. // delete globalState;
  1010. // globalState = NULL;
  1011. }
  1012. static void checkType(type_t javatype, size32_t javasize, type_t ecltype, size32_t eclsize)
  1013. {
  1014. if (javatype != ecltype || javasize != eclsize)
  1015. throw MakeStringException(0, "javaembed: Type mismatch"); // MORE - could provide some details!
  1016. }
  1017. enum PersistMode
  1018. {
  1019. persistNone,
  1020. persistSupplied,
  1021. persistThread,
  1022. persistChannel,
  1023. persistWorkunit,
  1024. persistQuery,
  1025. persistGlobal
  1026. };
  1027. static PersistMode getPersistMode(const char *val, StringAttr &globalScope)
  1028. {
  1029. StringAttr trimmed;
  1030. const char *colon = strchr(val, ':');
  1031. if (colon)
  1032. {
  1033. globalScope.set(colon+1);
  1034. trimmed.set(val, colon-val);
  1035. val = trimmed;
  1036. }
  1037. if (isEmptyString(val) || strieq(val, "none"))
  1038. return persistNone;
  1039. else if (strieq(val, "thread"))
  1040. return persistThread;
  1041. else if (strieq(val, "channel"))
  1042. return persistChannel;
  1043. else if (strieq(val, "workunit"))
  1044. return persistWorkunit;
  1045. else if (strieq(val, "query"))
  1046. return persistQuery;
  1047. else if (strieq(val, "global"))
  1048. return persistGlobal;
  1049. else
  1050. throw MakeStringException(MSGAUD_user, 0, "javaembed: Unrecognized persist mode %s", val);
  1051. }
  1052. //-------------------------------------------
  1053. // A JavaObject accessor has common functionality shared by both the builders below (Java-> ECL and ECL->Java)
  1054. class JavaObjectAccessor : public CInterface
  1055. {
  1056. protected:
  1057. JavaObjectAccessor(CheckedJNIEnv *_JNIenv, const RtlFieldInfo *_outerRow, jobject _row)
  1058. : JNIenv(_JNIenv), row(_row), outerRow(_outerRow), idx(0), limit(0), inSet(false), inDataSet(false)
  1059. {
  1060. Class = (jclass) JNIenv->NewGlobalRef(JNIenv->GetObjectClass(row), "Class");
  1061. }
  1062. JavaObjectAccessor(CheckedJNIEnv *_JNIenv, const RtlFieldInfo *_outerRow, jclass _Class)
  1063. : JNIenv(_JNIenv), outerRow(_outerRow), idx(0), limit(0), inSet(false), inDataSet(false)
  1064. {
  1065. row = NULL;
  1066. Class = (jclass) JNIenv->NewGlobalRef(_Class, "Class");
  1067. }
  1068. ~JavaObjectAccessor()
  1069. {
  1070. // Unwind anything left on the stack (in case we had exceptions), to make sure the Class we release is the global one
  1071. if (stack.length())
  1072. Class = (jclass) stack.item(0);
  1073. if (Class)
  1074. JNIenv->DeleteGlobalRef(Class);
  1075. }
  1076. void push()
  1077. {
  1078. stack.append(Class);
  1079. stack.append(row);
  1080. }
  1081. void pop()
  1082. {
  1083. row = (jobject) stack.popGet();
  1084. Class = (jclass) stack.popGet();
  1085. }
  1086. jfieldID checkCharField(const RtlFieldInfo * field)
  1087. {
  1088. return JNIenv->GetFieldIDUnchecked(Class, field->name, inSet ? "[C" : "C");
  1089. }
  1090. jfieldID getFieldId(const RtlFieldInfo * field, const char *sig, const char *expected)
  1091. {
  1092. // MORE - if we are going to stream a dataset we really should be caching these somehow
  1093. try
  1094. {
  1095. jfieldID fieldId = 0;
  1096. if (sig)
  1097. {
  1098. if (inSet)
  1099. {
  1100. VStringBuffer arraySig("[%s", sig);
  1101. fieldId = JNIenv->GetFieldID(Class, field->name, arraySig.str());
  1102. }
  1103. else
  1104. fieldId = JNIenv->GetFieldID(Class, field->name, sig);
  1105. }
  1106. else
  1107. {
  1108. // Do it the hard way via reflection API
  1109. // Equivalent java:
  1110. // Field field = object.getClass().getDeclaredField(fieldName);
  1111. jclass classClass =JNIenv->GetObjectClass(Class);
  1112. jmethodID getDeclaredField = JNIenv->GetMethodID(classClass, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;" );
  1113. jstring fieldName = JNIenv->NewStringUTF(field->name);
  1114. jobject reflectedField = JNIenv->CallObjectMethod(Class, getDeclaredField, fieldName);
  1115. fieldId = JNIenv->FromReflectedField(reflectedField);
  1116. }
  1117. return fieldId;
  1118. }
  1119. catch (IException *E)
  1120. {
  1121. ::Release(E);
  1122. throw MakeStringException(0, "javaembed: Unable to retrieve field %s of type %s", field->name, expected);
  1123. }
  1124. }
  1125. CheckedJNIEnv *JNIenv;
  1126. jobject row;
  1127. const RtlFieldInfo *outerRow;
  1128. jclass Class;
  1129. ConstPointerArray stack;
  1130. unsigned idx;
  1131. UnsignedArray idxStack;
  1132. unsigned limit;
  1133. bool inSet;
  1134. bool inDataSet;
  1135. };
  1136. // A JavaRowBuilder object is used to construct an ECL row from a Java object
  1137. class JavaRowBuilder : public JavaObjectAccessor, implements IFieldSource
  1138. {
  1139. public:
  1140. IMPLEMENT_IINTERFACE;
  1141. JavaRowBuilder(CheckedJNIEnv *_JNIenv, const RtlFieldInfo *_outerRow, jobject _row)
  1142. : JavaObjectAccessor(_JNIenv, _outerRow, _row)
  1143. {
  1144. }
  1145. virtual bool getBooleanResult(const RtlFieldInfo *field)
  1146. {
  1147. jboolean b;
  1148. if (inSet)
  1149. {
  1150. JNIenv->GetBooleanArrayRegion((jbooleanArray) row, idx, 1, &b);
  1151. }
  1152. else
  1153. {
  1154. jfieldID fieldId = getFieldId(field, "Z", "boolean");
  1155. b = JNIenv->GetBooleanField(row, fieldId);
  1156. }
  1157. return b;
  1158. }
  1159. virtual void getDataResult(const RtlFieldInfo *field, size32_t &__len, void * &__result)
  1160. {
  1161. jbyteArray array;
  1162. if (inSet)
  1163. {
  1164. array = (jbyteArray) JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  1165. }
  1166. else
  1167. {
  1168. jfieldID fieldId = getFieldId(field, "[B", "DATA");
  1169. array = (jbyteArray) JNIenv->GetObjectField(row, fieldId);
  1170. }
  1171. __len = (array != NULL ? JNIenv->GetArrayLength(array) : 0);
  1172. __result = (__len > 0 ? rtlMalloc(__len) : NULL);
  1173. if (__result)
  1174. JNIenv->GetByteArrayRegion(array, 0, __len, (jbyte *) __result);
  1175. }
  1176. virtual double getRealResult(const RtlFieldInfo *field)
  1177. {
  1178. double d;
  1179. if (inSet)
  1180. {
  1181. float f;
  1182. switch (field->size(NULL, NULL))
  1183. {
  1184. case 4:
  1185. JNIenv->GetFloatArrayRegion((jfloatArray) row, idx, 1, &f);
  1186. d = f;
  1187. break;
  1188. case 8:
  1189. JNIenv->GetDoubleArrayRegion((jdoubleArray) row, idx, 1, &d);
  1190. break;
  1191. default:
  1192. throwUnexpected();
  1193. }
  1194. }
  1195. else
  1196. {
  1197. jfieldID fieldId;
  1198. switch (field->size(NULL, NULL))
  1199. {
  1200. case 4:
  1201. fieldId = getFieldId(field, "F", "float");
  1202. d = JNIenv->GetFloatField(row, fieldId);
  1203. break;
  1204. case 8:
  1205. fieldId = getFieldId(field, "D", "double");
  1206. d = JNIenv->GetDoubleField(row, fieldId);
  1207. break;
  1208. default:
  1209. throwUnexpected();
  1210. }
  1211. }
  1212. return d;
  1213. }
  1214. virtual __int64 getSignedResult(const RtlFieldInfo *field)
  1215. {
  1216. __int64 ret;
  1217. if (inSet)
  1218. {
  1219. jbyte b;
  1220. jshort s;
  1221. jint i;
  1222. jlong l;
  1223. switch (field->size(NULL, NULL))
  1224. {
  1225. case 1:
  1226. JNIenv->GetByteArrayRegion((jbyteArray) row, idx, 1, &b);
  1227. ret = b;
  1228. break;
  1229. case 2:
  1230. JNIenv->GetShortArrayRegion((jshortArray) row, idx, 1, &s);
  1231. ret = s;
  1232. break;
  1233. case 4:
  1234. JNIenv->GetIntArrayRegion((jintArray) row, idx, 1, &i);
  1235. ret = i;
  1236. break;
  1237. case 8:
  1238. JNIenv->GetLongArrayRegion((jlongArray) row, idx, 1, &l);
  1239. ret = l;
  1240. break;
  1241. default:
  1242. UNSUPPORTED("non-standard integer sizes");
  1243. }
  1244. }
  1245. else
  1246. {
  1247. jfieldID fieldId;
  1248. switch (field->size(NULL, NULL))
  1249. {
  1250. case 1:
  1251. fieldId = getFieldId(field, "B", "byte");
  1252. ret = JNIenv->GetByteField(row, fieldId);
  1253. break;
  1254. case 2:
  1255. fieldId = getFieldId(field, "S", "short");
  1256. ret = JNIenv->GetShortField(row, fieldId);
  1257. break;
  1258. case 4:
  1259. fieldId = getFieldId(field, "I", "int");
  1260. ret = JNIenv->GetIntField(row, fieldId);
  1261. break;
  1262. case 8:
  1263. fieldId = getFieldId(field, "J", "long");
  1264. ret = JNIenv->GetLongField(row, fieldId);
  1265. break;
  1266. default:
  1267. UNSUPPORTED("non-standard integer sizes");
  1268. }
  1269. }
  1270. return ret;
  1271. }
  1272. virtual unsigned __int64 getUnsignedResult(const RtlFieldInfo *field)
  1273. {
  1274. UNSUPPORTED("unsigned fields"); // No unsigned types in Java
  1275. }
  1276. virtual void getStringResult(const RtlFieldInfo *field, size32_t &__len, char * &__result)
  1277. {
  1278. jstring result;
  1279. if (inSet)
  1280. {
  1281. // MORE - set of string1 mapping to Java array of char ? Not sure it's worth it.
  1282. result = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  1283. }
  1284. else
  1285. {
  1286. if (field->isFixedSize() && field->size(NULL, NULL)==1)
  1287. {
  1288. // See if there's a char field
  1289. jfieldID charFieldId = checkCharField(field);
  1290. if (charFieldId)
  1291. {
  1292. jchar resultChar = JNIenv->GetCharField(row, charFieldId);
  1293. rtlUnicodeToStrX(__len, __result, 1, &resultChar);
  1294. return;
  1295. }
  1296. }
  1297. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  1298. result = (jstring) JNIenv->GetObjectField(row, fieldId);
  1299. }
  1300. if (!result)
  1301. {
  1302. __len = 0;
  1303. __result = NULL;
  1304. return;
  1305. }
  1306. size_t size = JNIenv->GetStringUTFLength(result); // in bytes
  1307. const char *text = JNIenv->GetStringUTFChars(result, NULL);
  1308. size32_t chars = rtlUtf8Length(size, text);
  1309. rtlUtf8ToStrX(__len, __result, chars, text);
  1310. JNIenv->ReleaseStringUTFChars(result, text);
  1311. JNIenv->DeleteLocalRef(result);
  1312. }
  1313. virtual void getUTF8Result(const RtlFieldInfo *field, size32_t &__len, char * &__result)
  1314. {
  1315. jstring result;
  1316. if (inSet)
  1317. {
  1318. // MORE - set of string1 mapping to Java array of char ? Not sure it's worth it.
  1319. result = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  1320. }
  1321. else
  1322. {
  1323. if (field->isFixedSize() && field->size(NULL, NULL)==1)
  1324. {
  1325. // See if there's a char field
  1326. jfieldID charFieldId = checkCharField(field);
  1327. if (charFieldId)
  1328. {
  1329. jchar resultChar = JNIenv->GetCharField(row, charFieldId);
  1330. rtlUnicodeToUtf8X(__len, __result, 1, &resultChar);
  1331. return;
  1332. }
  1333. }
  1334. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  1335. result = (jstring) JNIenv->GetObjectField(row, fieldId);
  1336. }
  1337. if (!result)
  1338. {
  1339. __len = 0;
  1340. __result = NULL;
  1341. return;
  1342. }
  1343. size_t size = JNIenv->GetStringUTFLength(result); // in bytes
  1344. const char *text = JNIenv->GetStringUTFChars(result, NULL);
  1345. size32_t chars = rtlUtf8Length(size, text);
  1346. rtlUtf8ToUtf8X(__len, __result, chars, text);
  1347. JNIenv->ReleaseStringUTFChars(result, text);
  1348. JNIenv->DeleteLocalRef(result);
  1349. }
  1350. virtual void getUnicodeResult(const RtlFieldInfo *field, size32_t &__len, UChar * &__result)
  1351. {
  1352. jstring result;
  1353. if (inSet)
  1354. {
  1355. // MORE - set of string1 mapping to Java array of char ? Not sure it's worth it.
  1356. result = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  1357. }
  1358. else
  1359. {
  1360. if (field->isFixedSize() && field->size(NULL, NULL)==1)
  1361. {
  1362. // See if there's a char field
  1363. jfieldID charFieldId = checkCharField(field);
  1364. if (charFieldId)
  1365. {
  1366. jchar resultChar = JNIenv->GetCharField(row, charFieldId);
  1367. rtlUnicodeToUnicodeX(__len, __result, 1, &resultChar);
  1368. return;
  1369. }
  1370. }
  1371. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  1372. result = (jstring) JNIenv->GetObjectField(row, fieldId);
  1373. }
  1374. if (!result)
  1375. {
  1376. __len = 0;
  1377. __result = NULL;
  1378. return;
  1379. }
  1380. size_t size = JNIenv->GetStringUTFLength(result); // in bytes
  1381. const char *text = JNIenv->GetStringUTFChars(result, NULL);
  1382. size32_t chars = rtlUtf8Length(size, text);
  1383. rtlUtf8ToUnicodeX(__len, __result, chars, text);
  1384. JNIenv->ReleaseStringUTFChars(result, text);
  1385. JNIenv->DeleteLocalRef(result);
  1386. }
  1387. virtual void getDecimalResult(const RtlFieldInfo *field, Decimal &value)
  1388. {
  1389. double ret = getRealResult(field);
  1390. value.setReal(ret);
  1391. }
  1392. virtual void processBeginSet(const RtlFieldInfo * field, bool &isAll)
  1393. {
  1394. isAll = false; // No concept of an 'all' set in Java
  1395. push();
  1396. jfieldID fieldId = getFieldId(field, NULL, "object"); // We assume it will be an array, but not sure of what...
  1397. row = JNIenv->GetObjectField(row, fieldId);
  1398. inSet = true;
  1399. idx = -1; // First call to next() increments it to 0
  1400. limit = row != NULL ? JNIenv->GetArrayLength((jarray) row) : 0;
  1401. }
  1402. virtual bool processNextSet(const RtlFieldInfo * field)
  1403. {
  1404. assertex(inSet);
  1405. idx++;
  1406. return idx < limit;
  1407. }
  1408. virtual void processBeginDataset(const RtlFieldInfo * field)
  1409. {
  1410. push();
  1411. jfieldID fieldId = getFieldId(field, NULL, "object"); // We assume it will be an array, but not sure of what...
  1412. row = JNIenv->GetObjectField(row, fieldId);
  1413. inDataSet = true;
  1414. idx = -1; // First call to next() increments it to 0
  1415. limit = row != NULL ? JNIenv->GetArrayLength((jarray) row) : 0;
  1416. }
  1417. virtual void processBeginRow(const RtlFieldInfo * field)
  1418. {
  1419. if (field != outerRow)
  1420. {
  1421. push();
  1422. if (inDataSet)
  1423. {
  1424. row = JNIenv->GetObjectArrayElement((jobjectArray) row, idx);
  1425. }
  1426. else
  1427. {
  1428. jfieldID fieldId = getFieldId(field, NULL, "object");
  1429. row = JNIenv->GetObjectField(row, fieldId);
  1430. }
  1431. if (!row)
  1432. rtlFail(0, "javaembed: child dataset object should not be NULL");
  1433. Class = JNIenv->GetObjectClass(row);
  1434. }
  1435. }
  1436. virtual bool processNextRow(const RtlFieldInfo * field)
  1437. {
  1438. assertex(inDataSet);
  1439. idx++;
  1440. return idx < limit;
  1441. }
  1442. virtual void processEndSet(const RtlFieldInfo * field)
  1443. {
  1444. inSet = false;
  1445. JNIenv->DeleteLocalRef(row);
  1446. pop();
  1447. }
  1448. virtual void processEndDataset(const RtlFieldInfo * field)
  1449. {
  1450. inDataSet = false;
  1451. JNIenv->DeleteLocalRef(row);
  1452. pop();
  1453. }
  1454. virtual void processEndRow(const RtlFieldInfo * field)
  1455. {
  1456. if (field != outerRow)
  1457. {
  1458. JNIenv->DeleteLocalRef(row);
  1459. JNIenv->DeleteLocalRef(Class);
  1460. pop();
  1461. }
  1462. }
  1463. };
  1464. //-------------------------------------------
  1465. // A JavaObjectBuilder object is used to construct a Java object from an ECL row
  1466. class JavaObjectBuilder : public JavaObjectAccessor, implements IFieldProcessor
  1467. {
  1468. public:
  1469. IMPLEMENT_IINTERFACE;
  1470. JavaObjectBuilder(CheckedJNIEnv *_JNIenv, const RtlFieldInfo *_outerRow, jclass _Class)
  1471. : JavaObjectAccessor(_JNIenv, _outerRow, _Class)
  1472. {
  1473. setConstructor();
  1474. }
  1475. virtual void processString(unsigned numchars, const char *text, const RtlFieldInfo * field)
  1476. {
  1477. if (field->isFixedSize() && field->size(NULL, NULL)==1 && !inSet) // SET OF STRING1 is not mapped to array of char...
  1478. {
  1479. // See if there's a char field
  1480. jfieldID charFieldId = checkCharField(field);
  1481. if (charFieldId)
  1482. {
  1483. assertex(numchars==1);
  1484. jchar c;
  1485. rtlStrToUnicode(1, &c, 1, text);
  1486. JNIenv->SetCharField(row, charFieldId, c);
  1487. return;
  1488. }
  1489. }
  1490. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  1491. size32_t numchars16;
  1492. rtlDataAttr unicode16;
  1493. rtlStrToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
  1494. jstring value = JNIenv->NewString(unicode16.getustr(), numchars16);
  1495. if (inSet)
  1496. JNIenv->SetObjectArrayElement((jobjectArray) row, idx, value);
  1497. else
  1498. JNIenv->SetObjectField(row, fieldId, value);
  1499. JNIenv->DeleteLocalRef(value);
  1500. }
  1501. virtual void processBool(bool value, const RtlFieldInfo * field)
  1502. {
  1503. jfieldID fieldId = getFieldId(field, "Z", "boolean");
  1504. JNIenv->SetBooleanField(row, fieldId, value);
  1505. }
  1506. virtual void processData(unsigned len, const void *value, const RtlFieldInfo * field)
  1507. {
  1508. jfieldID fieldId = getFieldId(field, "[B", "data");
  1509. jbyteArray javaData = JNIenv->NewByteArray(len);
  1510. JNIenv->SetByteArrayRegion(javaData, 0, len, (jbyte *) value);
  1511. if (inSet)
  1512. JNIenv->SetObjectArrayElement((jobjectArray) row, idx, javaData);
  1513. else
  1514. JNIenv->SetObjectField(row, fieldId, javaData);
  1515. }
  1516. virtual void processInt(__int64 value, const RtlFieldInfo * field)
  1517. {
  1518. jfieldID fieldId;
  1519. switch (field->size(NULL, NULL))
  1520. {
  1521. case 1:
  1522. fieldId = getFieldId(field, "B", "byte");
  1523. JNIenv->SetByteField(row, fieldId, value);
  1524. break;
  1525. case 2:
  1526. fieldId = getFieldId(field, "S", "short");
  1527. JNIenv->SetShortField(row, fieldId, value);
  1528. break;
  1529. case 4:
  1530. fieldId = getFieldId(field, "I", "int");
  1531. JNIenv->SetIntField(row, fieldId, value);
  1532. break;
  1533. case 8:
  1534. fieldId = getFieldId(field, "J", "long");
  1535. JNIenv->SetLongField(row, fieldId, value);
  1536. break;
  1537. default:
  1538. UNSUPPORTED("non-standard integer sizes");
  1539. break;
  1540. }
  1541. }
  1542. virtual void processUInt(unsigned __int64 value, const RtlFieldInfo * field)
  1543. {
  1544. UNSUPPORTED("unsigned fields"); // No unsigned types in Java
  1545. }
  1546. virtual void processReal(double value, const RtlFieldInfo * field)
  1547. {
  1548. jfieldID fieldId;
  1549. switch (field->size(NULL, NULL))
  1550. {
  1551. case 4:
  1552. fieldId = getFieldId(field, "F", "float");
  1553. JNIenv->SetFloatField(row, fieldId, (float) value);
  1554. break;
  1555. case 8:
  1556. fieldId = getFieldId(field, "D", "double");
  1557. JNIenv->SetDoubleField(row, fieldId, value);
  1558. break;
  1559. default:
  1560. throwUnexpected();
  1561. }
  1562. }
  1563. virtual void processDecimal(const void *value, unsigned digits, unsigned precision, const RtlFieldInfo * field)
  1564. {
  1565. // we could map to doubles, but probably better to let the ECL programmer do that themselves
  1566. UNSUPPORTED("DECIMAL fields");
  1567. }
  1568. virtual void processUDecimal(const void *value, unsigned digits, unsigned precision, const RtlFieldInfo * field)
  1569. {
  1570. UNSUPPORTED("UDECIMAL fields");
  1571. }
  1572. virtual void processUnicode(unsigned numchars, const UChar *text, const RtlFieldInfo * field)
  1573. {
  1574. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  1575. jstring value = JNIenv->NewString(text, numchars);
  1576. if (inSet)
  1577. JNIenv->SetObjectArrayElement((jobjectArray) row, idx, value);
  1578. else
  1579. JNIenv->SetObjectField(row, fieldId, value);
  1580. JNIenv->DeleteLocalRef(value);
  1581. }
  1582. virtual void processQString(unsigned len, const char *value, const RtlFieldInfo * field)
  1583. {
  1584. size32_t charCount;
  1585. rtlDataAttr text;
  1586. rtlQStrToStrX(charCount, text.refstr(), len, value);
  1587. processString(charCount, text.getstr(), field);
  1588. }
  1589. virtual void processUtf8(unsigned numchars, const char *text, const RtlFieldInfo * field)
  1590. {
  1591. jfieldID fieldId = getFieldId(field, "Ljava/lang/String;", "String");
  1592. size32_t numchars16;
  1593. rtlDataAttr unicode16;
  1594. rtlUtf8ToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
  1595. jstring value = JNIenv->NewString(unicode16.getustr(), numchars16);
  1596. if (inSet)
  1597. JNIenv->SetObjectArrayElement((jobjectArray) row, idx, value);
  1598. else
  1599. JNIenv->SetObjectField(row, fieldId, value);
  1600. JNIenv->DeleteLocalRef(value);
  1601. }
  1602. virtual bool processBeginSet(const RtlFieldInfo * field, unsigned numElems, bool isAll, const byte *data)
  1603. {
  1604. push();
  1605. idx = 0;
  1606. limit = numElems;
  1607. const char *javaTypeSignature = NULL;
  1608. bool processElements = false;
  1609. // row needs to be created as an array of <whatever>
  1610. if (isAll)
  1611. UNSUPPORTED("ALL sets");
  1612. const RtlTypeInfo *childType = field->type->queryChildType();
  1613. jobject newRow;
  1614. switch(childType->fieldType & RFTMkind)
  1615. {
  1616. case type_boolean:
  1617. newRow = JNIenv->NewBooleanArray(numElems);
  1618. JNIenv->SetBooleanArrayRegion((jbooleanArray) newRow, 0, numElems, (jboolean *) data);
  1619. javaTypeSignature = "[Z";
  1620. break;
  1621. case type_int:
  1622. if (childType->fieldType & RFTMunsigned)
  1623. UNSUPPORTED("unsigned integers");
  1624. switch (childType->length)
  1625. {
  1626. case 1:
  1627. newRow = JNIenv->NewByteArray(numElems);
  1628. JNIenv->SetByteArrayRegion((jbyteArray) newRow, 0, numElems, (jbyte *) data);
  1629. javaTypeSignature = "[B";
  1630. break;
  1631. case 2:
  1632. newRow = JNIenv->NewShortArray(numElems);
  1633. JNIenv->SetShortArrayRegion((jshortArray) newRow, 0, numElems, (jshort *) data);
  1634. javaTypeSignature = "[S";
  1635. break;
  1636. case 4:
  1637. newRow = JNIenv->NewIntArray(numElems);
  1638. JNIenv->SetIntArrayRegion((jintArray) newRow, 0, numElems, (jint *) data);
  1639. javaTypeSignature = "[I";
  1640. break;
  1641. case 8:
  1642. newRow = JNIenv->NewLongArray(numElems);
  1643. JNIenv->SetLongArrayRegion((jlongArray) newRow, 0, numElems, (jlong *) data);
  1644. javaTypeSignature = "[J";
  1645. break;
  1646. default:
  1647. UNSUPPORTED("non-standard integer sizes");
  1648. break;
  1649. }
  1650. break;
  1651. case type_real:
  1652. switch (childType->length)
  1653. {
  1654. case 4:
  1655. newRow = JNIenv->NewFloatArray(numElems);
  1656. JNIenv->SetFloatArrayRegion((jfloatArray) newRow, 0, numElems, (float *) data);
  1657. javaTypeSignature = "[F";
  1658. break;
  1659. case 8:
  1660. newRow = JNIenv->NewDoubleArray(numElems);
  1661. JNIenv->SetDoubleArrayRegion((jdoubleArray) newRow, 0, numElems, (double *) data);
  1662. javaTypeSignature = "[D";
  1663. break;
  1664. default:
  1665. throwUnexpected();
  1666. break;
  1667. }
  1668. break;
  1669. case type_string:
  1670. case type_varstring:
  1671. case type_unicode:
  1672. case type_utf8:
  1673. newRow = JNIenv->NewObjectArray(numElems, langStringClass, NULL);
  1674. javaTypeSignature = "[Ljava/lang/String;";
  1675. processElements = true;
  1676. break;
  1677. case type_data:
  1678. newRow = JNIenv->NewObjectArray(numElems, JNIenv->FindClass("[B"), NULL);
  1679. javaTypeSignature = "[[B";
  1680. processElements = true;
  1681. break;
  1682. default:
  1683. throwUnexpected();
  1684. }
  1685. jfieldID fieldId = getFieldId(field, javaTypeSignature, "Array");
  1686. JNIenv->SetObjectField(row, fieldId, newRow);
  1687. row = newRow;
  1688. inSet = true;
  1689. return processElements;
  1690. }
  1691. virtual bool processBeginDataset(const RtlFieldInfo * field, unsigned numRows)
  1692. {
  1693. push();
  1694. idxStack.append(idx);
  1695. idx = 0;
  1696. inDataSet = true;
  1697. // Create an empty array
  1698. jfieldID childId = getFieldId( field, NULL, "RECORD");
  1699. jobject newRow = NULL;
  1700. if (numRows)
  1701. {
  1702. jclass arrayClass = getClassForChild(childId);
  1703. jmethodID isArrayMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(arrayClass), "isArray", "()Z" );
  1704. if (!JNIenv->CallBooleanMethod(arrayClass, isArrayMethod))
  1705. {
  1706. JNIenv->ExceptionClear();
  1707. VStringBuffer message("javaembed: Array expected for field %s", field->name);
  1708. rtlFail(0, message.str());
  1709. }
  1710. // Set up constructor etc for the child rows, so we don't do it per row
  1711. jmethodID getTypeMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(arrayClass), "getComponentType", "()Ljava/lang/Class;" );
  1712. Class = (jclass) JNIenv->CallObjectMethod(arrayClass, getTypeMethod);
  1713. setConstructor();
  1714. // Now we need to create the array
  1715. newRow = JNIenv->NewObjectArray(numRows, Class, NULL);
  1716. }
  1717. JNIenv->SetObjectField(row, childId, newRow);
  1718. row = newRow;
  1719. return true;
  1720. }
  1721. virtual bool processBeginRow(const RtlFieldInfo * field)
  1722. {
  1723. if (field == outerRow)
  1724. row = JNIenv->NewObject(Class, constructor);
  1725. else
  1726. {
  1727. push();
  1728. stack.append(constructor);
  1729. // Now we have to create the child object
  1730. jobject newRow = NULL;
  1731. if (inDataSet)
  1732. {
  1733. newRow = JNIenv->NewObject(Class, constructor);
  1734. JNIenv->SetObjectArrayElement((jobjectArray) row, idx++, newRow);
  1735. }
  1736. else
  1737. {
  1738. // All this is done once per dataset in the nested dataset case. But for embedded record case we have to do it here
  1739. jfieldID childId = getFieldId( field, NULL, "RECORD");
  1740. Class = getClassForChild(childId);
  1741. setConstructor();
  1742. newRow = JNIenv->NewObject(Class, constructor);
  1743. JNIenv->SetObjectField(row, childId, newRow);
  1744. }
  1745. row = newRow;
  1746. }
  1747. return true;
  1748. }
  1749. virtual void processEndSet(const RtlFieldInfo * field)
  1750. {
  1751. JNIenv->DeleteLocalRef(row);
  1752. pop();
  1753. inSet = false;
  1754. }
  1755. virtual void processEndDataset(const RtlFieldInfo * field)
  1756. {
  1757. inDataSet = false;
  1758. idx = idxStack.popGet();
  1759. pop();
  1760. }
  1761. virtual void processEndRow(const RtlFieldInfo * field)
  1762. {
  1763. if (field != outerRow)
  1764. {
  1765. constructor = (jmethodID) stack.popGet();
  1766. JNIenv->DeleteLocalRef(row);
  1767. pop();
  1768. }
  1769. }
  1770. inline jobject getObject()
  1771. {
  1772. return row;
  1773. }
  1774. protected:
  1775. jclass getClassForChild(jfieldID childId)
  1776. {
  1777. jobject reflectedField = JNIenv->ToReflectedField(Class, childId, false);
  1778. jclass fieldClass =JNIenv->GetObjectClass(reflectedField);
  1779. jmethodID getTypeMethod = JNIenv->GetMethodID(fieldClass, "getType", "()Ljava/lang/Class;" );
  1780. jclass result = (jclass) JNIenv->CallObjectMethod(reflectedField, getTypeMethod);
  1781. JNIenv->DeleteLocalRef(reflectedField);
  1782. JNIenv->DeleteLocalRef(fieldClass);
  1783. return result;
  1784. }
  1785. void setConstructor()
  1786. {
  1787. constructor = JNIenv->GetMethodIDUnchecked(Class, "<init>", "()V");
  1788. if (!constructor)
  1789. {
  1790. jmethodID getNameMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(Class), "getName", "()Ljava/lang/String;" );
  1791. jstring name = (jstring) JNIenv->CallObjectMethod(Class, getNameMethod);
  1792. const char *nameText = JNIenv->GetStringUTFChars(name, NULL);
  1793. VStringBuffer message("javaembed: no suitable constructor for class %s", nameText);
  1794. JNIenv->ReleaseStringUTFChars(name, nameText);
  1795. rtlFail(0, message.str());
  1796. }
  1797. }
  1798. jmethodID constructor;
  1799. };
  1800. //----------------------------------------------------------------------
  1801. // Wrap an IRowStream into a Java Iterator
  1802. class ECLDatasetIterator : public CInterfaceOf<IInterface>
  1803. {
  1804. public:
  1805. ECLDatasetIterator(CheckedJNIEnv *JNIenv, const RtlTypeInfo *_typeInfo, jclass className, IRowStream * _val)
  1806. : typeInfo(_typeInfo), val(_val),
  1807. dummyField("<row>", NULL, typeInfo),
  1808. javaBuilder(JNIenv, &dummyField, className)
  1809. {
  1810. nextRead = false;
  1811. }
  1812. bool hasNext()
  1813. {
  1814. if (!nextRead)
  1815. {
  1816. nextPending.setown(val->ungroupedNextRow());
  1817. nextRead = true;
  1818. if (!nextPending)
  1819. val->stop();
  1820. }
  1821. return nextPending != NULL;
  1822. }
  1823. jobject next()
  1824. {
  1825. if (!hasNext())
  1826. return NULL;
  1827. typeInfo->process((const byte *) nextPending.get(), (const byte *) nextPending.get(), &dummyField, javaBuilder); // Creates a java object from the incoming ECL row
  1828. nextRead = false;
  1829. return javaBuilder.getObject();
  1830. }
  1831. protected:
  1832. const RtlTypeInfo *typeInfo; // Not linked (or linkable)
  1833. Linked<IRowStream> val;
  1834. RtlFieldStrInfo dummyField;
  1835. JavaObjectBuilder javaBuilder;
  1836. roxiemem::OwnedConstRoxieRow nextPending;
  1837. bool nextRead;
  1838. };
  1839. //-------------------------------------------
  1840. // A Java function that returns a dataset will return a JavaRowStream object that can be
  1841. // interrogated to return each row of the result in turn
  1842. class JavaLocalFrame
  1843. {
  1844. public:
  1845. JavaLocalFrame(CheckedJNIEnv *_JNIenv, unsigned size = 16) : JNIenv(_JNIenv)
  1846. {
  1847. JNIenv->PushLocalFrame(size);
  1848. }
  1849. ~JavaLocalFrame()
  1850. {
  1851. JNIenv->PopLocalFrame(NULL);
  1852. }
  1853. private:
  1854. CheckedJNIEnv *JNIenv;
  1855. };
  1856. class JavaRowStream : public CInterfaceOf<IRowStream>
  1857. {
  1858. public:
  1859. JavaRowStream(jobject _iterator, IEngineRowAllocator *_resultAllocator)
  1860. : resultAllocator(_resultAllocator)
  1861. {
  1862. CheckedJNIEnv *JNIenv = queryJNIEnv();
  1863. iterator = JNIenv->NewGlobalRef(_iterator, "iterator");
  1864. iterClass = (jclass) JNIenv->NewGlobalRef(JNIenv->GetObjectClass(iterator), "iterClass");
  1865. hasNextMethod = JNIenv->GetMethodID(iterClass, "hasNext", "()Z" );
  1866. nextMethod = JNIenv->GetMethodID(iterClass, "next", "()Ljava/lang/Object;" );
  1867. // Note that we can't save the JNIEnv value here - calls may be made on different threads (though not at the same time).
  1868. }
  1869. ~JavaRowStream()
  1870. {
  1871. stop();
  1872. }
  1873. virtual const void *nextRow()
  1874. {
  1875. if (!iterator)
  1876. return NULL;
  1877. CheckedJNIEnv *JNIenv = queryJNIEnv();
  1878. JavaLocalFrame lf(JNIenv);
  1879. // Java code would be
  1880. // if (!iterator.hasNext)
  1881. // {
  1882. // stop();
  1883. // return NULL;
  1884. // }
  1885. // result = iterator.next();
  1886. jboolean hasNext = JNIenv->CallBooleanMethod(iterator, hasNextMethod);
  1887. if (!hasNext)
  1888. {
  1889. stop();
  1890. return NULL;
  1891. }
  1892. jobject result = JNIenv->CallObjectMethod(iterator, nextMethod);
  1893. RtlDynamicRowBuilder rowBuilder(resultAllocator);
  1894. const RtlTypeInfo *typeInfo = resultAllocator->queryOutputMeta()->queryTypeInfo();
  1895. assertex(typeInfo);
  1896. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  1897. JavaRowBuilder javaRowBuilder(queryJNIEnv(), &dummyField, result);
  1898. size32_t len = typeInfo->build(rowBuilder, 0, &dummyField, javaRowBuilder);
  1899. return rowBuilder.finalizeRowClear(len);
  1900. }
  1901. virtual void stop()
  1902. {
  1903. resultAllocator.clear();
  1904. CheckedJNIEnv *JNIenv = queryJNIEnv();
  1905. if (JNIenv)
  1906. {
  1907. if (iterator)
  1908. {
  1909. JNIenv->DeleteGlobalRef(iterator);
  1910. iterator = NULL;
  1911. }
  1912. if (iterClass)
  1913. {
  1914. JNIenv->DeleteGlobalRef(iterClass);
  1915. iterClass = NULL;
  1916. }
  1917. hasNextMethod = nullptr;
  1918. nextMethod = nullptr;
  1919. }
  1920. }
  1921. protected:
  1922. Linked<IEngineRowAllocator> resultAllocator;
  1923. jobject iterator = nullptr;
  1924. jclass iterClass = nullptr;
  1925. jmethodID hasNextMethod = nullptr;
  1926. jmethodID nextMethod = nullptr;
  1927. };
  1928. const char *esdl2JavaSig(IEsdlDefinition &esdl, const char *esdlType)
  1929. {
  1930. EsdlBasicElementType t = esdl.translateSimpleType(esdlType);
  1931. switch (t)
  1932. {
  1933. case ESDLT_INT16:
  1934. case ESDLT_UINT16:
  1935. return "Ljava/lang/Short;";
  1936. case ESDLT_INT32:
  1937. case ESDLT_UINT32:
  1938. return "Ljava/lang/Integer;";
  1939. case ESDLT_INT64:
  1940. case ESDLT_UINT64:
  1941. return "Ljava/math/BigInteger;";
  1942. case ESDLT_BOOL:
  1943. return "Ljava/lang/Boolean;";
  1944. case ESDLT_FLOAT:
  1945. return "Ljava/lang/Float;";
  1946. case ESDLT_DOUBLE:
  1947. return "Ljava/lang/Double;";
  1948. case ESDLT_INT8:
  1949. case ESDLT_UINT8:
  1950. case ESDLT_BYTE:
  1951. case ESDLT_UBYTE:
  1952. return "Ljava/lang/Byte;";
  1953. case ESDLT_STRING:
  1954. return "Ljava/lang/String;";
  1955. case ESDLT_UNKOWN:
  1956. case ESDLT_STRUCT:
  1957. case ESDLT_REQUEST:
  1958. case ESDLT_RESPONSE:
  1959. case ESDLT_COMPLEX:
  1960. default:
  1961. return NULL;
  1962. }
  1963. }
  1964. const char *esdl2JavaFullClassName(IEsdlDefinition &esdl, const char *esdlType)
  1965. {
  1966. EsdlBasicElementType t = esdl.translateSimpleType(esdlType);
  1967. switch (t)
  1968. {
  1969. case ESDLT_INT16:
  1970. case ESDLT_UINT16:
  1971. return "java/lang/Short";
  1972. case ESDLT_INT32:
  1973. case ESDLT_UINT32:
  1974. return "java/lang/Integer";
  1975. case ESDLT_INT64:
  1976. case ESDLT_UINT64:
  1977. return "java/math/BigInteger";
  1978. case ESDLT_BOOL:
  1979. return "java/lang/Boolean";
  1980. case ESDLT_FLOAT:
  1981. return "java/lang/Float";
  1982. case ESDLT_DOUBLE:
  1983. return "java/lang/Double";
  1984. case ESDLT_INT8:
  1985. case ESDLT_UINT8:
  1986. case ESDLT_BYTE:
  1987. case ESDLT_UBYTE:
  1988. return "java/lang/Byte";
  1989. case ESDLT_STRING:
  1990. return "java/lang/String";
  1991. case ESDLT_UNKOWN:
  1992. case ESDLT_STRUCT:
  1993. case ESDLT_REQUEST:
  1994. case ESDLT_RESPONSE:
  1995. case ESDLT_COMPLEX:
  1996. default:
  1997. return NULL;
  1998. }
  1999. }
  2000. class JavaObjectXmlWriter : public CInterface
  2001. {
  2002. public:
  2003. JavaObjectXmlWriter(CheckedJNIEnv *_JNIenv, jobject _obj, const char *_reqType, IEsdlDefinition &_esdl, const char *_esdlService, IXmlWriter &_writer)
  2004. : JNIenv(_JNIenv), obj(_obj), writer(_writer), esdl(_esdl), esdlService(_esdlService), reqType(_reqType)
  2005. {
  2006. Class = (jclass) JNIenv->NewGlobalRef(JNIenv->GetObjectClass(obj), "class");
  2007. }
  2008. ~JavaObjectXmlWriter()
  2009. {
  2010. if (Class)
  2011. JNIenv->DeleteGlobalRef(Class);
  2012. HashIterator it(javaClasses);
  2013. ForEach(it)
  2014. {
  2015. IMapping &entry = it.query();
  2016. jclass *pClass = javaClasses.mapToValue(&entry);
  2017. if (pClass)
  2018. JNIenv->DeleteGlobalRef(*pClass);
  2019. }
  2020. }
  2021. void writeSimpleType(const char *fieldname, jobject fieldObj)
  2022. {
  2023. jstring fieldStr = (jstring) JNIenv->CallObjectMethod(fieldObj, object_toString);
  2024. if (!fieldStr)
  2025. return;
  2026. const char *text = JNIenv->GetStringUTFChars(fieldStr, NULL);
  2027. if (text)
  2028. writer.outputCString(text, fieldname);
  2029. JNIenv->ReleaseStringUTFChars(fieldStr, text);
  2030. JNIenv->DeleteLocalRef(fieldStr);
  2031. }
  2032. void writeSimpleType(jclass parentClass, jobject parentObject, const char *fieldname, const char *javaSig)
  2033. {
  2034. if (!fieldname || !*fieldname)
  2035. return;
  2036. if (!javaSig || !*javaSig)
  2037. return;
  2038. jfieldID fieldId = JNIenv->GetFieldID(parentClass, fieldname, javaSig);
  2039. if (!fieldId)
  2040. return;
  2041. jobject fieldObj = (jobject) JNIenv->GetObjectField(parentObject, fieldId);
  2042. if (!fieldObj)
  2043. return;
  2044. writeSimpleType(fieldname, fieldObj);
  2045. JNIenv->DeleteLocalRef(fieldObj);
  2046. }
  2047. void writeSimpleType(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
  2048. {
  2049. const char *fieldname = defObject.queryName();
  2050. const char *javaSig = esdl2JavaSig(esdl, defObject.queryProp("type"));
  2051. writeSimpleType(parentClass, parentObject, fieldname, javaSig);
  2052. }
  2053. void writeEnumType(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
  2054. {
  2055. const char *fieldname = defObject.queryName();
  2056. VStringBuffer javaSig("L%s/%s;", esdlService.str(), defObject.queryProp("enum_type"));
  2057. jfieldID fieldId = JNIenv->GetFieldID(parentClass, fieldname, javaSig);
  2058. if (!fieldId)
  2059. return;
  2060. jobject fieldObj = (jobject) JNIenv->GetObjectField(parentObject, fieldId);
  2061. if (!fieldObj)
  2062. return;
  2063. jstring fieldStr = (jstring) JNIenv->CallObjectMethod(fieldObj, object_toString);
  2064. const char *text = JNIenv->GetStringUTFChars(fieldStr, NULL);
  2065. if (text)
  2066. writer.outputCString(text, defObject.queryName());
  2067. JNIenv->ReleaseStringUTFChars(fieldStr, text);
  2068. }
  2069. void writeComplexType(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
  2070. {
  2071. IEsdlDefStruct *defStruct = esdl.queryStruct(defObject.queryProp("complex_type"));
  2072. if (!defStruct)
  2073. return;
  2074. const char *fieldname = defObject.queryName();
  2075. VStringBuffer javaSig("L%s/%s;", esdlService.str(), defObject.queryProp("complex_type"));
  2076. jfieldID fieldId = JNIenv->GetFieldID(parentClass, fieldname, javaSig); //tbd cache this
  2077. if (!fieldId)
  2078. return;
  2079. jobject fieldObj = (jobject) JNIenv->GetObjectField(parentObject, fieldId);
  2080. if (!fieldObj)
  2081. return;
  2082. writer.outputBeginNested(fieldname, true);
  2083. writeChildren(JNIenv->GetObjectClass(fieldObj), fieldObj, defStruct);
  2084. writer.outputEndNested(fieldname);
  2085. }
  2086. void writeSimpleArray(jobjectArray arrayObj, jint count, const char *name, const char *item_tag)
  2087. {
  2088. writer.outputBeginNested(name, true);
  2089. writer.outputBeginArray(item_tag);
  2090. for (jint i=0; i < count; i++)
  2091. {
  2092. jobject elementObj = JNIenv->GetObjectArrayElement(arrayObj, i);
  2093. writeSimpleType(item_tag, elementObj);
  2094. JNIenv->DeleteLocalRef(elementObj);
  2095. }
  2096. writer.outputEndArray(item_tag);
  2097. writer.outputEndNested(name);
  2098. }
  2099. void writeComplexArray(jobjectArray arrayObj, jint count, const char *name, const char *item_tag, const char *itemTypeName)
  2100. {
  2101. writer.outputBeginNested(name, true);
  2102. writer.outputBeginArray(item_tag);
  2103. {
  2104. VStringBuffer javaClassName("%s/%s", esdlService.str(), itemTypeName);
  2105. jclass elementClass = FindClass(javaClassName);
  2106. if (!elementClass)
  2107. return;
  2108. IEsdlDefStruct *defStruct = esdl.queryStruct(itemTypeName);
  2109. if (!defStruct)
  2110. return;
  2111. for (jint i=0; i < count; i++)
  2112. {
  2113. jobject elementObj = JNIenv->GetObjectArrayElement(arrayObj, i);
  2114. writer.outputBeginNested(item_tag, true);
  2115. writeChildren(elementClass, elementObj, defStruct);
  2116. writer.outputEndNested(item_tag);
  2117. JNIenv->DeleteLocalRef(elementObj);
  2118. }
  2119. }
  2120. writer.outputEndArray(item_tag);
  2121. writer.outputEndNested(name);
  2122. }
  2123. void writeArray(jclass parentClass, jobject parentObject, IEsdlDefObject &defObject)
  2124. {
  2125. const char *itemTypeName = defObject.queryProp("type");
  2126. if (!itemTypeName)
  2127. return;
  2128. const char *item_tag = defObject.queryProp("item_tag");
  2129. if (!item_tag)
  2130. return;
  2131. const char *fieldname = defObject.queryName();
  2132. jfieldID fieldId = JNIenv->GetFieldID(parentClass, fieldname, "Ljava/util/ArrayList;");
  2133. if (!fieldId)
  2134. return;
  2135. jobject arrayListObj = (jobject) JNIenv->GetObjectField(parentObject, fieldId);
  2136. if (!arrayListObj)
  2137. return;
  2138. jobjectArray arrayObj = (jobjectArray) JNIenv->CallObjectMethod(arrayListObj, arrayList_toArray);
  2139. if (arrayObj)
  2140. {
  2141. jint count = JNIenv->GetArrayLength(arrayObj);
  2142. if (count)
  2143. {
  2144. if (esdl2JavaSig(esdl, itemTypeName))
  2145. writeSimpleArray(arrayObj, count, defObject.queryName(), item_tag);
  2146. else
  2147. writeComplexArray(arrayObj, count, defObject.queryName(), item_tag, itemTypeName);
  2148. }
  2149. JNIenv->DeleteLocalRef(arrayObj);
  2150. }
  2151. JNIenv->DeleteLocalRef(arrayListObj);
  2152. }
  2153. void writeChildren(jclass javaClass, jobject javaObject, IEsdlDefStruct *defStruct)
  2154. {
  2155. Owned<IEsdlDefObjectIterator> children = defStruct->getChildren();
  2156. ForEach (*children)
  2157. {
  2158. IEsdlDefObject &child = children->query();
  2159. if (child.getEsdlType()==EsdlTypeElement)
  2160. {
  2161. if (child.hasProp("type"))
  2162. writeSimpleType(javaClass, javaObject, child);
  2163. else if (child.hasProp("complex_type"))
  2164. writeComplexType(javaClass, javaObject, child);
  2165. }
  2166. else if (child.getEsdlType()==EsdlTypeEnumRef)
  2167. {
  2168. writeEnumType(javaClass, javaObject, child);
  2169. }
  2170. else if (child.getEsdlType()==EsdlTypeArray)
  2171. {
  2172. writeArray(javaClass, javaObject, child);
  2173. }
  2174. }
  2175. }
  2176. void write()
  2177. {
  2178. IEsdlDefStruct *reqStruct = esdl.queryStruct(reqType);
  2179. const char *name = reqStruct->queryName();
  2180. writer.outputBeginNested("Response", true);
  2181. writer.outputBeginNested("Results", true);
  2182. writer.outputBeginNested("Result", true);
  2183. writer.outputBeginDataset(name, true);
  2184. writer.outputBeginArray("Row");
  2185. writer.outputBeginNested("Row", true);
  2186. writeChildren(Class, obj, reqStruct);
  2187. writer.outputEndNested("Row");
  2188. writer.outputEndArray("Row");
  2189. writer.outputEndDataset(name);
  2190. writer.outputEndNested("Result");
  2191. writer.outputEndNested("Results");
  2192. writer.outputEndNested("Response");
  2193. }
  2194. jclass FindClass(const char *name)
  2195. {
  2196. jclass *pClass = javaClasses.getValue(name);
  2197. if (pClass)
  2198. return *pClass;
  2199. jclass localClass = JNIenv->FindClass(name);
  2200. if (!localClass)
  2201. return 0;
  2202. jclass Class = (jclass) JNIenv->NewGlobalRef(localClass, "class");
  2203. javaClasses.setValue(name, Class);
  2204. JNIenv->DeleteLocalRef(localClass);
  2205. return Class;
  2206. }
  2207. CheckedJNIEnv *JNIenv;
  2208. MapStringTo<jclass> javaClasses;
  2209. jclass Class;
  2210. jobject obj;
  2211. IXmlWriter &writer;
  2212. IEsdlDefinition &esdl;
  2213. StringAttr reqType;
  2214. StringAttr esdlService;
  2215. };
  2216. //-------------------------------------------
  2217. // There is a singleton JavaThreadContext per thread. This handles the interaction between
  2218. // the C++ thread and the java threading library, ensuring that we register/unregister as needed,
  2219. // and that any thread_local function contexts are destroyed before we detach from the java thread
  2220. interface IJavaEmbedFunctionContext : public IEmbedFunctionContext
  2221. {
  2222. virtual void endThread() = 0;
  2223. };
  2224. class JavaThreadContext
  2225. {
  2226. public:
  2227. CheckedJNIEnv *JNIenv; /* receives pointer to native method interface */
  2228. public:
  2229. JavaThreadContext()
  2230. {
  2231. jint res = globalState->javaVM->AttachCurrentThread((void **) &JNIenv, NULL);
  2232. assertex(res >= 0);
  2233. setThreadClassLoader(getSystemClassLoader());
  2234. }
  2235. ~JavaThreadContext()
  2236. {
  2237. // Make sure all thread-local function contexts and saved objects are destroyed before we detach from
  2238. // the Java thread
  2239. contexts.kill();
  2240. persistedObjects.kill();
  2241. loaders.kill();
  2242. // According to the Java VM 1.7 docs, "A native thread attached to
  2243. // the VM must call DetachCurrentThread() to detach itself before
  2244. // exiting."
  2245. globalState->javaVM->DetachCurrentThread();
  2246. }
  2247. void endThread()
  2248. {
  2249. persistedObjects.kill();
  2250. ForEachItemIn(idx, contexts)
  2251. {
  2252. auto &context = contexts.item(idx);
  2253. context.endThread();
  2254. }
  2255. }
  2256. jobject getSystemClassLoader()
  2257. {
  2258. jobject systemClassLoaderObj = JNIenv->CallStaticObjectMethod(javaLangClassLoaderClass, cl_getSystemClassLoader);
  2259. assertex(systemClassLoaderObj);
  2260. return systemClassLoaderObj;
  2261. }
  2262. void setThreadClassLoader(jobject classLoader)
  2263. {
  2264. jobject threadObj = JNIenv->CallStaticObjectMethod(javaLangThreadClass, thread_currentThread);
  2265. JNIenv->CallObjectMethod(threadObj, thread_setContextClassLoader, classLoader);
  2266. }
  2267. jobject getThreadClassLoader()
  2268. {
  2269. JNIenv->ExceptionClear();
  2270. jobject threadObj = JNIenv->CallStaticObjectMethod(javaLangThreadClass, thread_currentThread);
  2271. jobject contextClassLoaderObj = JNIenv->CallObjectMethod(threadObj, thread_getContextClassLoader);
  2272. assertex(contextClassLoaderObj);
  2273. return contextClassLoaderObj;
  2274. }
  2275. void writeObjectResult(jobject result, IEsdlDefinition *esdl, const char *esdlservice, const char *name, IXmlWriter *writer)
  2276. {
  2277. JavaObjectXmlWriter x(JNIenv, result, name, *esdl, esdlservice, *writer);
  2278. x.write();
  2279. }
  2280. void registerContext(IJavaEmbedFunctionContext *ctx)
  2281. {
  2282. // Note - this object is thread-local so no need for a critsec
  2283. contexts.append(*ctx);
  2284. }
  2285. PersistedObject *getLocalObject(CheckedJNIEnv *JNIenv, const char *name)
  2286. {
  2287. // Note - this object is thread-local so no need for a critsec
  2288. PersistedObject *p;
  2289. p = persistedObjects.find(name);
  2290. if (!p)
  2291. {
  2292. p = new PersistedObject(name);
  2293. persistedObjects.replaceOwn(*p);
  2294. }
  2295. p->crit.enter(); // needed to keep code common between local/global cases
  2296. return p;
  2297. }
  2298. jobject createThreadClassLoader(const char *classPath, const char *classname, size32_t bytecodeLen, const byte *bytecode)
  2299. {
  2300. if (bytecodeLen || (classPath && *classPath))
  2301. {
  2302. jstring jClassPath = (classPath && *classPath) ? JNIenv->NewStringUTF(classPath) : nullptr;
  2303. jobject helperName = JNIenv->NewStringUTF(helperLibraryName);
  2304. jobject contextClassLoaderObj = JNIenv->CallStaticObjectMethod(customLoaderClass, clc_newInstance, jClassPath, getSystemClassLoader(), bytecodeLen, (uint64_t) bytecode, helperName);
  2305. assertex(contextClassLoaderObj);
  2306. return contextClassLoaderObj;
  2307. }
  2308. else
  2309. {
  2310. return getSystemClassLoader();
  2311. }
  2312. }
  2313. jobject getThreadClassLoader(const char *classPath, const char *classname, size32_t bytecodeLen, const byte *bytecode)
  2314. {
  2315. StringBuffer key(classname);
  2316. if (classPath && *classPath)
  2317. key.append('!').append(classPath);
  2318. PersistedObject *p;
  2319. p = loaders.find(key);
  2320. if (!p)
  2321. {
  2322. p = new PersistedObject(key);
  2323. p->instance = JNIenv->NewGlobalRef(createThreadClassLoader(classPath, classname, bytecodeLen, bytecode), "cachedClassLoader");
  2324. loaders.replaceOwn(*p);
  2325. }
  2326. return p->instance;
  2327. }
  2328. private:
  2329. IArrayOf<IJavaEmbedFunctionContext> contexts;
  2330. StringMapOf<PersistedObject> persistedObjects = { false };
  2331. StringMapOf<PersistedObject> loaders = { false };
  2332. };
  2333. class JavaXmlBuilder : implements IXmlWriterExt, public CInterface
  2334. {
  2335. public:
  2336. IMPLEMENT_IINTERFACE;
  2337. JavaXmlBuilder(CheckedJNIEnv *_JNIenv, IEsdlDefinition *esdl_, const char *esdlservice, const char *esdltype_)
  2338. : JNIenv(_JNIenv), esdl(esdl_), javaPackage(esdlservice), esdlType(esdltype_)
  2339. {
  2340. }
  2341. ~JavaXmlBuilder()
  2342. {
  2343. while (defStack.length())
  2344. popDefStackEntry(JNIenv);
  2345. HashIterator it(javaClasses);
  2346. ForEach(it)
  2347. {
  2348. IMapping &entry = it.query();
  2349. jclass *pClass = javaClasses.mapToValue(&entry);
  2350. if (pClass)
  2351. JNIenv->DeleteGlobalRef(*pClass);
  2352. }
  2353. }
  2354. void initWriter()
  2355. {
  2356. }
  2357. IXmlWriterExt & clear()
  2358. {
  2359. throwUnexpected();
  2360. }
  2361. virtual size32_t length() const
  2362. {
  2363. return 0;
  2364. }
  2365. virtual const char *str() const
  2366. {
  2367. throwUnexpected();
  2368. }
  2369. virtual void finalize() override
  2370. {
  2371. }
  2372. virtual IInterface *saveLocation() const {return nullptr;}
  2373. virtual void rewindTo(IInterface *loc)
  2374. {
  2375. //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.
  2376. //revisit cleaning up any empty objects later.
  2377. }
  2378. inline IEsdlDefStruct *queryCurrentEsdlStruct()
  2379. {
  2380. if (!defStack.length() || !defStack.tos().defType)
  2381. return NULL;
  2382. return dynamic_cast<IEsdlDefStruct*>(defStack.tos().defType.get());
  2383. }
  2384. inline jobject getObject()
  2385. {
  2386. if (!defStack.length())
  2387. return 0;
  2388. return defStack.item(0).obj;
  2389. }
  2390. inline jobject getCurJavaObject()
  2391. {
  2392. if (!defStack.length())
  2393. return 0;
  2394. return defStack.tos().obj;
  2395. }
  2396. inline jclass getCurJavaClass()
  2397. {
  2398. if (!defStack.length())
  2399. return 0;
  2400. return defStack.tos().Class;
  2401. }
  2402. inline jmethodID getCurJavaConstructor()
  2403. {
  2404. if (!defStack.length())
  2405. return 0;
  2406. return defStack.tos().constructor;
  2407. }
  2408. virtual void outputEnumString(unsigned size, const char *text, const char *fieldname, IEsdlDefObject *defField)
  2409. {
  2410. const char *enum_type = defField->queryProp("enum_type");
  2411. if (!enum_type || !*enum_type)
  2412. return;
  2413. VStringBuffer enumClassName("%s/%s", javaPackage.str(), enum_type);
  2414. VStringBuffer enumSig("L%s;", enumClassName.str());
  2415. jfieldID fieldId = JNIenv->GetFieldID(getCurJavaClass(), fieldname, enumSig);
  2416. if (!fieldId)
  2417. return;
  2418. jclass enumClass = FindClass(enumClassName);
  2419. jmethodID fromString = JNIenv->GetStaticMethodID(enumClass, "fromString", "(Ljava/lang/String;)LEsdlExample/AddressType;"); //All types currently used for ESDL mapping have string constructors
  2420. StringAttr s(text, size);
  2421. jstring strvalue = JNIenv->NewStringUTF(s);
  2422. jobject value = JNIenv->CallStaticObjectMethod(enumClass, fromString, strvalue);
  2423. JNIenv->DeleteLocalRef(strvalue);
  2424. JNIenv->SetObjectField(getCurJavaObject(), fieldId, value);
  2425. JNIenv->DeleteLocalRef(value);
  2426. }
  2427. virtual void outputString(unsigned size, const char *text, const char *fieldname)
  2428. {
  2429. DefStackEntry *parent = defStack.length() ? &defStack.tos() : NULL;
  2430. if (!parent)
  2431. return;
  2432. const char *defTypeName = NULL;
  2433. bool isArray = (parent->defObj && parent->defObj->getEsdlType()==EsdlTypeArray);
  2434. if (isArray)
  2435. defTypeName = parent->defObj->queryProp("type");
  2436. else
  2437. {
  2438. IEsdlDefStruct *defStruct = queryCurrentEsdlStruct();
  2439. if (!defStruct)
  2440. return;
  2441. IEsdlDefObject *defField = defStruct->queryChild(fieldname);
  2442. if (!defField)
  2443. return;
  2444. if (defField->getEsdlType()==EsdlTypeEnumRef)
  2445. return outputEnumString(size, text, fieldname, defField);
  2446. defTypeName = defField->queryProp("type");
  2447. }
  2448. if (!defTypeName)
  2449. return;
  2450. const char *javaSig = esdl2JavaSig(*esdl, defTypeName);
  2451. if (!javaSig)
  2452. return;
  2453. const char *fieldClassName = esdl2JavaFullClassName(*esdl, defTypeName);
  2454. jclass typeClass = FindClass(fieldClassName);
  2455. jmethodID typeStringConstructor = JNIenv->GetMethodID(typeClass, "<init>", "(Ljava/lang/String;)V"); //All types currently used for ESDL mapping have string constructors
  2456. StringAttr s(text, size);
  2457. jstring strvalue = JNIenv->NewStringUTF(s);
  2458. jobject value = JNIenv->NewObject(typeClass, typeStringConstructor, strvalue);
  2459. JNIenv->DeleteLocalRef(strvalue);
  2460. if (!value)
  2461. return;
  2462. if (isArray)
  2463. JNIenv->CallObjectMethod(parent->obj, parent->append, value);
  2464. else
  2465. {
  2466. jfieldID fieldId = JNIenv->GetFieldID(getCurJavaClass(), fieldname, javaSig);
  2467. if (fieldId)
  2468. JNIenv->SetObjectField(getCurJavaObject(), fieldId, value);
  2469. }
  2470. JNIenv->DeleteLocalRef(value);
  2471. }
  2472. void outputString(const char *text, const char *fieldname)
  2473. {
  2474. outputString((unsigned)strlen(text), text, fieldname);
  2475. }
  2476. virtual void outputNumericString(const char *field, const char *fieldname)
  2477. {
  2478. outputString(field, fieldname);
  2479. }
  2480. virtual void outputBool(bool value, const char *fieldname)
  2481. {
  2482. outputString(value ? "true" : "false", fieldname);
  2483. }
  2484. virtual void outputUInt(unsigned __int64 field, unsigned size, const char *fieldname)
  2485. {
  2486. StringBuffer value;
  2487. value.append(field);
  2488. outputString(value.length(), value, fieldname);
  2489. }
  2490. virtual void outputInt(__int64 field, unsigned size, const char *fieldname)
  2491. {
  2492. StringBuffer value;
  2493. value.append(field);
  2494. outputString(value.length(), value, fieldname);
  2495. }
  2496. virtual void outputReal(double field, const char *fieldname)
  2497. {
  2498. StringBuffer value;
  2499. value.append(field);
  2500. outputString(value.length(), value, fieldname);
  2501. }
  2502. virtual void outputDecimal(const void *field, unsigned size, unsigned precision, const char *fieldname)
  2503. {
  2504. Decimal d;
  2505. d.setDecimal(size, precision, field);
  2506. outputString(d.getCString(), fieldname);
  2507. }
  2508. virtual void outputUDecimal(const void *field, unsigned size, unsigned precision, const char *fieldname)
  2509. {
  2510. Decimal d;
  2511. d.setUDecimal(size, precision, field);
  2512. outputString(d.getCString(), fieldname);
  2513. }
  2514. virtual void outputQString(unsigned len, const char *field, const char *fieldname)
  2515. {
  2516. MemoryAttr tempBuffer;
  2517. char * temp;
  2518. if (len <= 100)
  2519. temp = (char *)alloca(len);
  2520. else
  2521. temp = (char *)tempBuffer.allocate(len);
  2522. rtlQStrToStr(len, temp, len, field);
  2523. outputString(len, temp, fieldname);
  2524. }
  2525. virtual void outputUnicode(unsigned len, const UChar *field, const char *fieldname)
  2526. {
  2527. char * buff = 0;
  2528. unsigned bufflen = 0;
  2529. rtlUnicodeToCodepageX(bufflen, buff, len, field, "utf-8");
  2530. outputString(bufflen, buff, fieldname);
  2531. rtlFree(buff);
  2532. }
  2533. virtual void outputUtf8(unsigned len, const char *field, const char *fieldname)
  2534. {
  2535. outputString(len, field, fieldname);
  2536. }
  2537. virtual void outputData(unsigned len, const void *value, const char *fieldname)
  2538. {
  2539. }
  2540. virtual void outputQuoted(const char *text) //would have to let beginNested represent simple types with content set using this?
  2541. {
  2542. }
  2543. virtual void outputBeginDataset(const char *dsname, bool nestChildren) //not used by ESDL engine
  2544. {
  2545. }
  2546. virtual void outputEndDataset(const char *dsname)
  2547. {
  2548. }
  2549. inline IEsdlDefObject *queryChildStructDefObj(IEsdlDefObject *child)
  2550. {
  2551. if (child)
  2552. {
  2553. switch (child->getEsdlType())
  2554. {
  2555. case EsdlTypeArray:
  2556. {
  2557. const char *structType = child->queryProp("type");
  2558. if (structType)
  2559. return esdl->queryObj(structType);
  2560. break;
  2561. }
  2562. case EsdlTypeElement:
  2563. {
  2564. const char *structType = child->queryProp("complex_type");
  2565. if (structType)
  2566. return esdl->queryObj(structType);
  2567. break;
  2568. }
  2569. default:
  2570. break;
  2571. }
  2572. }
  2573. return NULL;
  2574. }
  2575. virtual void outputBeginNested(const char *fieldname, bool nestChildren)
  2576. {
  2577. IEsdlDefStruct *defStruct = NULL;
  2578. IEsdlDefObject *defField = NULL;
  2579. IEsdlDefObject *defType = NULL;
  2580. if (!defStack.length())
  2581. {
  2582. defType = esdl->queryObj(fieldname);
  2583. }
  2584. else
  2585. {
  2586. DefStackEntry &parent = defStack.tos();
  2587. if (parent.defObj && parent.defObj->getEsdlType()==EsdlTypeArray)
  2588. {
  2589. defType = parent.defType;
  2590. }
  2591. else
  2592. {
  2593. defStruct = queryCurrentEsdlStruct();
  2594. if (defStruct)
  2595. {
  2596. defField = defStruct->queryChild(fieldname);
  2597. if (defField)
  2598. defType = queryChildStructDefObj(defField);
  2599. }
  2600. }
  2601. }
  2602. pushDefStackEntry(JNIenv, javaPackage, fieldname, defType, defField);
  2603. }
  2604. virtual void outputEndNested(const char *fieldname)
  2605. {
  2606. if (defStack.length()<=1) //don't destroy root object yet
  2607. return;
  2608. if (!streq(fieldname, defStack.tos().name)) //should be exception? or forgive and forget?
  2609. return;
  2610. popDefStackEntry(JNIenv);
  2611. }
  2612. virtual void outputSetAll()
  2613. {
  2614. }
  2615. virtual void outputBeginArray(const char *fieldname)
  2616. {
  2617. }
  2618. virtual void outputEndArray(const char *fieldname)
  2619. {
  2620. }
  2621. virtual void outputInlineXml(const char *text)
  2622. {
  2623. }
  2624. virtual void outputXmlns(const char *name, const char *uri)
  2625. {
  2626. }
  2627. virtual void cutFrom(IInterface *location, StringBuffer& databuf)
  2628. {
  2629. }
  2630. virtual void outputInline(const char* text)
  2631. {
  2632. }
  2633. public:
  2634. CheckedJNIEnv *JNIenv;
  2635. Linked<IEsdlDefinition> esdl;
  2636. StringAttr javaPackage;
  2637. StringAttr esdlType;
  2638. class DefStackEntry : public CInterface
  2639. {
  2640. public:
  2641. DefStackEntry(const char *fieldname, IEsdlDefObject *_defType, IEsdlDefObject *_defObj) : name(fieldname), defType(_defType), defObj(_defObj), Class(0), obj(0), constructor(0), append(0), fieldId(0)
  2642. {
  2643. }
  2644. ~DefStackEntry()
  2645. {
  2646. }
  2647. public:
  2648. Linked<IEsdlDefObject> defType;
  2649. Linked<IEsdlDefObject> defObj;
  2650. StringAttr name;
  2651. jclass Class;
  2652. jmethodID constructor;
  2653. jmethodID append;
  2654. jfieldID fieldId;
  2655. jobject obj;
  2656. };
  2657. jobject MakeObjectGlobal(jobject local)
  2658. {
  2659. if (!local)
  2660. return 0;
  2661. jobject global = JNIenv->NewGlobalRef(local, "makeObjectGlobal");
  2662. JNIenv->DeleteLocalRef(local);
  2663. return global;
  2664. }
  2665. jclass FindClass(const char *name)
  2666. {
  2667. jclass *pClass = javaClasses.getValue(name);
  2668. if (pClass)
  2669. return *pClass;
  2670. jclass Class = (jclass) MakeObjectGlobal(JNIenv->FindClass(name));
  2671. javaClasses.setValue(name, Class); //even save if result has no class
  2672. return Class;
  2673. }
  2674. void popDefStackEntry(CheckedJNIEnv *JNIenv)
  2675. {
  2676. if (!defStack.length())
  2677. return;
  2678. Owned<DefStackEntry> entry = &defStack.popGet();
  2679. if (entry->obj)
  2680. JNIenv->DeleteGlobalRef(entry->obj);
  2681. }
  2682. void pushDefStackEntry(CheckedJNIEnv *JNIenv, const char *package, const char *fieldname, IEsdlDefObject *defType, IEsdlDefObject *defObject)
  2683. {
  2684. DefStackEntry *parent = defStack.length() ? &defStack.tos() : NULL;
  2685. Owned<DefStackEntry> entry = new DefStackEntry(fieldname, defType, defObject);
  2686. JNIenv->ExceptionClear();
  2687. if (defObject && defObject->getEsdlType()==EsdlTypeArray)
  2688. {
  2689. entry->Class = arrayListClass;
  2690. entry->constructor = arrayList_constructor;
  2691. entry->append = arrayList_add;
  2692. entry->obj = MakeObjectGlobal(JNIenv->NewObject(entry->Class, entry->constructor));
  2693. if (entry->obj)
  2694. {
  2695. if (parent && parent->Class)
  2696. {
  2697. entry->fieldId = JNIenv->GetFieldID(parent->Class, fieldname, "Ljava/util/ArrayList;");
  2698. if (parent->obj && entry->fieldId)
  2699. JNIenv->SetObjectField(parent->obj, entry->fieldId, entry->obj);
  2700. }
  2701. }
  2702. }
  2703. else if (defType)
  2704. {
  2705. VStringBuffer javaClassName("%s/%s", package, defType->queryName());
  2706. entry->Class = FindClass(javaClassName);
  2707. if (entry->Class)
  2708. {
  2709. entry->constructor = JNIenv->GetMethodID(entry->Class, "<init>", "()V");
  2710. entry->obj = MakeObjectGlobal(JNIenv->NewObject(entry->Class, entry->constructor));
  2711. if (entry->obj)
  2712. {
  2713. if (parent)
  2714. {
  2715. if (parent->defObj && parent->defObj->getEsdlType()==EsdlTypeArray)
  2716. JNIenv->CallObjectMethod(parent->obj, parent->append, entry->obj);
  2717. else if (parent->Class)
  2718. {
  2719. VStringBuffer javaSig("L%s;", javaClassName.str());
  2720. entry->fieldId = JNIenv->GetFieldID(parent->Class, fieldname, javaSig);
  2721. if (parent->obj && entry->fieldId)
  2722. JNIenv->SetObjectField(parent->obj, entry->fieldId, entry->obj);
  2723. }
  2724. }
  2725. }
  2726. }
  2727. }
  2728. defStack.append(*entry.getClear());
  2729. }
  2730. CIArrayOf<DefStackEntry> defStack;
  2731. MapStringTo<jclass> javaClasses;
  2732. };
  2733. // Each call to a Java function will use a new JavaEmbedScriptContext object
  2734. #define MAX_JNI_ARGS 10
  2735. class JavaClassReader
  2736. {
  2737. public:
  2738. JavaClassReader(const char *filename)
  2739. {
  2740. // Pull apart a class file to see its name and signature.
  2741. /* From https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1
  2742. ClassFile {
  2743. u4 magic;
  2744. u2 minor_version;
  2745. u2 major_version;
  2746. u2 constant_pool_count;
  2747. cp_info constant_pool[constant_pool_count-1];
  2748. u2 access_flags;
  2749. u2 this_class;
  2750. u2 super_class;
  2751. u2 interfaces_count;
  2752. u2 interfaces[interfaces_count];
  2753. u2 fields_count;
  2754. field_info fields[fields_count];
  2755. u2 methods_count;
  2756. method_info methods[methods_count];
  2757. u2 attributes_count;
  2758. attribute_info attributes[attributes_count];
  2759. }
  2760. */
  2761. #ifdef TRACE_CLASSFILE
  2762. DBGLOG("Reading class file created in %s", filename);
  2763. #endif
  2764. Owned<IFile> file = createIFile(filename);
  2765. OwnedIFileIO io = file->open(IFOread);
  2766. assertex(io);
  2767. read(io, 0, (size32_t)-1, b);
  2768. b.setEndian(__BIG_ENDIAN);
  2769. uint32_t magic;
  2770. b.read(magic);
  2771. if (magic != 0xcafebabe)
  2772. throwUnexpected();
  2773. uint16_t major, minor, cpc;
  2774. b.read(major);
  2775. b.read(minor);
  2776. b.read(cpc);
  2777. constOffsets = new unsigned[cpc];
  2778. constOffsets[0] = 0;
  2779. for (int i = 0; i < cpc-1; i++) // There are only cpc-1 entries, for reasons best known to the java designers
  2780. {
  2781. constOffsets[i+1] = b.getPos();
  2782. byte tag;
  2783. b.read(tag);
  2784. switch (tag)
  2785. {
  2786. case CONSTANT_Class:
  2787. uint16_t idx;
  2788. b.read(idx);
  2789. #ifdef TRACE_CLASSFILE
  2790. DBGLOG("%u: Class %u", i+1, idx);
  2791. #endif
  2792. break;
  2793. case CONSTANT_Fieldref:
  2794. case CONSTANT_Methodref:
  2795. case CONSTANT_InterfaceMethodref:
  2796. uint16_t classIdx;
  2797. uint16_t nametypeIdx;
  2798. b.read(classIdx);
  2799. b.read(nametypeIdx);
  2800. #ifdef TRACE_CLASSFILE
  2801. DBGLOG("%u: ref(%u) class %u nametype %u", i+1, tag, classIdx, nametypeIdx);
  2802. #endif
  2803. break;
  2804. case CONSTANT_String:
  2805. #ifdef TRACE_CLASSFILE
  2806. DBGLOG("%u: Tag %u", i+1, tag);
  2807. #endif
  2808. b.skip(2);
  2809. break;
  2810. case CONSTANT_Integer:
  2811. case CONSTANT_Float:
  2812. #ifdef TRACE_CLASSFILE
  2813. DBGLOG("%u: Tag %u", i+1, tag);
  2814. #endif
  2815. b.skip(4);
  2816. break;
  2817. case CONSTANT_Long:
  2818. case CONSTANT_Double:
  2819. #ifdef TRACE_CLASSFILE
  2820. DBGLOG("%u: Tag %u", i+1, tag);
  2821. #endif
  2822. b.skip(8);
  2823. break;
  2824. case CONSTANT_NameAndType:
  2825. uint16_t nameIdx;
  2826. uint16_t descIdx;
  2827. b.read(nameIdx);
  2828. b.read(descIdx);
  2829. #ifdef TRACE_CLASSFILE
  2830. DBGLOG("%u: NameAndType(%u) name %u desc %u", i+1, tag, nameIdx, descIdx);
  2831. #endif
  2832. break;
  2833. case CONSTANT_Utf8:
  2834. // length-prefixed
  2835. uint16_t length;
  2836. b.read(length);
  2837. const byte *val;
  2838. val = b.readDirect(length);
  2839. #ifdef TRACE_CLASSFILE
  2840. DBGLOG("%u: %.*s", i+1, length, val);
  2841. #endif
  2842. break;
  2843. case CONSTANT_MethodHandle:
  2844. #ifdef TRACE_CLASSFILE
  2845. DBGLOG("%u: Tag %u", i+1, tag);
  2846. #endif
  2847. b.skip(3);
  2848. break;
  2849. case CONSTANT_MethodType:
  2850. #ifdef TRACE_CLASSFILE
  2851. DBGLOG("%u: Tag %u", i+1, tag);
  2852. #endif
  2853. b.skip(2);
  2854. break;
  2855. case CONSTANT_InvokeDynamic:
  2856. #ifdef TRACE_CLASSFILE
  2857. DBGLOG("%u: Tag %u", i+1, tag);
  2858. #endif
  2859. b.skip(4);
  2860. break;
  2861. default:
  2862. DBGLOG("Unexpected tag %u reading bytecode file", tag);
  2863. throwUnexpected();
  2864. }
  2865. }
  2866. uint16_t access_flags; b.read(access_flags);
  2867. uint16_t this_class; b.read(this_class);
  2868. uint16_t super_class; b.read(super_class);
  2869. uint16_t interfaces_count; b.read(interfaces_count);
  2870. b.skip(interfaces_count*sizeof(uint16_t));
  2871. uint16_t fields_count; b.read(fields_count);
  2872. #ifdef TRACE_CLASSFILE
  2873. 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);
  2874. #endif
  2875. for (unsigned i = 0; i < fields_count; i++)
  2876. {
  2877. b.skip(6);
  2878. uint16_t attr_count;
  2879. b.read(attr_count);
  2880. for (unsigned j = 0; j < attr_count; j++)
  2881. {
  2882. b.skip(2);
  2883. uint32_t attr_length;
  2884. b.read(attr_length);
  2885. b.skip(attr_length);
  2886. }
  2887. }
  2888. uint16_t methods_count; b.read(methods_count);
  2889. #ifdef TRACE_CLASSFILE
  2890. DBGLOG("methods_count %u", methods_count);
  2891. #endif
  2892. for (unsigned i = 0; i < methods_count; i++)
  2893. {
  2894. uint16_t flags; b.read(flags);
  2895. uint16_t name; b.read(name);
  2896. uint16_t desc; b.read(desc);
  2897. #ifdef TRACE_CLASSFILE
  2898. DBGLOG("Method %u name %u desc %u flags %x", i, name, desc, flags);
  2899. #endif
  2900. if (flags & ACC_PUBLIC)
  2901. {
  2902. StringAttr thisName;
  2903. readUtf(thisName, name);
  2904. StringAttr thisSig;
  2905. readUtf(thisSig, desc);
  2906. methodNames.append(thisName);
  2907. methodSigs.append(thisSig);
  2908. methodFlags.append(flags);
  2909. }
  2910. uint16_t attr_count;
  2911. b.read(attr_count);
  2912. for (unsigned j = 0; j < attr_count; j++)
  2913. {
  2914. uint16_t attr_name_idx; b.read(attr_name_idx);
  2915. StringAttr attrName;
  2916. readUtf(attrName, attr_name_idx);
  2917. uint32_t attr_length;
  2918. b.read(attr_length);
  2919. if (streq(attrName, "Signature") && attr_length==2)
  2920. {
  2921. uint16_t ext_sig_idx; b.read(ext_sig_idx);
  2922. StringAttr extSig;
  2923. readUtf(extSig, ext_sig_idx);
  2924. #ifdef TRACE_CLASSFILE
  2925. DBGLOG("Seen extended signature %s", extSig.str());
  2926. #endif
  2927. if (flags & ACC_PUBLIC)
  2928. {
  2929. methodSigs.pop();
  2930. methodSigs.append(extSig);
  2931. }
  2932. }
  2933. else
  2934. b.skip(attr_length);
  2935. }
  2936. }
  2937. /* Don't bother reading attributes as they are not really interesting to us
  2938. uint16_t attributes_count; b.read(attributes_count);
  2939. #ifdef TRACE_CLASSFILE
  2940. DBGLOG("attributes_count %u", attributes_count);
  2941. #endif
  2942. for (unsigned i = 0; i < attributes_count; i++)
  2943. {
  2944. b.skip(2);
  2945. uint32_t attr_length;
  2946. b.read(attr_length);
  2947. b.skip(attr_length);
  2948. }
  2949. #ifdef TRACE_CLASSFILE
  2950. DBGLOG("%u of %u bytes remaining", b.remaining(), b.length());
  2951. #endif
  2952. */
  2953. // Now we can find this class name
  2954. readTag(this_class, CONSTANT_Class);
  2955. readUtf(className, readIdx());
  2956. }
  2957. ~JavaClassReader()
  2958. {
  2959. delete [] constOffsets;
  2960. }
  2961. StringBuffer & getSignature(StringBuffer &ret, unsigned idx) const
  2962. {
  2963. if (!methodNames.isItem(idx))
  2964. throw makeStringException(0, "No public static method found");
  2965. ret.appendf("%s.%s:", className.get(), methodNames.item(idx));
  2966. if ((methodFlags[idx] & ACC_STATIC) == 0)
  2967. ret.append('@');
  2968. return ret.append(methodSigs.item(idx));
  2969. }
  2970. const char *queryClassName() const
  2971. {
  2972. return className.get();
  2973. }
  2974. MemoryBuffer &getEmbedData(MemoryBuffer &result, const char *funcName, bool mainClass) const
  2975. {
  2976. result.setEndian(__BIG_ENDIAN);
  2977. StringBuffer signature;
  2978. if (mainClass)
  2979. {
  2980. unsigned methodIdx = getFunctionIdx(funcName);
  2981. getSignature(signature, methodIdx);
  2982. }
  2983. else
  2984. signature.set(className);
  2985. result.append((size32_t) signature.length());
  2986. result.append(signature.length(), signature.str());
  2987. result.append((size32_t) b.length());
  2988. result.append(b);
  2989. return result;
  2990. }
  2991. enum access_flag : uint16_t
  2992. {
  2993. ACC_PUBLIC = 0x0001, // Declared public; may be accessed from outside its package.
  2994. ACC_PRIVATE = 0x0002, // Declared private; accessible only within the defining class.
  2995. ACC_PROTECTED = 0x0004, // Declared protected; may be accessed within subclasses.
  2996. ACC_STATIC = 0x0008, // Declared static.
  2997. ACC_FINAL = 0x0010, // Declared final; must not be overridden (§5.4.5).
  2998. ACC_SYNCHRONIZED = 0x0020, // Declared synchronized; invocation is wrapped by a monitor use.
  2999. ACC_BRIDGE = 0x0040, // A bridge method, generated by the compiler.
  3000. ACC_VARARGS = 0x0080, // Declared with variable number of arguments.
  3001. ACC_NATIVE = 0x0100, // Declared native; implemented in a language other than Java.
  3002. ACC_ABSTRACT = 0x0400, // Declared abstract; no implementation is provided.
  3003. ACC_STRICT = 0x0800, // Declared strictfp; floating-point mode is FP-strict.
  3004. ACC_SYNTHETIC = 0x1000, // Declared synthetic; not present in the source code.
  3005. };
  3006. unsigned getFlags(const char *funcName) const
  3007. {
  3008. unsigned idx = getFunctionIdx(funcName);
  3009. return methodFlags[idx];
  3010. }
  3011. private:
  3012. bool isConstructor(const char *name) const
  3013. {
  3014. const char *shortClass = strrchr(className, '/');
  3015. if (shortClass)
  3016. shortClass++;
  3017. else
  3018. shortClass = className;
  3019. return streq(shortClass, name);
  3020. }
  3021. unsigned getFunctionIdx(const char *funcName) const
  3022. {
  3023. if (isConstructor(funcName))
  3024. funcName = "<init>";
  3025. unsigned methodIdx = (unsigned) -1;
  3026. ForEachItemIn(idx, methodNames)
  3027. {
  3028. if (streq(funcName, methodNames[idx]))
  3029. {
  3030. if (methodIdx != (unsigned) -1)
  3031. throw makeStringExceptionV(0, "Embedded java has multiple public methods called %s", funcName);
  3032. methodIdx = idx;
  3033. }
  3034. }
  3035. if (methodIdx == (unsigned) -1)
  3036. throw makeStringExceptionV(0, "Embedded java should export a public method %s", funcName);
  3037. return methodIdx;
  3038. }
  3039. uint16_t readIdx()
  3040. {
  3041. uint16_t idx;
  3042. b.read(idx);
  3043. return idx;
  3044. }
  3045. void readTag(unsigned idx, byte expected)
  3046. {
  3047. b.reset(constOffsets[idx]);
  3048. byte tag;
  3049. b.read(tag);
  3050. assertex(tag == expected);
  3051. }
  3052. void readUtf(StringAttr &dest, unsigned idx)
  3053. {
  3054. auto savepos = b.getPos();
  3055. readTag(idx, CONSTANT_Utf8);
  3056. uint16_t length;
  3057. b.read(length);
  3058. dest.set((const char *) b.readDirect(length), length);
  3059. b.reset(savepos);
  3060. }
  3061. enum const_type
  3062. {
  3063. CONSTANT_Class = 7,
  3064. CONSTANT_Fieldref = 9,
  3065. CONSTANT_Methodref = 10,
  3066. CONSTANT_InterfaceMethodref = 11,
  3067. CONSTANT_String = 8,
  3068. CONSTANT_Integer = 3,
  3069. CONSTANT_Float = 4,
  3070. CONSTANT_Long = 5,
  3071. CONSTANT_Double = 6,
  3072. CONSTANT_NameAndType = 12,
  3073. CONSTANT_Utf8 = 1,
  3074. CONSTANT_MethodHandle = 15,
  3075. CONSTANT_MethodType = 16,
  3076. CONSTANT_InvokeDynamic = 18
  3077. };
  3078. MemoryBuffer b;
  3079. unsigned *constOffsets = nullptr;
  3080. StringAttr className;
  3081. StringArray methodNames;
  3082. StringArray methodSigs;
  3083. UnsignedArray methodFlags;
  3084. };
  3085. // Objects of class JavaEmbedImportContext are created locally for each call of a function, or thread-local to persist from one call to the next.
  3086. // Methods in here do not need to be thread-safe
  3087. class JavaEmbedImportContext : public CInterfaceOf<IJavaEmbedFunctionContext>
  3088. {
  3089. public:
  3090. JavaEmbedImportContext(ICodeContext *codeCtx, JavaThreadContext *_sharedCtx, jobject _instance, unsigned _flags, const char *options, const IThorActivityContext *_activityContext)
  3091. : sharedCtx(_sharedCtx), JNIenv(sharedCtx->JNIenv), instance(_instance), flags(_flags), activityContext(_activityContext)
  3092. {
  3093. argcount = 0;
  3094. argsig = NULL;
  3095. nonStatic = (instance != nullptr);
  3096. javaClass = nullptr;
  3097. StringArray opts;
  3098. opts.appendList(options, ",");
  3099. if (codeCtx)
  3100. {
  3101. engine = codeCtx->queryEngineContext();
  3102. try {
  3103. nodeNum = codeCtx->getNodeNum();
  3104. }
  3105. catch (IException *E)
  3106. {
  3107. E->Release(); // We may get an error if calling on the master - we just want to ignore it
  3108. }
  3109. }
  3110. StringBuffer lclassPath;
  3111. if (engine)
  3112. {
  3113. const StringArray &manifestJars = engine->queryManifestFiles("jar");
  3114. ForEachItemIn(idx, manifestJars)
  3115. {
  3116. lclassPath.append(';').append(manifestJars.item(idx));
  3117. }
  3118. }
  3119. ForEachItemIn(idx, opts)
  3120. {
  3121. const char *opt = opts.item(idx);
  3122. const char *val = strchr(opt, '=');
  3123. if (val)
  3124. {
  3125. StringBuffer optName(val-opt, opt);
  3126. val++;
  3127. if (stricmp(optName, "classpath")==0)
  3128. lclassPath.append(';').append(val);
  3129. else if (strieq(optName, "globalscope"))
  3130. globalScopeKey.set(val);
  3131. else if (strieq(optName, "persist"))
  3132. {
  3133. if (persistMode != persistNone)
  3134. throw MakeStringException(MSGAUD_user, 0, "javaembed: Persist option specified more than once");
  3135. persistMode = getPersistMode(val, globalScopeKey);
  3136. switch (persistMode)
  3137. {
  3138. case persistChannel:
  3139. case persistWorkunit:
  3140. case persistQuery:
  3141. if (!engine)
  3142. throw MakeStringException(MSGAUD_user, 0, "javaembed: Persist mode '%s' not supported here", val);
  3143. break;
  3144. }
  3145. }
  3146. else
  3147. throw MakeStringException(0, "javaembed: Unknown option %s", optName.str());
  3148. }
  3149. }
  3150. if (lclassPath.length()>1)
  3151. classpath.set(lclassPath.str()+1);
  3152. if (flags & EFthreadlocal)
  3153. sharedCtx->registerContext(this); // Do at end - otherwise an exception thrown during construction will leave this reference dangling
  3154. }
  3155. ~JavaEmbedImportContext()
  3156. {
  3157. if (javaClass)
  3158. JNIenv->DeleteGlobalRef(javaClass);
  3159. if (classLoader)
  3160. JNIenv->DeleteGlobalRef(classLoader);
  3161. }
  3162. virtual void endThread() override
  3163. {
  3164. instance = nullptr;
  3165. if (javaClass)
  3166. {
  3167. JNIenv->DeleteGlobalRef(javaClass);
  3168. javaClass = nullptr;
  3169. }
  3170. if (classLoader)
  3171. {
  3172. JNIenv->DeleteGlobalRef(classLoader);
  3173. classLoader = nullptr;
  3174. }
  3175. javaMethodID = nullptr;
  3176. }
  3177. virtual bool getBooleanResult()
  3178. {
  3179. switch (*returnType)
  3180. {
  3181. case 'Z': return result.z;
  3182. case 'L':
  3183. {
  3184. // Result should be of class 'Boolean'
  3185. if (!result.l)
  3186. return false;
  3187. jmethodID getVal = JNIenv->GetMethodID(JNIenv->GetObjectClass(result.l), "booleanValue", "()Z");
  3188. if (!getVal)
  3189. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  3190. bool ret=JNIenv->CallBooleanMethod(result.l, getVal);
  3191. return ret;
  3192. }
  3193. default:
  3194. throw MakeStringException(MSGAUD_user, 0, "javaembed: Type mismatch on result");
  3195. }
  3196. }
  3197. virtual void getDataResult(size32_t &__len, void * &__result)
  3198. {
  3199. if (strcmp(returnType, "[B")!=0)
  3200. throw resultMismatchException("data");
  3201. jbyteArray array = (jbyteArray) result.l;
  3202. __len = (array != NULL ? JNIenv->GetArrayLength(array) : 0);
  3203. __result = (__len > 0 ? rtlMalloc(__len) : NULL);
  3204. if (__result)
  3205. JNIenv->GetByteArrayRegion(array, 0, __len, (jbyte *) __result);
  3206. }
  3207. virtual double getRealResult()
  3208. {
  3209. switch (*returnType)
  3210. {
  3211. case 'D': return result.d;
  3212. case 'F': return result.f;
  3213. case 'L':
  3214. {
  3215. // Result should be of class 'Number'
  3216. if (!result.l)
  3217. return 0;
  3218. jmethodID getVal = JNIenv->GetMethodID(JNIenv->GetObjectClass(result.l), "doubleValue", "()D");
  3219. if (!getVal)
  3220. throw resultMismatchException("real");
  3221. double ret = JNIenv->CallDoubleMethod(result.l, getVal);
  3222. return ret;
  3223. }
  3224. default:
  3225. throw resultMismatchException("real");
  3226. }
  3227. }
  3228. virtual __int64 getSignedResult()
  3229. {
  3230. switch (*returnType)
  3231. {
  3232. case 'B': return result.b;
  3233. case 'S': return result.s;
  3234. case 'I': return result.i;
  3235. case 'J': return result.j;
  3236. case 'L':
  3237. {
  3238. // Result should be of class 'Number'
  3239. if (!result.l)
  3240. return 0;
  3241. jmethodID getVal = JNIenv->GetMethodID(JNIenv->GetObjectClass(result.l), "longValue", "()J");
  3242. if (!getVal)
  3243. throw resultMismatchException("integer");
  3244. __int64 ret = JNIenv->CallLongMethod(result.l, getVal);
  3245. return ret;
  3246. }
  3247. default:
  3248. throw resultMismatchException("integer");
  3249. }
  3250. }
  3251. virtual unsigned __int64 getUnsignedResult()
  3252. {
  3253. if (*returnType=='V' && strieq(methodName, "<init>"))
  3254. return (unsigned __int64) result.l;
  3255. if (*returnType=='L' && JNIenv->IsSameObject(result.l, instance) && persistMode != persistNone)
  3256. return (unsigned __int64) instance;
  3257. StringBuffer s;
  3258. throw makeStringExceptionV(MSGAUD_user, 0, "javaembed: In method %s: Unsigned results not supported", getReportName(s).str()); // Java doesn't support unsigned
  3259. }
  3260. virtual void getStringResult(size32_t &__len, char * &__result)
  3261. {
  3262. switch (*returnType)
  3263. {
  3264. case 'C': // Single char returned, prototyped as STRING or STRING1 in ECL
  3265. rtlUnicodeToStrX(__len, __result, 1, &result.c);
  3266. break;
  3267. case 'L':
  3268. {
  3269. jstring sresult = (jstring) result.l;
  3270. if (sresult)
  3271. {
  3272. size_t size = JNIenv->GetStringUTFLength(sresult); // in bytes
  3273. const char *text = JNIenv->GetStringUTFChars(sresult, NULL);
  3274. size32_t chars = rtlUtf8Length(size, text);
  3275. rtlUtf8ToStrX(__len, __result, chars, text);
  3276. JNIenv->ReleaseStringUTFChars(sresult, text);
  3277. }
  3278. else
  3279. {
  3280. __len = 0;
  3281. __result = NULL;
  3282. }
  3283. break;
  3284. }
  3285. default:
  3286. throw resultMismatchException("string");
  3287. }
  3288. }
  3289. virtual void getUTF8Result(size32_t &__chars, char * &__result)
  3290. {
  3291. switch (*returnType)
  3292. {
  3293. case 'C': // Single jchar returned, prototyped as UTF8 in ECL
  3294. rtlUnicodeToUtf8X(__chars, __result, 1, &result.c);
  3295. break;
  3296. case 'L':
  3297. {
  3298. jstring sresult = (jstring) result.l;
  3299. if (sresult)
  3300. {
  3301. size_t size = JNIenv->GetStringUTFLength(sresult); // Returns length in bytes (not chars)
  3302. const char * text = JNIenv->GetStringUTFChars(sresult, NULL);
  3303. rtlUtf8ToUtf8X(__chars, __result, rtlUtf8Length(size, text), text);
  3304. JNIenv->ReleaseStringUTFChars(sresult, text);
  3305. }
  3306. else
  3307. {
  3308. __chars = 0;
  3309. __result = NULL;
  3310. }
  3311. break;
  3312. }
  3313. default:
  3314. throw resultMismatchException("utf8");
  3315. }
  3316. }
  3317. virtual void getUnicodeResult(size32_t &__chars, UChar * &__result)
  3318. {
  3319. switch (*returnType)
  3320. {
  3321. case 'C': // Single jchar returned, prototyped as UNICODE or UNICODE1 in ECL
  3322. rtlUnicodeToUnicodeX(__chars, __result, 1, &result.c);
  3323. break;
  3324. case 'L':
  3325. {
  3326. jstring sresult = (jstring) result.l;
  3327. if (sresult)
  3328. {
  3329. size_t size = JNIenv->GetStringUTFLength(sresult); // in bytes
  3330. const char *text = JNIenv->GetStringUTFChars(sresult, NULL);
  3331. size32_t chars = rtlUtf8Length(size, text);
  3332. rtlUtf8ToUnicodeX(__chars, __result, chars, text);
  3333. JNIenv->ReleaseStringUTFChars(sresult, text);
  3334. }
  3335. else
  3336. {
  3337. __chars = 0;
  3338. __result = NULL;
  3339. }
  3340. break;
  3341. }
  3342. default:
  3343. throw resultMismatchException("unicode");
  3344. }
  3345. }
  3346. virtual void getSetResult(bool & __isAllResult, size32_t & __resultBytes, void * & __result, int _elemType, size32_t elemSize)
  3347. {
  3348. if (*returnType != '[')
  3349. throw resultMismatchException("array");
  3350. type_t elemType = (type_t) _elemType;
  3351. jarray array = (jarray) result.l;
  3352. int numResults = (array != NULL ? JNIenv->GetArrayLength(array) : 0);
  3353. rtlRowBuilder out;
  3354. byte *outData = NULL;
  3355. size32_t outBytes = 0;
  3356. if (numResults > 0)
  3357. {
  3358. if (elemSize != UNKNOWN_LENGTH)
  3359. {
  3360. out.ensureAvailable(numResults * elemSize); // MORE - check for overflow?
  3361. outData = out.getbytes();
  3362. }
  3363. switch(returnType[1])
  3364. {
  3365. case 'Z':
  3366. checkType(type_boolean, sizeof(jboolean), elemType, elemSize);
  3367. JNIenv->GetBooleanArrayRegion((jbooleanArray) array, 0, numResults, (jboolean *) outData);
  3368. break;
  3369. case 'B':
  3370. checkType(type_int, sizeof(jbyte), elemType, elemSize);
  3371. JNIenv->GetByteArrayRegion((jbyteArray) array, 0, numResults, (jbyte *) outData);
  3372. break;
  3373. case 'C':
  3374. // we COULD map to a set of string1, but is there any point?
  3375. {
  3376. StringBuffer s;
  3377. throw MakeStringException(0, "javaembed: In method %s: Return type mismatch (char[] not supported)", getReportName(s).str());
  3378. break;
  3379. }
  3380. case 'S':
  3381. checkType(type_int, sizeof(jshort), elemType, elemSize);
  3382. JNIenv->GetShortArrayRegion((jshortArray) array, 0, numResults, (jshort *) outData);
  3383. break;
  3384. case 'I':
  3385. checkType(type_int, sizeof(jint), elemType, elemSize);
  3386. JNIenv->GetIntArrayRegion((jintArray) array, 0, numResults, (jint *) outData);
  3387. break;
  3388. case 'J':
  3389. checkType(type_int, sizeof(jlong), elemType, elemSize);
  3390. JNIenv->GetLongArrayRegion((jlongArray) array, 0, numResults, (jlong *) outData);
  3391. break;
  3392. case 'F':
  3393. checkType(type_real, sizeof(jfloat), elemType, elemSize);
  3394. JNIenv->GetFloatArrayRegion((jfloatArray) array, 0, numResults, (jfloat *) outData);
  3395. break;
  3396. case 'D':
  3397. checkType(type_real, sizeof(jdouble), elemType, elemSize);
  3398. JNIenv->GetDoubleArrayRegion((jdoubleArray) array, 0, numResults, (jdouble *) outData);
  3399. break;
  3400. case 'L':
  3401. if (strcmp(returnType, "[Ljava/lang/String;") == 0)
  3402. {
  3403. for (int i = 0; i < numResults; i++)
  3404. {
  3405. jstring elem = (jstring) JNIenv->GetObjectArrayElement((jobjectArray) array, i);
  3406. size_t lenBytes = JNIenv->GetStringUTFLength(elem); // in bytes
  3407. const char *text = JNIenv->GetStringUTFChars(elem, NULL);
  3408. switch (elemType)
  3409. {
  3410. case type_string:
  3411. if (elemSize == UNKNOWN_LENGTH)
  3412. {
  3413. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  3414. outData = out.getbytes() + outBytes;
  3415. * (size32_t *) outData = lenBytes;
  3416. rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
  3417. outBytes += lenBytes + sizeof(size32_t);
  3418. }
  3419. else
  3420. rtlStrToStr(elemSize, outData, lenBytes, text);
  3421. break;
  3422. case type_varstring:
  3423. if (elemSize == UNKNOWN_LENGTH)
  3424. {
  3425. out.ensureAvailable(outBytes + lenBytes + 1);
  3426. outData = out.getbytes() + outBytes;
  3427. rtlStrToVStr(0, outData, lenBytes, text);
  3428. outBytes += lenBytes + 1;
  3429. }
  3430. else
  3431. rtlStrToVStr(elemSize, outData, lenBytes, text); // Fixed size null terminated strings... weird.
  3432. break;
  3433. case type_utf8:
  3434. case type_unicode:
  3435. {
  3436. size32_t numchars = rtlUtf8Length(lenBytes, text);
  3437. if (elemType == type_utf8)
  3438. {
  3439. assertex (elemSize == UNKNOWN_LENGTH);
  3440. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  3441. outData = out.getbytes() + outBytes;
  3442. * (size32_t *) outData = numchars;
  3443. rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
  3444. outBytes += lenBytes + sizeof(size32_t);
  3445. }
  3446. else
  3447. {
  3448. if (elemSize == UNKNOWN_LENGTH)
  3449. {
  3450. // You can't assume that number of chars in utf8 matches number in unicode16 ...
  3451. size32_t numchars16;
  3452. rtlDataAttr unicode16;
  3453. rtlUtf8ToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
  3454. out.ensureAvailable(outBytes + numchars16*sizeof(UChar) + sizeof(size32_t));
  3455. outData = out.getbytes() + outBytes;
  3456. * (size32_t *) outData = numchars16;
  3457. rtlUnicodeToUnicode(numchars16, (UChar *) (outData+sizeof(size32_t)), numchars16, unicode16.getustr());
  3458. outBytes += numchars16*sizeof(UChar) + sizeof(size32_t);
  3459. }
  3460. else
  3461. rtlUtf8ToUnicode(elemSize / sizeof(UChar), (UChar *) outData, numchars, text);
  3462. }
  3463. break;
  3464. }
  3465. default:
  3466. JNIenv->ReleaseStringUTFChars(elem, text);
  3467. StringBuffer s;
  3468. throw MakeStringException(0, "javaembed: In method %s: Return type mismatch (ECL string type expected)", getReportName(s).str());
  3469. }
  3470. JNIenv->ReleaseStringUTFChars(elem, text);
  3471. JNIenv->DeleteLocalRef(elem);
  3472. if (elemSize != UNKNOWN_LENGTH)
  3473. outData += elemSize;
  3474. }
  3475. }
  3476. else
  3477. {
  3478. StringBuffer s;
  3479. throw MakeStringException(0, "javaembed: In method %s: Return type mismatch (%s[] not supported)", getReportName(s).str(), returnType+2);
  3480. }
  3481. break;
  3482. }
  3483. }
  3484. __isAllResult = false;
  3485. __resultBytes = elemSize == UNKNOWN_LENGTH ? outBytes : elemSize * numResults;
  3486. __result = out.detachdata();
  3487. }
  3488. virtual IRowStream *getDatasetResult(IEngineRowAllocator * _resultAllocator)
  3489. {
  3490. jclass iterClass =JNIenv->GetObjectClass(result.l);
  3491. if (!JNIenv->IsAssignableFrom(iterClass, utilIteratorClass))
  3492. {
  3493. if (JNIenv->IsAssignableFrom(iterClass, langIterableClass))
  3494. {
  3495. result.l = JNIenv->CallObjectMethod(result.l, iterable_iterator);
  3496. }
  3497. else
  3498. {
  3499. StringBuffer s;
  3500. throw MakeStringException(0, "javaembed: In method %s: Java code should return an iterator or iterable object", getReportName(s).str());
  3501. }
  3502. }
  3503. return new JavaRowStream(result.l, _resultAllocator);
  3504. }
  3505. virtual byte * getRowResult(IEngineRowAllocator * _resultAllocator)
  3506. {
  3507. RtlDynamicRowBuilder rowBuilder(_resultAllocator);
  3508. size32_t len = getRowResult(result.l, rowBuilder);
  3509. return (byte *) rowBuilder.finalizeRowClear(len);
  3510. }
  3511. virtual size32_t getTransformResult(ARowBuilder & builder)
  3512. {
  3513. return getRowResult(result.l, builder);
  3514. }
  3515. virtual void bindBooleanParam(const char *name, bool val)
  3516. {
  3517. if (*argsig != 'Z')
  3518. typeError("BOOLEAN");
  3519. argsig++;
  3520. jvalue v;
  3521. v.z = val;
  3522. addArg(v);
  3523. }
  3524. virtual void bindDataParam(const char *name, size32_t len, const void *val)
  3525. {
  3526. if (argsig[0] != '[' || argsig[1] != 'B')
  3527. typeError("DATA");
  3528. argsig += 2;
  3529. jvalue v;
  3530. jbyteArray javaData = JNIenv->NewByteArray(len);
  3531. JNIenv->SetByteArrayRegion(javaData, 0, len, (jbyte *) val);
  3532. v.l = javaData;
  3533. addArg(v);
  3534. }
  3535. virtual void bindFloatParam(const char *name, float val)
  3536. {
  3537. // Could argue that the size should match...
  3538. jvalue v;
  3539. switch(*argsig)
  3540. {
  3541. case 'D':
  3542. v.d = val;
  3543. break;
  3544. case 'F':
  3545. v.f = val;
  3546. break;
  3547. default:
  3548. typeError("REAL");
  3549. break;
  3550. }
  3551. argsig++;
  3552. addArg(v);
  3553. }
  3554. virtual void bindRealParam(const char *name, double val)
  3555. {
  3556. jvalue v;
  3557. switch(*argsig)
  3558. {
  3559. case 'D':
  3560. v.d = val;
  3561. break;
  3562. case 'F':
  3563. v.f = val;
  3564. break;
  3565. default:
  3566. typeError("REAL");
  3567. break;
  3568. }
  3569. argsig++;
  3570. addArg(v);
  3571. }
  3572. virtual void bindSignedSizeParam(const char *name, int size, __int64 val)
  3573. {
  3574. bindSignedParam(name, val);
  3575. }
  3576. virtual void bindSignedParam(const char *name, __int64 val)
  3577. {
  3578. jvalue v;
  3579. switch(*argsig)
  3580. {
  3581. case 'I':
  3582. v.i = val;
  3583. break;
  3584. case 'J':
  3585. v.j = val;
  3586. break;
  3587. case 'S':
  3588. v.s = val;
  3589. break;
  3590. case 'B':
  3591. v.b = val;
  3592. break;
  3593. default:
  3594. typeError("INTEGER");
  3595. break;
  3596. }
  3597. argsig++;
  3598. addArg(v);
  3599. }
  3600. virtual void bindUnsignedSizeParam(const char *name, int size, unsigned __int64 val)
  3601. {
  3602. StringBuffer s;
  3603. throw MakeStringException(MSGAUD_user, 0, "javaembed: In method %s: Unsigned parameters not supported", getReportName(s).str()); // Java doesn't support unsigned
  3604. }
  3605. virtual void bindUnsignedParam(const char *name, unsigned __int64 val)
  3606. {
  3607. if (!strchr(importName, '.') && argcount==0) // Could require a flag, or a special parameter name...
  3608. {
  3609. if (!val)
  3610. {
  3611. StringBuffer s;
  3612. throw MakeStringException(MSGAUD_user, 0, "javaembed: In method %s: Null value passed for \"this\"", getReportName(s).str());
  3613. }
  3614. instance = (jobject) val;
  3615. persistMode = persistSupplied;
  3616. if (JNIenv->GetObjectRefType(instance) != JNIGlobalRefType)
  3617. {
  3618. StringBuffer s;
  3619. throw MakeStringException(MSGAUD_user, 0, "javaembed: In method %s: Invalid value passed for \"this\"", getReportName(s).str());
  3620. }
  3621. jclass newJavaClass = JNIenv->GetObjectClass(instance);
  3622. if (!JNIenv->IsSameObject(newJavaClass, javaClass))
  3623. {
  3624. if (javaClass)
  3625. {
  3626. JNIenv->DeleteGlobalRef(javaClass);
  3627. javaClass = nullptr;
  3628. }
  3629. if (classLoader)
  3630. {
  3631. JNIenv->DeleteGlobalRef(classLoader);
  3632. classLoader = nullptr;
  3633. }
  3634. loadFunction(classpath, 0, nullptr);
  3635. }
  3636. reinit();
  3637. }
  3638. else
  3639. {
  3640. // We could match a java class, to allow objects returned from one embed to be passed as parameters to another
  3641. StringBuffer s;
  3642. throw MakeStringException(MSGAUD_user, 0, "javaembed: In method %s: Unsigned parameters not supported", getReportName(s).str()); // Java doesn't support unsigned
  3643. }
  3644. }
  3645. virtual void bindStringParam(const char *name, size32_t len, const char *val)
  3646. {
  3647. jvalue v;
  3648. switch(*argsig)
  3649. {
  3650. case 'C':
  3651. rtlStrToUnicode(1, &v.c, len, val);
  3652. argsig++;
  3653. break;
  3654. case 'L':
  3655. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  3656. {
  3657. argsig += 18;
  3658. unsigned unicodeChars;
  3659. UChar *unicode;
  3660. rtlStrToUnicodeX(unicodeChars, unicode, len, val);
  3661. v.l = JNIenv->NewString(unicode, unicodeChars);
  3662. rtlFree(unicode);
  3663. break;
  3664. }
  3665. // fall into ...
  3666. default:
  3667. typeError("STRING");
  3668. break;
  3669. }
  3670. addArg(v);
  3671. }
  3672. virtual void bindVStringParam(const char *name, const char *val)
  3673. {
  3674. bindStringParam(name, strlen(val), val);
  3675. }
  3676. virtual void bindUTF8Param(const char *name, size32_t numchars, const char *val)
  3677. {
  3678. jvalue v;
  3679. switch(*argsig)
  3680. {
  3681. case 'C':
  3682. rtlUtf8ToUnicode(1, &v.c, numchars, val);
  3683. argsig++;
  3684. break;
  3685. case 'L':
  3686. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  3687. {
  3688. argsig += 18;
  3689. unsigned unicodeChars;
  3690. UChar *unicode;
  3691. rtlUtf8ToUnicodeX(unicodeChars, unicode, numchars, val);
  3692. v.l = JNIenv->NewString(unicode, unicodeChars);
  3693. rtlFree(unicode);
  3694. break;
  3695. }
  3696. // fall into ...
  3697. default:
  3698. typeError("UTF8");
  3699. break;
  3700. }
  3701. addArg(v);
  3702. }
  3703. virtual void bindUnicodeParam(const char *name, size32_t numchars, const UChar *val)
  3704. {
  3705. jvalue v;
  3706. switch(*argsig)
  3707. {
  3708. case 'C':
  3709. rtlUnicodeToUnicode(1, &v.c, numchars, val);
  3710. argsig++;
  3711. break;
  3712. case 'L':
  3713. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  3714. {
  3715. argsig += 18;
  3716. v.l = JNIenv->NewString(val, numchars);
  3717. break;
  3718. }
  3719. // fall into ...
  3720. default:
  3721. typeError("UNICODE");
  3722. break;
  3723. }
  3724. addArg(v);
  3725. }
  3726. virtual void bindSetParam(const char *name, int _elemType, size32_t elemSize, bool isAll, size32_t totalBytes, const void *setData)
  3727. {
  3728. jvalue v;
  3729. if (*argsig != '[')
  3730. typeError("SET");
  3731. argsig++;
  3732. type_t elemType = (type_t) _elemType;
  3733. int numElems = totalBytes / elemSize;
  3734. switch(*argsig)
  3735. {
  3736. case 'Z':
  3737. checkType(type_boolean, sizeof(jboolean), elemType, elemSize);
  3738. v.l = JNIenv->NewBooleanArray(numElems);
  3739. JNIenv->SetBooleanArrayRegion((jbooleanArray) v.l, 0, numElems, (jboolean *) setData);
  3740. break;
  3741. case 'B':
  3742. checkType(type_int, sizeof(jbyte), elemType, elemSize);
  3743. v.l = JNIenv->NewByteArray(numElems);
  3744. JNIenv->SetByteArrayRegion((jbyteArray) v.l, 0, numElems, (jbyte *) setData);
  3745. break;
  3746. case 'C':
  3747. // we COULD map to a set of string1, but is there any point?
  3748. typeError("");
  3749. break;
  3750. case 'S':
  3751. checkType(type_int, sizeof(jshort), elemType, elemSize);
  3752. v.l = JNIenv->NewShortArray(numElems);
  3753. JNIenv->SetShortArrayRegion((jshortArray) v.l, 0, numElems, (jshort *) setData);
  3754. break;
  3755. case 'I':
  3756. checkType(type_int, sizeof(jint), elemType, elemSize);
  3757. v.l = JNIenv->NewIntArray(numElems);
  3758. JNIenv->SetIntArrayRegion((jintArray) v.l, 0, numElems, (jint *) setData);
  3759. break;
  3760. case 'J':
  3761. checkType(type_int, sizeof(jlong), elemType, elemSize);
  3762. v.l = JNIenv->NewLongArray(numElems);
  3763. JNIenv->SetLongArrayRegion((jlongArray) v.l, 0, numElems, (jlong *) setData);
  3764. break;
  3765. case 'F':
  3766. checkType(type_real, sizeof(jfloat), elemType, elemSize);
  3767. v.l = JNIenv->NewFloatArray(numElems);
  3768. JNIenv->SetFloatArrayRegion((jfloatArray) v.l, 0, numElems, (jfloat *) setData);
  3769. break;
  3770. case 'D':
  3771. checkType(type_real, sizeof(jdouble), elemType, elemSize);
  3772. v.l = JNIenv->NewDoubleArray(numElems);
  3773. JNIenv->SetDoubleArrayRegion((jdoubleArray) v.l, 0, numElems, (jdouble *) setData);
  3774. break;
  3775. case 'L':
  3776. if (strncmp(argsig, "Ljava/lang/String;", 18) == 0)
  3777. {
  3778. argsig += 17; // Yes, 17, because we increment again at the end of the case
  3779. const byte *inData = (const byte *) setData;
  3780. const byte *endData = inData + totalBytes;
  3781. if (elemSize == UNKNOWN_LENGTH)
  3782. {
  3783. numElems = 0;
  3784. // Will need 2 passes to work out how many elements there are in the set :(
  3785. while (inData < endData)
  3786. {
  3787. int thisSize;
  3788. switch (elemType)
  3789. {
  3790. case type_varstring:
  3791. thisSize = strlen((const char *) inData) + 1;
  3792. break;
  3793. case type_string:
  3794. thisSize = * (size32_t *) inData + sizeof(size32_t);
  3795. break;
  3796. case type_unicode:
  3797. thisSize = (* (size32_t *) inData) * sizeof(UChar) + sizeof(size32_t);
  3798. break;
  3799. case type_utf8:
  3800. thisSize = rtlUtf8Size(* (size32_t *) inData, inData + sizeof(size32_t)) + sizeof(size32_t);;
  3801. break;
  3802. default:
  3803. typeError("STRING");
  3804. }
  3805. inData += thisSize;
  3806. numElems++;
  3807. }
  3808. inData = (const byte *) setData;
  3809. }
  3810. int idx = 0;
  3811. v.l = JNIenv->NewObjectArray(numElems, langStringClass, NULL);
  3812. while (inData < endData)
  3813. {
  3814. jstring thisElem;
  3815. size32_t thisSize = elemSize;
  3816. switch (elemType)
  3817. {
  3818. case type_varstring:
  3819. {
  3820. size32_t numChars = strlen((const char *) inData);
  3821. unsigned unicodeChars;
  3822. rtlDataAttr unicode;
  3823. rtlStrToUnicodeX(unicodeChars, unicode.refustr(), numChars, (const char *) inData);
  3824. thisElem = JNIenv->NewString(unicode.getustr(), unicodeChars);
  3825. if (elemSize == UNKNOWN_LENGTH)
  3826. thisSize = numChars + 1;
  3827. break;
  3828. }
  3829. case type_string:
  3830. {
  3831. if (elemSize == UNKNOWN_LENGTH)
  3832. {
  3833. thisSize = * (size32_t *) inData;
  3834. inData += sizeof(size32_t);
  3835. }
  3836. unsigned unicodeChars;
  3837. rtlDataAttr unicode;
  3838. rtlStrToUnicodeX(unicodeChars, unicode.refustr(), thisSize, (const char *) inData);
  3839. thisElem = JNIenv->NewString(unicode.getustr(), unicodeChars);
  3840. break;
  3841. }
  3842. case type_unicode:
  3843. {
  3844. if (elemSize == UNKNOWN_LENGTH)
  3845. {
  3846. thisSize = (* (size32_t *) inData) * sizeof(UChar); // NOTE - it's in chars...
  3847. inData += sizeof(size32_t);
  3848. }
  3849. thisElem = JNIenv->NewString((const UChar *) inData, thisSize / sizeof(UChar));
  3850. break;
  3851. }
  3852. case type_utf8:
  3853. {
  3854. assertex (elemSize == UNKNOWN_LENGTH);
  3855. size32_t numChars = * (size32_t *) inData;
  3856. inData += sizeof(size32_t);
  3857. unsigned unicodeChars;
  3858. rtlDataAttr unicode;
  3859. rtlUtf8ToUnicodeX(unicodeChars, unicode.refustr(), numChars, (const char *) inData);
  3860. thisElem = JNIenv->NewString(unicode.getustr(), unicodeChars);
  3861. thisSize = rtlUtf8Size(numChars, inData);
  3862. break;
  3863. }
  3864. default:
  3865. typeError("STRING");
  3866. }
  3867. inData += thisSize;
  3868. JNIenv->SetObjectArrayElement((jobjectArray) v.l, idx, thisElem);
  3869. JNIenv->DeleteLocalRef(thisElem);
  3870. idx++;
  3871. }
  3872. }
  3873. else
  3874. typeError("");
  3875. break;
  3876. default:
  3877. throwUnexpected();
  3878. }
  3879. argsig++;
  3880. addArg(v);
  3881. }
  3882. virtual void bindRowParam(const char *name, IOutputMetaData & metaVal, const byte *val) override
  3883. {
  3884. if (*argsig != 'L') // should tell us the type of the object we need to create to pass in
  3885. typeError("RECORD");
  3886. // Class name is from the char after the L up to the first ;
  3887. const char *tail = strchr(argsig, ';');
  3888. if (!tail)
  3889. typeError("RECORD");
  3890. StringAttr className(argsig+1, tail - (argsig+1));
  3891. argsig = tail+1;
  3892. const RtlTypeInfo *typeInfo = metaVal.queryTypeInfo();
  3893. assertex(typeInfo);
  3894. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  3895. JavaObjectBuilder javaBuilder((CheckedJNIEnv *)JNIenv, &dummyField, loadClass(className));
  3896. typeInfo->process(val, val, &dummyField, javaBuilder); // Creates a java object from the incoming ECL row
  3897. jvalue v;
  3898. v.l = javaBuilder.getObject();
  3899. addArg(v);
  3900. }
  3901. virtual IInterface *bindParamWriter(IInterface *esdl, const char *esdlservice, const char *esdltype, const char *name)
  3902. {
  3903. if (*argsig != 'L') // should tell us the type of the object we need to create to pass in
  3904. typeError("OBJECT");
  3905. // Class name is from the char after the L up to the first ;
  3906. const char *tail = strchr(argsig, ';');
  3907. if (!tail)
  3908. typeError("OBJECT");
  3909. StringAttr className(argsig+1, tail - (argsig+1));
  3910. argsig = tail+1;
  3911. Owned<JavaXmlBuilder> writer = new JavaXmlBuilder(JNIenv, dynamic_cast<IEsdlDefinition*>(esdl), esdlservice, esdltype);
  3912. writer->initWriter();
  3913. return (IXmlWriter*)writer.getClear();
  3914. }
  3915. virtual void paramWriterCommit(IInterface *writer)
  3916. {
  3917. JavaXmlBuilder *javaWriter = dynamic_cast<JavaXmlBuilder*>(writer);
  3918. if (!javaWriter)
  3919. throw MakeStringException(0, "javaembed: Invalid object writer for %s", signature.get());
  3920. jvalue v;
  3921. v.l = javaWriter->getObject();
  3922. addArg(v);
  3923. }
  3924. virtual void bindDatasetParam(const char *name, IOutputMetaData & metaVal, IRowStream * val)
  3925. {
  3926. jvalue v;
  3927. char argsigStart = *argsig;
  3928. switch (argsigStart)
  3929. {
  3930. case '[':
  3931. case '<':
  3932. ++argsig;
  3933. break;
  3934. case 'L':
  3935. if (strncmp(argsig, "Ljava/util/Iterator<", 20) == 0)
  3936. {
  3937. argsig += 20;
  3938. break;
  3939. }
  3940. /* no break */
  3941. default:
  3942. typeError("DATASET");
  3943. }
  3944. if (*argsig != 'L') // should tell us the type of the object we need to create to pass in
  3945. typeError("DATASET");
  3946. // Class name is from the char after the L up to the first ;
  3947. const char *tail = strchr(argsig, ';');
  3948. if (!tail)
  3949. typeError("RECORD");
  3950. StringAttr className(argsig+1, tail - (argsig+1));
  3951. argsig = tail+1;
  3952. if (argsigStart=='L')
  3953. {
  3954. if (argsig[0] != '>' || argsig[1] != ';')
  3955. typeError("DATASET");
  3956. argsig += 2;
  3957. }
  3958. if (argsigStart=='[')
  3959. {
  3960. // Pass in an array of objects
  3961. PointerArrayOf<_jobject> allRows;
  3962. const RtlTypeInfo *typeInfo = metaVal.queryTypeInfo();
  3963. assertex(typeInfo);
  3964. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  3965. jclass Class = loadClass(className);
  3966. JavaObjectBuilder javaBuilder((CheckedJNIEnv *) JNIenv, &dummyField, Class);
  3967. for (;;)
  3968. {
  3969. roxiemem::OwnedConstRoxieRow thisRow = val->ungroupedNextRow();
  3970. if (!thisRow)
  3971. break;
  3972. const byte *brow = (const byte *) thisRow.get();
  3973. typeInfo->process(brow, brow, &dummyField, javaBuilder); // Creates a java object from the incoming ECL row
  3974. allRows.append(javaBuilder.getObject());
  3975. }
  3976. jobjectArray array = JNIenv->NewObjectArray(allRows.length(), Class, NULL);
  3977. ForEachItemIn(idx, allRows)
  3978. {
  3979. JNIenv->SetObjectArrayElement(array, idx, allRows.item(idx));
  3980. }
  3981. v.l = array;
  3982. }
  3983. else
  3984. {
  3985. // Pass in an iterator
  3986. // Create a java object of type com.HPCCSystems.HpccUtils - this acts as a proxy for the iterator
  3987. JNIenv->ExceptionClear();
  3988. jvalue param;
  3989. const RtlTypeInfo *typeInfo = metaVal.queryTypeInfo();
  3990. ECLDatasetIterator *iterator = new ECLDatasetIterator((CheckedJNIEnv *) JNIenv, typeInfo, loadClass(className), val);
  3991. param.j = (jlong) iterator;
  3992. iterators.append(*iterator);
  3993. jobject proxy = JNIenv->NewObject(hpccIteratorClass, hi_constructor, param, JNIenv->NewStringUTF(helperLibraryName));
  3994. v.l = proxy;
  3995. }
  3996. addArg(v);
  3997. }
  3998. virtual void writeResult(IInterface *esdl, const char *esdlservice, const char *esdltype, IInterface *writer)
  3999. {
  4000. return sharedCtx->writeObjectResult(result.l, dynamic_cast<IEsdlDefinition*>(esdl), esdlservice, esdltype, dynamic_cast<IXmlWriter*>(writer));
  4001. }
  4002. virtual void importFunction(size32_t lenChars, const char *utf) override
  4003. {
  4004. if (!javaClass)
  4005. {
  4006. size32_t bytes = rtlUtf8Size(lenChars, utf);
  4007. importName.set(utf, bytes);
  4008. if (strchr(importName, '.'))
  4009. loadFunction(classpath, 0, nullptr);
  4010. }
  4011. if (javaClass)
  4012. reinit();
  4013. }
  4014. void bindActivityParam()
  4015. {
  4016. // Note: We don't require that the function takes an activityCtx parameter - if they don't care, they can omit the param
  4017. if (strncmp(argsig, "Lcom/HPCCSystems/ActivityContext;", 33) == 0)
  4018. {
  4019. argsig += 33;
  4020. jvalue v;
  4021. v.l = JNIenv->NewObject(hpccIteratorClass, hi_constructor, activityContext, JNIenv->NewStringUTF(helperLibraryName));
  4022. addArg(v);
  4023. }
  4024. }
  4025. IException *translateException(IException *E)
  4026. {
  4027. StringBuffer msg;
  4028. E->errorMessage(msg);
  4029. const char *text = msg;
  4030. if (strncmp(text, "javaembed: ", 11)==0)
  4031. text += 11;
  4032. auto code = E->errorCode();
  4033. auto aud = E->errorAudience();
  4034. E->Release();
  4035. StringBuffer s;
  4036. return makeStringExceptionV(aud, code, "javaembed: In method %s: %s", getReportName(s).str(), text);
  4037. }
  4038. IException *resultMismatchException(const char *expected)
  4039. {
  4040. StringBuffer s;
  4041. return makeStringExceptionV(0, "javaembed: In method %s: Type mismatch on result (%s expected)", getReportName(s).str(), expected);
  4042. }
  4043. virtual void callFunction()
  4044. {
  4045. try
  4046. {
  4047. if (*argsig != ')')
  4048. throw MakeStringException(0, "Too few ECL parameters passed for Java signature %s", signature.get());
  4049. JNIenv->ExceptionClear();
  4050. if (nonStatic)
  4051. {
  4052. if (streq(methodName, "<init>"))
  4053. {
  4054. if (!instance)
  4055. {
  4056. if (persistMode == persistNone)
  4057. throw MakeStringException(0, "Cannot return object without persist");
  4058. StringBuffer scopeKey;
  4059. getScopeKey(scopeKey);
  4060. PersistedObjectCriticalBlock persistBlock;
  4061. persistBlock.enter(persistMode==persistThread ? sharedCtx->getLocalObject(JNIenv, scopeKey) : globalState->getGlobalObject(JNIenv, scopeKey));
  4062. instance = persistBlock.getInstance();
  4063. if (instance)
  4064. persistBlock.leave();
  4065. else
  4066. {
  4067. instance = JNIenv->NewGlobalRef(JNIenv->NewObjectA(javaClass, javaMethodID, args), "constructor");
  4068. #ifdef TRACE_GLOBALREF
  4069. StringBuffer myClassName;
  4070. getClassNameForObject(JNIenv, myClassName, instance);
  4071. DBGLOG("Constructed object %p of class %s", instance, myClassName.str());
  4072. #endif
  4073. if (persistMode==persistQuery || persistMode==persistWorkunit || persistMode==persistChannel)
  4074. {
  4075. assertex(engine);
  4076. engine->onTermination(JavaGlobalState::unregister, scopeKey.str(), persistMode==persistWorkunit);
  4077. }
  4078. persistBlock.leave(instance);
  4079. }
  4080. }
  4081. result.l = instance;
  4082. return;
  4083. }
  4084. else if (!instance)
  4085. {
  4086. assertex(persistMode == persistNone); // Any other persist mode should have already created the instance
  4087. instance = createInstance(); // Local object, will be released at exit() from function
  4088. }
  4089. assertex(javaMethodID);
  4090. switch (*returnType)
  4091. {
  4092. case 'C': result.c = JNIenv->CallCharMethodA(instance, javaMethodID, args); break;
  4093. case 'Z': result.z = JNIenv->CallBooleanMethodA(instance, javaMethodID, args); break;
  4094. case 'J': result.j = JNIenv->CallLongMethodA(instance, javaMethodID, args); break;
  4095. case 'F': result.f = JNIenv->CallFloatMethodA(instance, javaMethodID, args); break;
  4096. case 'D': result.d = JNIenv->CallDoubleMethodA(instance, javaMethodID, args); break;
  4097. case 'I': result.i = JNIenv->CallIntMethodA(instance, javaMethodID, args); break;
  4098. case 'S': result.s = JNIenv->CallShortMethodA(instance, javaMethodID, args); break;
  4099. case 'B': result.s = JNIenv->CallByteMethodA(instance, javaMethodID, args); break;
  4100. case '[':
  4101. case 'L': result.l = JNIenv->CallObjectMethodA(instance, javaMethodID, args); break;
  4102. case 'V': JNIenv->CallVoidMethodA(instance, javaMethodID, args); result.l = nullptr; break;
  4103. default: throwUnexpected();
  4104. }
  4105. }
  4106. else
  4107. {
  4108. switch (*returnType)
  4109. {
  4110. case 'C': result.c = JNIenv->CallStaticCharMethodA(javaClass, javaMethodID, args); break;
  4111. case 'Z': result.z = JNIenv->CallStaticBooleanMethodA(javaClass, javaMethodID, args); break;
  4112. case 'J': result.j = JNIenv->CallStaticLongMethodA(javaClass, javaMethodID, args); break;
  4113. case 'F': result.f = JNIenv->CallStaticFloatMethodA(javaClass, javaMethodID, args); break;
  4114. case 'D': result.d = JNIenv->CallStaticDoubleMethodA(javaClass, javaMethodID, args); break;
  4115. case 'I': result.i = JNIenv->CallStaticIntMethodA(javaClass, javaMethodID, args); break;
  4116. case 'S': result.s = JNIenv->CallStaticShortMethodA(javaClass, javaMethodID, args); break;
  4117. case 'B': result.s = JNIenv->CallStaticByteMethodA(javaClass, javaMethodID, args); break;
  4118. case '[':
  4119. case 'L': result.l = JNIenv->CallStaticObjectMethodA(javaClass, javaMethodID, args); break;
  4120. case 'V': JNIenv->CallStaticVoidMethodA(javaClass, javaMethodID, args); result.l = nullptr; break;
  4121. default: throwUnexpected();
  4122. }
  4123. }
  4124. }
  4125. catch (IException *E)
  4126. {
  4127. throw translateException(E);
  4128. }
  4129. }
  4130. virtual void compileEmbeddedScript(size32_t lenChars, const char *_script)
  4131. {
  4132. throwUnexpected();
  4133. }
  4134. virtual void loadCompiledScript(size32_t bytecodeLen, const void *bytecode) override
  4135. {
  4136. if (!javaClass)
  4137. {
  4138. MemoryBuffer b;
  4139. b.setBuffer(bytecodeLen, (void *) bytecode, false);
  4140. b.setEndian(__BIG_ENDIAN);
  4141. uint32_t siglen; b.read(siglen);
  4142. const char *sig = (const char *) b.readDirect(siglen);
  4143. size32_t bytes = rtlUtf8Size(siglen, sig); // MORE - check that this size is serialized in chars not bytes!
  4144. importName.set(sig, bytes);
  4145. loadFunction(classpath, bytecodeLen, (const byte *) bytecode);
  4146. }
  4147. reinit();
  4148. }
  4149. virtual void enter() override
  4150. {
  4151. reenter(nullptr);
  4152. }
  4153. virtual void reenter(ICodeContext *codeCtx) override
  4154. {
  4155. // If we rejig codegen to only call loadCompiledScript etc at construction time, then this will need to do the reinit()
  4156. // until we do, it's too early
  4157. if (codeCtx)
  4158. engine = codeCtx->queryEngineContext();
  4159. else if (flags & EFthreadlocal && persistMode > persistThread)
  4160. {
  4161. StringBuffer s;
  4162. throw MakeStringException(0, "javaembed: In method %s: Workunit must be recompiled to support this persist mode", getReportName(s).str());
  4163. }
  4164. // Create a new frame for local references and increase the capacity
  4165. // of those references to 64 (default is 16)
  4166. JNIenv->PushLocalFrame(64);
  4167. }
  4168. virtual void exit() override
  4169. {
  4170. if (persistMode==persistNone)
  4171. instance = 0; // otherwise we leave it for next call as it saves a lot of time looking it up
  4172. iterators.kill();
  4173. #ifdef FORCE_GC
  4174. forceGC(JNIenv);
  4175. #endif
  4176. JNIenv->PopLocalFrame(nullptr);
  4177. }
  4178. protected:
  4179. __declspec(noreturn) void typeError(const char *ECLtype) __attribute__((noreturn))
  4180. {
  4181. const char *javaType;
  4182. int javaLen = 0;
  4183. switch (*argsig)
  4184. {
  4185. case 'Z': javaType = "boolean"; break;
  4186. case 'B': javaType = "byte"; break;
  4187. case 'C': javaType = "char"; break;
  4188. case 'S': javaType = "short"; break;
  4189. case 'I': javaType = "int"; break;
  4190. case 'J': javaType = "long"; break;
  4191. case 'F': javaType = "float"; break;
  4192. case 'D': javaType = "double"; break;
  4193. case '[': javaType = "array"; break;
  4194. case 'L':
  4195. {
  4196. javaType = argsig+1;
  4197. const char *semi = strchr(argsig, ';');
  4198. if (semi)
  4199. javaLen = semi - javaType;
  4200. break;
  4201. }
  4202. case ')':
  4203. {
  4204. StringBuffer s;
  4205. throw MakeStringException(0, "javaembed: In method %s: Too many ECL parameters passed for Java signature", getReportName(s).str());
  4206. }
  4207. default:
  4208. StringBuffer s;
  4209. throw MakeStringException(0, "javaembed: In method %s: Unrecognized character %c in Java signature", getReportName(s).str(), *argsig);
  4210. }
  4211. if (!javaLen)
  4212. javaLen = strlen(javaType);
  4213. StringBuffer s;
  4214. throw MakeStringException(0, "javaembed: In Method %s: ECL type %s cannot be passed to Java type %.*s", getReportName(s).str(), ECLtype, javaLen, javaType);
  4215. }
  4216. void addArg(jvalue &arg)
  4217. {
  4218. assertex(argcount < MAX_JNI_ARGS);
  4219. args[argcount] = arg;
  4220. argcount++;
  4221. }
  4222. size32_t getRowResult(jobject result, ARowBuilder &builder)
  4223. {
  4224. const RtlTypeInfo *typeInfo = builder.queryAllocator()->queryOutputMeta()->queryTypeInfo();
  4225. assertex(typeInfo);
  4226. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  4227. JavaRowBuilder javaRowBuilder((CheckedJNIEnv *) JNIenv, &dummyField, result);
  4228. return typeInfo->build(builder, 0, &dummyField, javaRowBuilder);
  4229. }
  4230. jclass loadClass(const char *className)
  4231. {
  4232. StringBuffer uclassname(className);
  4233. uclassname.replace('/', '.');
  4234. className=uclassname.str();
  4235. JNIenv->ExceptionClear();
  4236. jmethodID loadClassMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(classLoader), "loadClass","(Ljava/lang/String;)Ljava/lang/Class;");
  4237. jstring classNameString = JNIenv->NewStringUTF(className);
  4238. jclass Class = (jclass) JNIenv->CallObjectMethod(classLoader, loadClassMethod, classNameString);
  4239. return Class;
  4240. }
  4241. StringBuffer &getReportName(StringBuffer &s)
  4242. {
  4243. if (classname.length())
  4244. {
  4245. const char *report = strrchr(classname.str(), '.');
  4246. if (report)
  4247. report++;
  4248. else
  4249. report = classname.str();
  4250. s.append(classname).append('.');
  4251. }
  4252. return s.append(methodName);
  4253. }
  4254. jobject createInstance()
  4255. {
  4256. jmethodID constructor;
  4257. try
  4258. {
  4259. constructor = JNIenv->GetMethodID(javaClass, "<init>", "()V");
  4260. }
  4261. catch (IException *E)
  4262. {
  4263. Owned<IException> e = E;
  4264. throw MakeStringException(0, "parameterless constructor required");
  4265. }
  4266. return JNIenv->NewObject(javaClass, constructor);
  4267. }
  4268. void loadFunction(const char *classpath, size32_t bytecodeLen, const byte *bytecode)
  4269. {
  4270. try
  4271. {
  4272. StringAttr checkedClassName;
  4273. // Name should be in the form class.method:signature
  4274. const char *funcname = strrchr(importName, '.');
  4275. if (funcname)
  4276. {
  4277. classname.clear().append(funcname-importName, importName);
  4278. classname.replace('/', '.');
  4279. funcname++; // skip the '.'
  4280. }
  4281. else
  4282. funcname = importName;
  4283. const char *coloncolon = strstr(funcname, "::");
  4284. if (coloncolon)
  4285. {
  4286. // ClassName::FunctionName syntax - used to check that object passed in is of proper class
  4287. checkedClassName.set(funcname, coloncolon-funcname);
  4288. funcname = coloncolon+2;
  4289. }
  4290. const char *sig = strchr(funcname, ':');
  4291. if (sig)
  4292. {
  4293. methodName.set(funcname, sig-funcname);
  4294. sig++; // skip the ':'
  4295. if (*sig == '@') // indicates a non-static method
  4296. {
  4297. sig++;
  4298. nonStatic = true;
  4299. }
  4300. else
  4301. nonStatic = false;
  4302. signature.set(sig);
  4303. }
  4304. else
  4305. methodName.set(funcname);
  4306. bool isConstructor = streq(methodName, "<init>");
  4307. {
  4308. PersistedObjectCriticalBlock persistBlock;
  4309. StringBuffer scopeKey;
  4310. if (nonStatic && !instance && persistMode >= persistThread && !isConstructor)
  4311. {
  4312. // If a persist scope is specified, we may want to use a pre-existing object. If we do we share its classloader, class, etc.
  4313. assertex(classname.length()); // MORE - what does this imply?
  4314. getScopeKey(scopeKey);
  4315. persistBlock.enter(persistMode==persistThread ? sharedCtx->getLocalObject(JNIenv, scopeKey) : globalState->getGlobalObject(JNIenv, scopeKey));
  4316. instance = persistBlock.getInstance();
  4317. if (instance)
  4318. persistBlock.leave();
  4319. }
  4320. if (instance)
  4321. {
  4322. javaClass = (jclass) JNIenv->NewGlobalRef(JNIenv->GetObjectClass(instance), "javaClass");
  4323. classLoader = JNIenv->NewGlobalRef(getClassLoader(JNIenv, javaClass), "classLoader");
  4324. sharedCtx->setThreadClassLoader(classLoader);
  4325. }
  4326. if (!javaClass)
  4327. {
  4328. if (!classname)
  4329. throw MakeStringException(MSGAUD_user, 0, "Invalid import name - Expected classname.methodname:signature");
  4330. classLoader = JNIenv->NewGlobalRef(sharedCtx->getThreadClassLoader(classpath, classname, bytecodeLen, bytecode), "classLoader");
  4331. sharedCtx->setThreadClassLoader(classLoader);
  4332. jmethodID loadClassMethod = JNIenv->GetMethodID(JNIenv->GetObjectClass(classLoader), "loadClass","(Ljava/lang/String;)Ljava/lang/Class;");
  4333. try
  4334. {
  4335. javaClass = (jclass) JNIenv->CallObjectMethod(classLoader, loadClassMethod, JNIenv->NewStringUTF(classname));
  4336. }
  4337. catch (IException *E)
  4338. {
  4339. Owned<IException> e = E;
  4340. throw makeWrappedExceptionV(E, E->errorCode(), "Failed to resolve class name %s", classname.str());
  4341. }
  4342. javaClass = (jclass) JNIenv->NewGlobalRef(javaClass, "javaClass");
  4343. }
  4344. if (nonStatic && !instance && !isConstructor && persistMode != persistNone)
  4345. {
  4346. instance = createInstance();
  4347. #ifdef TRACE_GLOBALREF
  4348. StringBuffer myClassName;
  4349. getClassNameForObject(JNIenv, myClassName, instance);
  4350. DBGLOG("Created object %p of class %s", instance, myClassName.str());
  4351. #endif
  4352. if (persistBlock.locked()) // I think this should always be true?
  4353. {
  4354. instance = JNIenv->NewGlobalRef(instance, "createInstance");
  4355. if (persistMode==persistQuery || persistMode==persistWorkunit || persistMode==persistChannel)
  4356. {
  4357. assertex(engine);
  4358. engine->onTermination(JavaGlobalState::unregister, scopeKey.str(), persistMode==persistWorkunit);
  4359. }
  4360. persistBlock.leave(instance);
  4361. }
  4362. }
  4363. }
  4364. if (!signature)
  4365. {
  4366. getSignature(signature, JNIenv, javaClass, funcname);
  4367. if (signature.str()[0]=='@')
  4368. {
  4369. nonStatic = true;
  4370. signature.set(signature.str()+1);
  4371. }
  4372. else
  4373. nonStatic = false;
  4374. }
  4375. StringBuffer javaSignature;
  4376. patchSignature(javaSignature, signature);
  4377. if (nonStatic)
  4378. javaMethodID = JNIenv->GetMethodID(javaClass, methodName, javaSignature);
  4379. else
  4380. javaMethodID = JNIenv->GetStaticMethodID(javaClass, methodName, javaSignature);
  4381. if (checkedClassName)
  4382. {
  4383. StringBuffer myClassName;
  4384. getClassNameForObject(JNIenv, myClassName, instance);
  4385. #ifdef CHECK_JNI
  4386. DBGLOG("Checking class name %s for %p matches %s for function %s", myClassName.str(), instance, checkedClassName.str(), methodName.str());
  4387. #endif
  4388. const char *shortClassName = strrchr(myClassName, '.');
  4389. if (shortClassName)
  4390. shortClassName++;
  4391. else
  4392. shortClassName = myClassName;
  4393. if (!streq(checkedClassName, shortClassName))
  4394. throw MakeStringException(0, "Object class %s does not match expected class name %s", shortClassName, checkedClassName.str());
  4395. }
  4396. returnType = strrchr(signature, ')');
  4397. assertex(returnType); // Otherwise how did Java accept it??
  4398. returnType++;
  4399. }
  4400. catch(IException *E)
  4401. {
  4402. throw translateException(E);
  4403. }
  4404. }
  4405. static StringBuffer &patchSignature(StringBuffer &ret, const char *signature)
  4406. {
  4407. // We need to patch up the provided signature - any instances of <classname; need to be replaced by Ljava.utils.iterator
  4408. const char *finger = signature;
  4409. while (finger && *finger)
  4410. {
  4411. if (*finger == '<')
  4412. {
  4413. // If there is a corresponding >, assume it's the 'extended' form and just strip out the bit from < to >
  4414. const char *close = strchr(finger, '>');
  4415. if (close)
  4416. finger = close;
  4417. else
  4418. {
  4419. ret.append("Ljava/util/Iterator;");
  4420. finger = strchr(finger, ';');
  4421. if (!finger)
  4422. throw MakeStringException(MSGAUD_user, 0, "javaembed: Invalid java function signature %s", signature);
  4423. }
  4424. }
  4425. else
  4426. ret.append(*finger);
  4427. finger++;
  4428. }
  4429. return ret;
  4430. }
  4431. StringBuffer &getScopeKey(StringBuffer &ret)
  4432. {
  4433. if (globalScopeKey)
  4434. ret.append(globalScopeKey).append('.');
  4435. ret.append(classname).append('.');
  4436. switch (persistMode)
  4437. {
  4438. case persistThread:
  4439. ret.append(__uint64(GetCurrentThreadId()));
  4440. break;
  4441. case persistGlobal:
  4442. ret.append("global");
  4443. break;
  4444. // Fall into
  4445. case persistWorkunit:
  4446. engine->getQueryId(ret, true);
  4447. break;
  4448. case persistChannel:
  4449. ret.append(nodeNum).append('.');
  4450. case persistQuery:
  4451. engine->getQueryId(ret, false);
  4452. break;
  4453. }
  4454. return ret;
  4455. }
  4456. JavaThreadContext *sharedCtx = nullptr;
  4457. CheckedJNIEnv *JNIenv = nullptr;
  4458. jvalue result = {0};
  4459. StringAttr classpath;
  4460. StringBuffer classname;
  4461. IArrayOf<ECLDatasetIterator> iterators; // to make sure they get freed
  4462. bool nonStatic = false;
  4463. jobject instance = nullptr; // class instance of object to call methods on
  4464. const IThorActivityContext *activityContext = nullptr;
  4465. unsigned flags = 0;
  4466. unsigned nodeNum = 0;
  4467. StringAttr globalScopeKey;
  4468. PersistMode persistMode = persistNone; // Defines the lifetime of the java object for which this is called.
  4469. // The following members are set up the first time a method is called only
  4470. IEngineContext *engine = nullptr;
  4471. jclass javaClass = nullptr;
  4472. jobject classLoader = nullptr;
  4473. jmethodID javaMethodID = nullptr;
  4474. StringAttr methodName;
  4475. StringAttr importName;
  4476. StringAttr signature;
  4477. const char *returnType = nullptr; // A pointer within signature
  4478. // These point to the current arg/signature byte as we are binding
  4479. int argcount = 0;
  4480. jvalue args[MAX_JNI_ARGS];
  4481. const char *argsig = nullptr; // A pointer within signature
  4482. void reinit()
  4483. {
  4484. argcount = 0;
  4485. argsig = signature;
  4486. assertex(*argsig == '(');
  4487. argsig++;
  4488. if (activityContext)
  4489. bindActivityParam();
  4490. }
  4491. };
  4492. static __thread JavaThreadContext* threadContext; // We reuse per thread, for speed
  4493. static __thread ThreadTermFunc threadHookChain;
  4494. static void releaseContext(bool isPooled)
  4495. {
  4496. if (threadContext)
  4497. {
  4498. threadContext->endThread();
  4499. if (!isPooled)
  4500. {
  4501. delete threadContext;
  4502. threadContext = NULL;
  4503. }
  4504. }
  4505. if (threadHookChain)
  4506. {
  4507. (*threadHookChain)(isPooled);
  4508. }
  4509. }
  4510. static JavaThreadContext *queryContext()
  4511. {
  4512. if (!threadContext)
  4513. {
  4514. threadContext = new JavaThreadContext;
  4515. threadHookChain = addThreadTermFunc(releaseContext);
  4516. }
  4517. return threadContext;
  4518. }
  4519. static CheckedJNIEnv *queryJNIEnv()
  4520. {
  4521. return queryContext()->JNIenv;
  4522. }
  4523. class JavaEmbedServiceContext : public CInterfaceOf<IEmbedServiceContext>
  4524. {
  4525. public:
  4526. JavaEmbedServiceContext(JavaThreadContext *_sharedCtx, const char *service, const char *_options)
  4527. : sharedCtx(_sharedCtx), Class(0), options(_options), className(service), object(0)
  4528. {
  4529. StringArray opts;
  4530. opts.appendList(options, ",");
  4531. ForEachItemIn(idx, opts)
  4532. {
  4533. const char *opt = opts.item(idx);
  4534. const char *val = strchr(opt, '=');
  4535. if (val)
  4536. {
  4537. StringBuffer optName(val-opt, opt);
  4538. val++;
  4539. if (strieq(optName, "classpath"))
  4540. classpath.set(val);
  4541. else
  4542. throw MakeStringException(0, "javaembed: Unknown option %s", optName.str());
  4543. }
  4544. }
  4545. }
  4546. ~JavaEmbedServiceContext()
  4547. {
  4548. if (object)
  4549. sharedCtx->JNIenv->DeleteGlobalRef(object);
  4550. if (Class)
  4551. sharedCtx->JNIenv->DeleteGlobalRef(Class);
  4552. }
  4553. void init()
  4554. {
  4555. jobject classLoader = sharedCtx->getThreadClassLoader();
  4556. jmethodID loadClassMethod = sharedCtx->JNIenv->GetMethodID(sharedCtx->JNIenv->GetObjectClass(classLoader), "loadClass","(Ljava/lang/String;)Ljava/lang/Class;");
  4557. jstring methodString = sharedCtx->JNIenv->NewStringUTF(className);
  4558. Class = (jclass) sharedCtx->JNIenv->NewGlobalRef(sharedCtx->JNIenv->CallObjectMethod(classLoader, loadClassMethod, methodString), "Class");
  4559. jmethodID constructor = sharedCtx->JNIenv->GetMethodID(Class, "<init>", "()V");
  4560. object = sharedCtx->JNIenv->NewGlobalRef(sharedCtx->JNIenv->NewObject(Class, constructor), "constructed");
  4561. }
  4562. virtual IEmbedFunctionContext *createFunctionContext(const char *function)
  4563. {
  4564. if (!object)
  4565. return NULL;
  4566. Owned<JavaEmbedImportContext> fctx = new JavaEmbedImportContext(nullptr, queryContext(), object, 0, options, nullptr);
  4567. fctx->importFunction(rtlUtf8Length(strlen(function), function), function);
  4568. return fctx.getClear();
  4569. }
  4570. protected:
  4571. JavaThreadContext *sharedCtx;
  4572. StringBuffer className;
  4573. jclass Class;
  4574. jobject object;
  4575. StringAttr classpath;
  4576. StringAttr options;
  4577. };
  4578. class JavaEmbedContext : public CInterfaceOf<IEmbedContext>
  4579. {
  4580. public:
  4581. virtual IEmbedFunctionContext *createFunctionContext(unsigned flags, const char *options) override
  4582. {
  4583. return createFunctionContextEx(nullptr, nullptr, flags, options);
  4584. }
  4585. virtual IEmbedFunctionContext *createFunctionContextEx(ICodeContext * ctx, const IThorActivityContext *activityCtx, unsigned flags, const char *options) override
  4586. {
  4587. return new JavaEmbedImportContext(ctx, queryContext(), nullptr, flags, options, activityCtx);
  4588. }
  4589. virtual IEmbedServiceContext *createServiceContext(const char *service, unsigned flags, const char *options) override
  4590. {
  4591. Owned<JavaEmbedServiceContext> serviceContext = new JavaEmbedServiceContext(queryContext(), service, options);
  4592. serviceContext->init();
  4593. return serviceContext.getClear();
  4594. }
  4595. };
  4596. static JavaEmbedContext embedContext;
  4597. extern DECL_EXPORT IEmbedContext* queryEmbedContext()
  4598. {
  4599. return &embedContext;
  4600. }
  4601. extern DECL_EXPORT IEmbedContext* getEmbedContext()
  4602. {
  4603. return new JavaEmbedContext;
  4604. }
  4605. static bool isValidIdentifier(const char *source)
  4606. {
  4607. return isalnum(*source) || *source=='_' || *source=='$' || ::readUtf8Size(source)>1; // This is not strictly accurate but probably good enough
  4608. }
  4609. static bool isFullClassFile(StringBuffer &className, bool &seenPublic, size32_t len, const char *source)
  4610. {
  4611. // A heuristic to determine whether the supplied embedded source is a full class file or just a single method
  4612. // Basically, if we see keyword "class" before we see { then we assume it's a full file
  4613. // Also track whether the public keyword has been supplied
  4614. bool inLineComment = false;
  4615. bool inBlockComment = false;
  4616. seenPublic = false;
  4617. while (len)
  4618. {
  4619. if (inLineComment)
  4620. {
  4621. if (*source=='\n')
  4622. inLineComment = false;
  4623. }
  4624. else if (inBlockComment)
  4625. {
  4626. if (*source=='*' && len > 1 && source[1]=='/')
  4627. {
  4628. inBlockComment = false;
  4629. len--;
  4630. source++;
  4631. }
  4632. }
  4633. else switch(*source)
  4634. {
  4635. case '/':
  4636. if (len > 1)
  4637. {
  4638. if (source[1]=='*')
  4639. {
  4640. inBlockComment = true;
  4641. len--;
  4642. source++;
  4643. }
  4644. else if (source[1]=='/')
  4645. inLineComment = true;
  4646. }
  4647. break;
  4648. case '{':
  4649. return false;
  4650. default:
  4651. if (isValidIdentifier(source))
  4652. {
  4653. const char *start = source;
  4654. while (len && isValidIdentifier(source))
  4655. {
  4656. source+=::readUtf8Size(source);
  4657. len--;
  4658. }
  4659. if (source-start == 5 && memcmp(start, "class", source-start)==0)
  4660. {
  4661. while (len && isspace(*source)) // MORE - a comment between the keyword and the classname will fail - tough.
  4662. {
  4663. source += ::readUtf8Size(source);
  4664. len--;
  4665. }
  4666. start = source;
  4667. while (len && isValidIdentifier(source))
  4668. {
  4669. source += ::readUtf8Size(source);
  4670. len--;
  4671. }
  4672. className.append(source-start, start);
  4673. return true;
  4674. }
  4675. else if (source-start == 6 && memcmp(start, "public", source-start)==0)
  4676. seenPublic = true;
  4677. }
  4678. if (!len)
  4679. return false;
  4680. break;
  4681. }
  4682. source += ::readUtf8Size(source);
  4683. len--;
  4684. }
  4685. // 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...
  4686. return false;
  4687. }
  4688. static bool suppressJavaError(const char *err)
  4689. {
  4690. if (!err || !*err)
  4691. return true;
  4692. if (streq(err, "1 error"))
  4693. return true;
  4694. char *rest;
  4695. if (strtoul(err, &rest, 10) && streq(rest, " errors"))
  4696. return true;
  4697. return false;
  4698. }
  4699. static StringBuffer & cleanupJavaError(StringBuffer &ret, StringBuffer &prefix, const char *err, unsigned lineNumberOffset)
  4700. {
  4701. // Remove filename (as it's generated) and fix up line number. Errors that do not have line number use previous error's line number.
  4702. const char *colon = strchr(err, ':');
  4703. // Java errors are a bit of a pain - if you suppress the ones without line numbers you get too little information, if you don't you get too much
  4704. if (colon && isdigit(colon[1]))
  4705. {
  4706. char *end;
  4707. unsigned lineno = strtoul(colon+1, &end, 10) - lineNumberOffset;
  4708. prefix.clear().appendf("(%u,1)", lineno);
  4709. ret.append(prefix).append(end);
  4710. }
  4711. else if (!suppressJavaError(err))
  4712. ret.append(prefix).appendf(": error: %s", err);
  4713. return ret;
  4714. }
  4715. static void cleanupJavaErrors(StringBuffer &ret, const char *errors, unsigned lineNumberOffset)
  4716. {
  4717. StringArray errlines;
  4718. errlines.appendList(errors, "\n", false);
  4719. StringBuffer prefix;
  4720. ForEachItemIn(idx, errlines)
  4721. {
  4722. StringBuffer cleaned;
  4723. cleanupJavaError(cleaned, prefix, errlines.item(idx), lineNumberOffset);
  4724. if (cleaned.length())
  4725. ret.append(cleaned).append('\n');
  4726. }
  4727. }
  4728. static thread_local unsigned prevHash = 0;
  4729. static thread_local MemoryBuffer prevCompile;
  4730. void doPrecompile(size32_t & __lenResult, void * & __result, const char *funcName, size32_t charsBody, const char * body, const char *argNames, const char *compilerOptions, const char *persistOptions, StringBuffer &errors, bool checking)
  4731. {
  4732. unsigned sizeBody = rtlUtf8Size(charsBody, body); // size in bytes
  4733. unsigned hash = rtlHash32Data(sizeBody,body,0xcafebabe);
  4734. if (hash==prevHash) // Reusing result from the syntax check that normally immediately precedes a precompile
  4735. {
  4736. __lenResult = prevCompile.length();
  4737. __result = prevCompile.detachOwn();
  4738. prevHash = 0;
  4739. return;
  4740. }
  4741. StringAttr globalScope;
  4742. PersistMode persistMode = getPersistMode(persistOptions, globalScope);
  4743. StringBuffer tmpDirName;
  4744. getTempFilePath(tmpDirName, "javaembed", nullptr);
  4745. tmpDirName.append(PATHSEPCHAR).append("tmp.XXXXXX");
  4746. if (!mkdtemp((char *) tmpDirName.str()))
  4747. throw makeStringExceptionV(0, "Failed to create temporary directory %s (error %d)", tmpDirName.str(), errno);
  4748. Owned<IFile> tempDir = createIFile(tmpDirName);
  4749. StringBuffer classname;
  4750. bool seenPublic = false;
  4751. bool isFullClass = isFullClassFile(classname, seenPublic, charsBody, body); // note - we pass in length in characters, not bytes
  4752. if (!isFullClass)
  4753. classname.set("embed");
  4754. VStringBuffer javafile("%s" PATHSEPSTR "%s.java", tmpDirName.str(), classname.str());
  4755. FILE *source = fopen(javafile.str(), "wt");
  4756. fprintf(source, "package com.HPCCSystems.embed.x%x;\n", hash);
  4757. unsigned lineNumberOffset = 1; // for the /n above
  4758. if (isFullClass)
  4759. fprintf(source, "%.*s", sizeBody, body);
  4760. else
  4761. {
  4762. if (seenPublic)
  4763. fprintf(source, "public class embed\n{\n %.*s\n}", sizeBody, body);
  4764. else
  4765. fprintf(source, "public class embed\n{\n public static %.*s\n}", sizeBody, body);
  4766. lineNumberOffset += 2; // for the /n's above
  4767. }
  4768. fclose(source);
  4769. MemoryBuffer result;
  4770. Owned<IPipeProcess> pipe = createPipeProcess();
  4771. StringBuffer options(compilerOptions);
  4772. if (isEmptyString(compilerOptions))
  4773. options.append("-g:none");
  4774. appendClassPath(options.append(" -cp "));
  4775. VStringBuffer javac("javac %s %s", options.str(), javafile.str());
  4776. if (!pipe->run("javac", javac, tmpDirName, false, false, true, 0, false))
  4777. {
  4778. throw makeStringException(0, "Failed to run javac");
  4779. }
  4780. else
  4781. {
  4782. StringBuffer javaErrors;
  4783. Owned<ISimpleReadStream> pipeReader = pipe->getErrorStream();
  4784. readSimpleStream(javaErrors, *pipeReader);
  4785. pipe->closeError();
  4786. unsigned retcode = pipe->wait();
  4787. cleanupJavaErrors(errors, javaErrors, lineNumberOffset);
  4788. if (retcode)
  4789. throw makeStringException(0, "Failed to precompile java code");
  4790. VStringBuffer mainfile("%s" PATHSEPSTR "%s.class", tmpDirName.str(), classname.str());
  4791. JavaClassReader reader(mainfile);
  4792. DBGLOG("Analysing generated class %s", reader.queryClassName());
  4793. // Not sure how useful this warning is.
  4794. //if (persistMode > persistThread && (reader.getFlags(funcName) & JavaClassReader::ACC_SYNCHRONIZED)==0)
  4795. // errors.appendf("Warning: persist mode set but function is not synchronized\n");
  4796. reader.getEmbedData(result, funcName, true);
  4797. removeFileTraceIfFail(mainfile);
  4798. // Now read nested classes
  4799. Owned<IDirectoryIterator> classFiles = tempDir->directoryFiles("*$*.class",false,false);
  4800. ForEach(*classFiles)
  4801. {
  4802. const char *thisFile = classFiles->query().queryFilename();
  4803. JavaClassReader reader(thisFile);
  4804. reader.getEmbedData(result, nullptr, false);
  4805. removeFileTraceIfFail(thisFile);
  4806. }
  4807. // We could give a warning if persist is set to anything over "thread" and the function we are calling is not synchronized.
  4808. }
  4809. removeFileTraceIfFail(javafile);
  4810. tempDir->remove();
  4811. __lenResult = result.length();
  4812. __result = result.detachOwn();
  4813. }
  4814. extern DECL_EXPORT void precompile(size32_t & __lenResult, void * & __result, const char *funcName, size32_t charsBody, const char * body, const char *argNames, const char *compilerOptions, const char *persistOptions)
  4815. {
  4816. StringBuffer errors;
  4817. doPrecompile(__lenResult, __result, funcName, charsBody, body, argNames, compilerOptions, persistOptions, errors, false);
  4818. }
  4819. extern DECL_EXPORT void syntaxCheck(size32_t & __lenResult, char * & __result, const char *funcname, size32_t charsBody, const char * body, const char *argNames, const char *compilerOptions, const char *persistOptions)
  4820. {
  4821. StringBuffer result;
  4822. try
  4823. {
  4824. size32_t ds;
  4825. rtlDataAttr d;
  4826. StringBuffer errors;
  4827. doPrecompile(ds, d.refdata(), funcname, charsBody, body, argNames, compilerOptions, persistOptions, result, true);
  4828. // Reuse result in the precompile that normally immediately follows
  4829. unsigned sizeBody = rtlUtf8Size(charsBody, body); // size in bytes
  4830. prevHash = rtlHash32Data(sizeBody,body,0xcafebabe);
  4831. prevCompile.setBuffer(ds, d.detachdata(), true);
  4832. }
  4833. catch (IException *E)
  4834. {
  4835. StringBuffer msg;
  4836. result.append(E->errorMessage(msg));
  4837. E->Release();
  4838. }
  4839. __lenResult = result.length();
  4840. __result = result.detach();
  4841. }
  4842. extern DECL_EXPORT void checkImport(size32_t & __lenResult, char * & __result, const char *funcname, size32_t charsImport, const char * import, const char *argNames, const char *compilerOptions, const char *persistOptions)
  4843. {
  4844. StringBuffer result;
  4845. try
  4846. {
  4847. StringAttr globalScope;
  4848. PersistMode persistMode = getPersistMode(persistOptions, globalScope);
  4849. if (persistMode > persistThread && !globalScope)
  4850. {
  4851. StringBuffer b(rtlUtf8Size(charsImport, import), import);
  4852. const char *dotpos = strrchr(b, '.');
  4853. if (!dotpos)
  4854. throw MakeStringException(0, "javaembed: cannot determine key for persist in function %s", b.str());
  4855. }
  4856. }
  4857. catch (IException *E)
  4858. {
  4859. StringBuffer msg;
  4860. result.appendf("%s\n", E->errorMessage(msg).str());
  4861. E->Release();
  4862. }
  4863. __lenResult = result.length();
  4864. __result = result.detach();
  4865. }
  4866. } // namespace
  4867. // Callbacks from java
  4868. extern "C" {
  4869. JNIEXPORT jboolean JNICALL Java_com_HPCCSystems_HpccUtils__1hasNext (JNIEnv *, jclass, jlong);
  4870. JNIEXPORT jobject JNICALL Java_com_HPCCSystems_HpccUtils__1next (JNIEnv *, jclass, jlong);
  4871. JNIEXPORT jclass JNICALL Java_com_HPCCSystems_HpccClassLoader_defineClassForEmbed(JNIEnv *env, jobject loader, jint bytecodeLen, jlong bytecode, jstring name);
  4872. JNIEXPORT void JNICALL Java_com_HPCCSystems_HpccUtils_log(JNIEnv *JNIenv, jclass, jstring msg);
  4873. JNIEXPORT jboolean JNICALL Java_com_HPCCSystems_HpccUtils__1isLocal (JNIEnv *, jclass, jlong);
  4874. JNIEXPORT jint JNICALL Java_com_HPCCSystems_HpccUtils__1numSlaves (JNIEnv *, jclass, jlong);
  4875. JNIEXPORT jint JNICALL Java_com_HPCCSystems_HpccUtils__1numStrands (JNIEnv *, jclass, jlong);
  4876. JNIEXPORT jint JNICALL Java_com_HPCCSystems_HpccUtils__1querySlave (JNIEnv *, jclass, jlong);
  4877. JNIEXPORT jint JNICALL Java_com_HPCCSystems_HpccUtils__1queryStrand (JNIEnv *, jclass, jlong);
  4878. }
  4879. JNIEXPORT jboolean JNICALL Java_com_HPCCSystems_HpccUtils__1hasNext (JNIEnv *JNIenv, jclass, jlong proxy)
  4880. {
  4881. try
  4882. {
  4883. javaembed::ECLDatasetIterator *e = (javaembed::ECLDatasetIterator *) proxy;
  4884. return e->hasNext();
  4885. }
  4886. catch (IException *E)
  4887. {
  4888. StringBuffer msg;
  4889. E->errorMessage(msg);
  4890. E->Release();
  4891. JNIenv->ThrowNew(javaembed::langIllegalArgumentExceptionClass, msg.str());
  4892. return false;
  4893. }
  4894. }
  4895. JNIEXPORT jobject JNICALL Java_com_HPCCSystems_HpccUtils__1next (JNIEnv *JNIenv, jclass, jlong proxy)
  4896. {
  4897. try
  4898. {
  4899. javaembed::ECLDatasetIterator *e = (javaembed::ECLDatasetIterator *) proxy;
  4900. return e->next();
  4901. }
  4902. catch (IException *E)
  4903. {
  4904. StringBuffer msg;
  4905. E->errorMessage(msg);
  4906. E->Release();
  4907. JNIenv->ThrowNew(javaembed::langIllegalArgumentExceptionClass, msg.str());
  4908. return NULL;
  4909. }
  4910. }
  4911. JNIEXPORT jclass JNICALL Java_com_HPCCSystems_HpccClassLoader_defineClassForEmbed(JNIEnv *env, jobject loader, jint datalen, jlong data, jstring name)
  4912. {
  4913. const char *nameChars = env->GetStringUTFChars(name, nullptr);
  4914. size32_t namelen = strlen(nameChars);
  4915. MemoryBuffer b;
  4916. b.setBuffer(datalen, (void *) data, false);
  4917. b.setEndian(__BIG_ENDIAN);
  4918. jclass ret = nullptr;
  4919. while (b.remaining())
  4920. {
  4921. uint32_t siglen; b.read(siglen);
  4922. const char *sig = (const char *) b.readDirect(siglen);
  4923. uint32_t bytecodeLen; b.read(bytecodeLen);
  4924. const jbyte * bytecode = (const jbyte *) b.readDirect(bytecodeLen);
  4925. if (siglen >= namelen && memcmp(sig, nameChars, namelen)==0 && (namelen == siglen || sig[namelen] == '.'))
  4926. {
  4927. #ifdef TRACE_CLASSFILE
  4928. DBGLOG("javaembed: loading class %s (%.*s)", nameChars, siglen, sig);
  4929. #endif
  4930. ret = env->DefineClass(nameChars, loader, bytecode, bytecodeLen);
  4931. // NOTE - if there is an exception it will get thrown next time we get into java code
  4932. break;
  4933. }
  4934. }
  4935. if (!ret)
  4936. DBGLOG("javaembed: Failed to load class %s", nameChars);
  4937. env->ReleaseStringUTFChars(name, nameChars);
  4938. return ret;
  4939. }
  4940. JNIEXPORT void JNICALL Java_com_HPCCSystems_HpccUtils_log(JNIEnv *JNIenv, jclass, jstring msg)
  4941. {
  4942. if (msg)
  4943. {
  4944. const char *text = JNIenv->GetStringUTFChars(msg, 0);
  4945. DBGLOG("javaembed: user: %s", text);
  4946. JNIenv->ReleaseStringUTFChars(msg, text);
  4947. }
  4948. }
  4949. JNIEXPORT jboolean JNICALL Java_com_HPCCSystems_HpccUtils__1isLocal(JNIEnv *JNIenv, jclass, jlong proxy)
  4950. {
  4951. const IThorActivityContext *a = (IThorActivityContext *) proxy;
  4952. return a->isLocal();
  4953. }
  4954. JNIEXPORT jint JNICALL Java_com_HPCCSystems_HpccUtils__1numSlaves(JNIEnv *JNIenv, jclass, jlong proxy)
  4955. {
  4956. const IThorActivityContext *a = (IThorActivityContext *) proxy;
  4957. return a->numSlaves();
  4958. }
  4959. JNIEXPORT jint JNICALL Java_com_HPCCSystems_HpccUtils__1numStrands(JNIEnv *JNIenv, jclass, jlong proxy)
  4960. {
  4961. const IThorActivityContext *a = (IThorActivityContext *) proxy;
  4962. return a->numStrands();
  4963. }
  4964. JNIEXPORT jint JNICALL Java_com_HPCCSystems_HpccUtils__1querySlave(JNIEnv *JNIenv, jclass, jlong proxy)
  4965. {
  4966. const IThorActivityContext *a = (IThorActivityContext *) proxy;
  4967. return a->querySlave();
  4968. }
  4969. JNIEXPORT jint JNICALL Java_com_HPCCSystems_HpccUtils__1queryStrand(JNIEnv *JNIenv, jclass, jlong proxy)
  4970. {
  4971. const IThorActivityContext *a = (IThorActivityContext *) proxy;
  4972. return a->queryStrand();
  4973. }
  4974. // Used for dynamically loading in ESDL
  4975. extern "C" DECL_EXPORT IEmbedContext *getEmbedContextDynamic()
  4976. {
  4977. return javaembed::getEmbedContext();
  4978. }