agjasmin.pas 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239
  1. {
  2. Copyright (c) 1998-2010 by the Free Pascal team
  3. This unit implements the Jasmin assembler writer
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. { Unit for writing Jasmin assembler (JVM bytecode) output.
  18. }
  19. unit agjasmin;
  20. {$i fpcdefs.inc}
  21. interface
  22. uses
  23. cclasses,systems,
  24. globtype,globals,
  25. symconst,symbase,symdef,symsym,
  26. aasmbase,aasmtai,aasmdata,aasmcpu,
  27. assemble;
  28. type
  29. TJasminAssemblerOutputFile=class(TExternalAssemblerOutputFile)
  30. procedure RemoveAsm; override;
  31. end;
  32. TJasminInstrWriter = class;
  33. {# This is a derived class which is used to write
  34. Jasmin-styled assembler.
  35. }
  36. { TJasminAssembler }
  37. TJasminAssembler=class(texternalassembler)
  38. protected
  39. jasminjar: tcmdstr;
  40. asmfiles: TCmdStrList;
  41. procedure WriteExtraHeader(obj: tabstractrecorddef);
  42. procedure WriteInstruction(hp: tai);
  43. procedure NewAsmFileForStructDef(obj: tabstractrecorddef);
  44. function VisibilityToStr(vis: tvisibility): ansistring;
  45. function MethodDefinition(pd: tprocdef): ansistring;
  46. function ConstValue(csym: tconstsym): ansistring;
  47. function ConstAssignmentValue(csym: tconstsym): ansistring;
  48. function ConstDefinition(sym: tconstsym): ansistring;
  49. function FieldDefinition(sym: tabstractvarsym): ansistring;
  50. function InnerStructDef(obj: tabstractrecorddef): ansistring;
  51. procedure WriteProcDef(pd: tprocdef);
  52. procedure WriteFieldSym(sym: tabstractvarsym);
  53. procedure WriteConstSym(sym: tconstsym);
  54. procedure WriteSymtableVarSyms(st: TSymtable);
  55. procedure WriteSymtableProcdefs(st: TSymtable);
  56. procedure WriteSymtableStructDefs(st: TSymtable);
  57. function CreateNewAsmWriter: TExternalAssemblerOutputFile; override;
  58. public
  59. constructor CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); override;
  60. function MakeCmdLine: TCmdStr;override;
  61. procedure WriteTree(p:TAsmList);override;
  62. procedure WriteAsmList;override;
  63. destructor destroy; override;
  64. protected
  65. InstrWriter: TJasminInstrWriter;
  66. end;
  67. {# This is the base class for writing instructions.
  68. The WriteInstruction() method must be overridden
  69. to write a single instruction to the assembler
  70. file.
  71. }
  72. { TJasminInstrWriter }
  73. TJasminInstrWriter = class
  74. constructor create(_owner: TJasminAssembler);
  75. procedure WriteInstruction(hp : tai); virtual;
  76. protected
  77. owner: TJasminAssembler;
  78. end;
  79. implementation
  80. uses
  81. SysUtils,
  82. cutils,cfileutl,cscript,
  83. fmodule,finput,verbose,
  84. symtype,symcpu,symtable,jvmdef,
  85. itcpujas,cpubase,cpuinfo,cgutils,
  86. widestr
  87. ;
  88. const
  89. line_length = 70;
  90. type
  91. t64bitarray = array[0..7] of byte;
  92. t32bitarray = array[0..3] of byte;
  93. {****************************************************************************}
  94. { Support routines }
  95. {****************************************************************************}
  96. function fixline(s:string):string;
  97. {
  98. return s with all leading and ending spaces and tabs removed
  99. }
  100. var
  101. i,j,k : integer;
  102. begin
  103. i:=length(s);
  104. while (i>0) and (s[i] in [#9,' ']) do
  105. dec(i);
  106. j:=1;
  107. while (j<i) and (s[j] in [#9,' ']) do
  108. inc(j);
  109. for k:=j to i do
  110. if s[k] in [#0..#31,#127..#255] then
  111. s[k]:='.';
  112. fixline:=Copy(s,j,i-j+1);
  113. end;
  114. function constastr(p: pchar; len: longint): ansistring;
  115. var
  116. i,runstart,runlen: longint;
  117. procedure flush;
  118. begin
  119. if runlen>0 then
  120. begin
  121. setlength(result,length(result)+runlen);
  122. move(p[runstart],result[length(result)-runlen+1],runlen);
  123. runlen:=0;
  124. end;
  125. end;
  126. begin
  127. result:='"';
  128. runlen:=0;
  129. runstart:=0;
  130. for i:=0 to len-1 do
  131. begin
  132. { escape control codes }
  133. case p[i] of
  134. { LF and CR must be escaped specially, because \uXXXX parsing
  135. happens in the pre-processor, so it's the same as actually
  136. inserting a newline in the middle of a string constant }
  137. #10:
  138. begin
  139. flush;
  140. result:=result+'\n';
  141. end;
  142. #13:
  143. begin
  144. flush;
  145. result:=result+'\r';
  146. end;
  147. '"','\':
  148. begin
  149. flush;
  150. result:=result+'\'+p[i];
  151. end
  152. else if p[i]<#32 then
  153. begin
  154. flush;
  155. result:=result+'\u'+hexstr(ord(p[i]),4);
  156. end
  157. else if p[i]<#127 then
  158. begin
  159. if runlen=0 then
  160. runstart:=i;
  161. inc(runlen);
  162. end
  163. else
  164. begin
  165. { see comments in njvmcon }
  166. flush;
  167. result:=result+'\u'+hexstr(ord(p[i]),4)
  168. end;
  169. end;
  170. end;
  171. flush;
  172. result:=result+'"';
  173. end;
  174. function constwstr(w: pcompilerwidechar; len: longint): ansistring;
  175. var
  176. i: longint;
  177. begin
  178. result:='"';
  179. for i:=0 to len-1 do
  180. begin
  181. { escape control codes }
  182. case w[i] of
  183. 10:
  184. result:=result+'\n';
  185. 13:
  186. result:=result+'\r';
  187. ord('"'),ord('\'):
  188. result:=result+'\'+chr(w[i]);
  189. else if (w[i]<32) or
  190. (w[i]>=127) then
  191. result:=result+'\u'+hexstr(w[i],4)
  192. else
  193. result:=result+char(w[i]);
  194. end;
  195. end;
  196. result:=result+'"';
  197. end;
  198. function constsingle(s: single): ansistring;
  199. begin
  200. result:='0fx'+hexstr(longint(t32bitarray(s)),8);
  201. end;
  202. function constdouble(d: double): ansistring;
  203. begin
  204. // force interpretation as double (since we write it out as an
  205. // integer, we never have to swap the endianess). We have to
  206. // include the sign separately because of the way Java parses
  207. // hex numbers (0x8000000000000000 is not a valid long)
  208. result:=hexstr(abs(int64(t64bitarray(d))),16);
  209. if int64(t64bitarray(d))<0 then
  210. result:='-'+result;
  211. result:='0dx'+result;
  212. end;
  213. {****************************************************************************}
  214. { Jasmin Output File }
  215. {****************************************************************************}
  216. procedure TJasminAssemblerOutputFile.RemoveAsm;
  217. var
  218. g : file;
  219. begin
  220. inherited;
  221. if cs_asm_leave in current_settings.globalswitches then
  222. exit;
  223. while not TJasminAssembler(owner).asmfiles.empty do
  224. begin
  225. if cs_asm_extern in current_settings.globalswitches then
  226. AsmRes.AddDeleteCommand(TJasminAssembler(owner).asmfiles.GetFirst)
  227. else
  228. begin
  229. assign(g,TJasminAssembler(owner).asmfiles.GetFirst);
  230. {$I-}
  231. erase(g);
  232. {$I+}
  233. if ioresult<>0 then;
  234. end;
  235. end;
  236. end;
  237. {****************************************************************************}
  238. { Jasmin Assembler writer }
  239. {****************************************************************************}
  240. destructor TJasminAssembler.Destroy;
  241. begin
  242. InstrWriter.free;
  243. asmfiles.free;
  244. inherited destroy;
  245. end;
  246. procedure TJasminAssembler.WriteTree(p:TAsmList);
  247. var
  248. ch : char;
  249. hp : tai;
  250. hp1 : tailineinfo;
  251. s : ansistring;
  252. i,pos : longint;
  253. InlineLevel : longint;
  254. do_line : boolean;
  255. begin
  256. if not assigned(p) then
  257. exit;
  258. InlineLevel:=0;
  259. { lineinfo is only needed for al_procedures (PFV) }
  260. do_line:=(cs_asm_source in current_settings.globalswitches);
  261. hp:=tai(p.first);
  262. while assigned(hp) do
  263. begin
  264. prefetch(pointer(hp.next)^);
  265. if not(hp.typ in SkipLineInfo) then
  266. begin
  267. hp1 := hp as tailineinfo;
  268. current_filepos:=hp1.fileinfo;
  269. { no line info for inlined code }
  270. if do_line and (inlinelevel=0) then
  271. begin
  272. { load infile }
  273. if lastfileinfo.fileindex<>hp1.fileinfo.fileindex then
  274. begin
  275. infile:=current_module.sourcefiles.get_file(hp1.fileinfo.fileindex);
  276. if assigned(infile) then
  277. begin
  278. { open only if needed !! }
  279. if (cs_asm_source in current_settings.globalswitches) then
  280. infile.open;
  281. end;
  282. { avoid unnecessary reopens of the same file !! }
  283. lastfileinfo.fileindex:=hp1.fileinfo.fileindex;
  284. { be sure to change line !! }
  285. lastfileinfo.line:=-1;
  286. end;
  287. { write source }
  288. if (cs_asm_source in current_settings.globalswitches) and
  289. assigned(infile) then
  290. begin
  291. if (infile<>lastinfile) then
  292. begin
  293. writer.AsmWriteLn(asminfo^.comment+'['+infile.name+']');
  294. if assigned(lastinfile) then
  295. lastinfile.close;
  296. end;
  297. if (hp1.fileinfo.line<>lastfileinfo.line) and
  298. ((hp1.fileinfo.line<infile.maxlinebuf) or (InlineLevel>0)) then
  299. begin
  300. if (hp1.fileinfo.line<>0) and
  301. ((infile.linebuf^[hp1.fileinfo.line]>=0) or (InlineLevel>0)) then
  302. writer.AsmWriteLn(asminfo^.comment+'['+tostr(hp1.fileinfo.line)+'] '+
  303. fixline(infile.GetLineStr(hp1.fileinfo.line)));
  304. { set it to a negative value !
  305. to make that is has been read already !! PM }
  306. if (infile.linebuf^[hp1.fileinfo.line]>=0) then
  307. infile.linebuf^[hp1.fileinfo.line]:=-infile.linebuf^[hp1.fileinfo.line]-1;
  308. end;
  309. end;
  310. lastfileinfo:=hp1.fileinfo;
  311. lastinfile:=infile;
  312. end;
  313. end;
  314. case hp.typ of
  315. ait_comment :
  316. Begin
  317. writer.AsmWrite(asminfo^.comment);
  318. writer.AsmWritePChar(tai_comment(hp).str);
  319. writer.AsmLn;
  320. End;
  321. ait_regalloc :
  322. begin
  323. if (cs_asm_regalloc in current_settings.globalswitches) then
  324. begin
  325. writer.AsmWrite(#9+asminfo^.comment+'Register ');
  326. repeat
  327. writer.AsmWrite(std_regname(Tai_regalloc(hp).reg));
  328. if (hp.next=nil) or
  329. (tai(hp.next).typ<>ait_regalloc) or
  330. (tai_regalloc(hp.next).ratype<>tai_regalloc(hp).ratype) then
  331. break;
  332. hp:=tai(hp.next);
  333. writer.AsmWrite(',');
  334. until false;
  335. writer.AsmWrite(' ');
  336. writer.AsmWriteLn(regallocstr[tai_regalloc(hp).ratype]);
  337. end;
  338. end;
  339. ait_tempalloc :
  340. begin
  341. if (cs_asm_tempalloc in current_settings.globalswitches) then
  342. begin
  343. {$ifdef EXTDEBUG}
  344. if assigned(tai_tempalloc(hp).problem) then
  345. writer.AsmWriteLn(asminfo^.comment+'Temp '+tostr(tai_tempalloc(hp).temppos)+','+
  346. tostr(tai_tempalloc(hp).tempsize)+' '+tai_tempalloc(hp).problem^)
  347. else
  348. {$endif EXTDEBUG}
  349. writer.AsmWriteLn(asminfo^.comment+'Temp '+tostr(tai_tempalloc(hp).temppos)+','+
  350. tostr(tai_tempalloc(hp).tempsize)+' '+tempallocstr[tai_tempalloc(hp).allocation]);
  351. end;
  352. end;
  353. ait_align :
  354. begin
  355. end;
  356. ait_section :
  357. begin
  358. end;
  359. ait_datablock :
  360. begin
  361. internalerror(2010122701);
  362. end;
  363. ait_const:
  364. begin
  365. writer.AsmWriteln('constant');
  366. // internalerror(2010122702);
  367. end;
  368. ait_realconst :
  369. begin
  370. internalerror(2010122703);
  371. end;
  372. ait_string :
  373. begin
  374. pos:=0;
  375. for i:=1 to tai_string(hp).len do
  376. begin
  377. if pos=0 then
  378. begin
  379. writer.AsmWrite(#9'strconst: '#9'"');
  380. pos:=20;
  381. end;
  382. ch:=tai_string(hp).str[i-1];
  383. case ch of
  384. #0, {This can't be done by range, because a bug in FPC}
  385. #1..#31,
  386. #128..#255 : s:='\'+tostr(ord(ch) shr 6)+tostr((ord(ch) and 63) shr 3)+tostr(ord(ch) and 7);
  387. '"' : s:='\"';
  388. '\' : s:='\\';
  389. else
  390. s:=ch;
  391. end;
  392. writer.AsmWrite(s);
  393. inc(pos,length(s));
  394. if (pos>line_length) or (i=tai_string(hp).len) then
  395. begin
  396. writer.AsmWriteLn('"');
  397. pos:=0;
  398. end;
  399. end;
  400. end;
  401. ait_label :
  402. begin
  403. if (tai_label(hp).labsym.is_used) then
  404. begin
  405. writer.AsmWrite(tai_label(hp).labsym.name);
  406. writer.AsmWriteLn(':');
  407. end;
  408. end;
  409. ait_symbol :
  410. begin
  411. if (tai_symbol(hp).sym.typ = AT_FUNCTION) then
  412. begin
  413. end
  414. else
  415. begin
  416. writer.AsmWrite('data symbol: ');
  417. writer.AsmWriteln(tai_symbol(hp).sym.name);
  418. // internalerror(2010122706);
  419. end;
  420. end;
  421. ait_symbol_end :
  422. begin
  423. end;
  424. ait_instruction :
  425. begin
  426. WriteInstruction(hp);
  427. end;
  428. ait_force_line,
  429. ait_function_name : ;
  430. ait_cutobject :
  431. begin
  432. end;
  433. ait_marker :
  434. if tai_marker(hp).kind=mark_NoLineInfoStart then
  435. inc(InlineLevel)
  436. else if tai_marker(hp).kind=mark_NoLineInfoEnd then
  437. dec(InlineLevel);
  438. ait_directive :
  439. begin
  440. { the CPU directive is probably not supported by the JVM assembler,
  441. so it's commented out }
  442. if tai_directive(hp).directive=asd_cpu then
  443. writer.AsmWrite(asminfo^.comment);
  444. writer.AsmWrite('.'+directivestr[tai_directive(hp).directive]+' ');
  445. if tai_directive(hp).name<>'' then
  446. writer.AsmWrite(tai_directive(hp).name);
  447. writer.AsmLn;
  448. end;
  449. ait_jvar:
  450. begin
  451. writer.AsmWrite('.var ');
  452. writer.AsmWrite(tostr(tai_jvar(hp).stackslot));
  453. writer.AsmWrite(' is ');
  454. writer.AsmWrite(tai_jvar(hp).desc^);
  455. writer.AsmWrite(' from ');
  456. writer.AsmWrite(tai_jvar(hp).startlab.name);
  457. writer.AsmWrite(' to ');
  458. writer.AsmWriteLn(tai_jvar(hp).stoplab.name);
  459. end;
  460. ait_jcatch:
  461. begin
  462. writer.AsmWrite('.catch ');
  463. writer.AsmWrite(tai_jcatch(hp).name^);
  464. writer.AsmWrite(' from ');
  465. writer.AsmWrite(tai_jcatch(hp).startlab.name);
  466. writer.AsmWrite(' to ');
  467. writer.AsmWrite(tai_jcatch(hp).stoplab.name);
  468. writer.AsmWrite(' using ');
  469. writer.AsmWriteLn(tai_jcatch(hp).handlerlab.name);
  470. end;
  471. else
  472. internalerror(2010122707);
  473. end;
  474. hp:=tai(hp.next);
  475. end;
  476. end;
  477. procedure TJasminAssembler.WriteExtraHeader(obj: tabstractrecorddef);
  478. var
  479. superclass,
  480. intf: tobjectdef;
  481. n: ansistring;
  482. i: longint;
  483. toplevelowner: tsymtable;
  484. begin
  485. superclass:=nil;
  486. { JVM 1.5+ }
  487. writer.AsmWriteLn('.bytecode 49.0');
  488. // include files are not support by Java, and the directory of the main
  489. // source file must not be specified
  490. if current_module.mainsource<>'' then
  491. n:=ExtractFileName(current_module.mainsource)
  492. else
  493. n:=InputFileName;
  494. writer.AsmWriteLn('.source '+ExtractFileName(n));
  495. { class/interface name }
  496. if not assigned(obj) then
  497. begin
  498. { fake class type for unit -> name=unitname and
  499. superclass=java.lang.object, make final so you cannot descend
  500. from it }
  501. writer.AsmWrite('.class final public ');
  502. if assigned(current_module.namespace) then
  503. writer.AsmWrite(current_module.namespace^+'.');
  504. writer.AsmWriteln(current_module.realmodulename^);
  505. writer.AsmWriteLn('.super java/lang/Object');
  506. end
  507. else
  508. begin
  509. toplevelowner:=obj.owner;
  510. while not(toplevelowner.symtabletype in [staticsymtable,globalsymtable]) do
  511. toplevelowner:=toplevelowner.defowner.owner;
  512. case obj.typ of
  513. recorddef:
  514. begin
  515. { can't inherit from records }
  516. writer.AsmWrite('.class final ');
  517. if toplevelowner.symtabletype=globalsymtable then
  518. writer.AsmWrite('public ');
  519. writer.AsmWriteln(obj.jvm_full_typename(true));
  520. superclass:=java_fpcbaserecordtype;
  521. end;
  522. objectdef:
  523. begin
  524. case tobjectdef(obj).objecttype of
  525. odt_javaclass:
  526. begin
  527. writer.AsmWrite('.class ');
  528. if oo_is_sealed in tobjectdef(obj).objectoptions then
  529. writer.AsmWrite('final ');
  530. if (oo_is_abstract in tobjectdef(obj).objectoptions) or
  531. (tobjectdef(obj).abstractcnt<>0) then
  532. writer.AsmWrite('abstract ');
  533. if toplevelowner.symtabletype=globalsymtable then
  534. writer.AsmWrite('public ');
  535. if (oo_is_enum_class in tobjectdef(obj).objectoptions) then
  536. writer.AsmWrite('enum ');
  537. writer.AsmWriteln(obj.jvm_full_typename(true));
  538. superclass:=tobjectdef(obj).childof;
  539. end;
  540. odt_interfacejava:
  541. begin
  542. writer.AsmWrite('.interface abstract ');
  543. if toplevelowner.symtabletype=globalsymtable then
  544. writer.AsmWrite('public ');
  545. writer.AsmWriteLn(obj.jvm_full_typename(true));
  546. { interfaces must always specify Java.lang.object as
  547. superclass }
  548. superclass:=java_jlobject;
  549. end
  550. else
  551. internalerror(2011010906);
  552. end;
  553. end;
  554. end;
  555. { superclass }
  556. if assigned(superclass) then
  557. begin
  558. writer.AsmWrite('.super ');
  559. if assigned(superclass.import_lib) then
  560. writer.AsmWrite(superclass.import_lib^+'/');
  561. writer.AsmWriteln(superclass.objextname^);
  562. end;
  563. { implemented interfaces }
  564. if (obj.typ=objectdef) and
  565. assigned(tobjectdef(obj).ImplementedInterfaces) then
  566. begin
  567. for i:=0 to tobjectdef(obj).ImplementedInterfaces.count-1 do
  568. begin
  569. intf:=TImplementedInterface(tobjectdef(obj).ImplementedInterfaces[i]).IntfDef;
  570. writer.AsmWrite('.implements ');
  571. writer.AsmWriteLn(intf.jvm_full_typename(true));
  572. end;
  573. end;
  574. { signature for enum classes (must come after superclass and
  575. implemented interfaces) }
  576. if (obj.typ=objectdef) and
  577. (oo_is_enum_class in tobjectdef(obj).objectoptions) then
  578. writer.AsmWriteln('.signature "Ljava/lang/Enum<L'+obj.jvm_full_typename(true)+';>;"');
  579. { in case of nested class: relation to parent class }
  580. if obj.owner.symtabletype in [objectsymtable,recordsymtable] then
  581. writer.AsmWriteln(InnerStructDef(obj));
  582. { add all nested classes }
  583. for i:=0 to obj.symtable.deflist.count-1 do
  584. if (is_java_class_or_interface(tdef(obj.symtable.deflist[i])) or
  585. (tdef(obj.symtable.deflist[i]).typ=recorddef)) and
  586. not(df_generic in tdef(obj.symtable.deflist[i]).defoptions) then
  587. writer.AsmWriteln(InnerStructDef(tabstractrecorddef(obj.symtable.deflist[i])));
  588. end;
  589. writer.AsmLn;
  590. end;
  591. procedure TJasminAssembler.WriteInstruction(hp: tai);
  592. begin
  593. InstrWriter.WriteInstruction(hp);
  594. end;
  595. function TJasminAssembler.MakeCmdLine: TCmdStr;
  596. const
  597. jasminjarname = 'jasmin.jar';
  598. var
  599. filenames: tcmdstr;
  600. asmfile: tcmdstrlistitem;
  601. jasminjarfound: boolean;
  602. begin
  603. if jasminjar='' then
  604. begin
  605. jasminjarfound:=false;
  606. if utilsdirectory<>'' then
  607. jasminjarfound:=FindFile(jasminjarname,utilsdirectory,false,jasminjar);
  608. if not jasminjarfound then
  609. jasminjarfound:=FindFileInExeLocations(jasminjarname,false,jasminjar);
  610. if (not jasminjarfound) and not(cs_asm_extern in current_settings.globalswitches) then
  611. begin
  612. Message1(exec_e_assembler_not_found,jasminjarname);
  613. current_settings.globalswitches:=current_settings.globalswitches+[cs_asm_extern];
  614. end;
  615. if jasminjarfound then
  616. Message1(exec_t_using_assembler,jasminjar);
  617. end;
  618. result:=asminfo^.asmcmd;
  619. filenames:=ScriptFixFileName(AsmFileName);
  620. if cs_asm_extern in current_settings.globalswitches then
  621. filenames:=maybequoted(filenames);
  622. asmfile:=tcmdstrlistitem(asmfiles.First);
  623. while assigned(asmfile) do
  624. begin
  625. if cs_asm_extern in current_settings.globalswitches then
  626. filenames:=filenames+' '+maybequoted(ScriptFixFileName(asmfile.str))
  627. else
  628. filenames:=filenames+' '+ScriptFixFileName(asmfile.str);
  629. asmfile:=tcmdstrlistitem(asmfile.next);
  630. end;
  631. Replace(result,'$ASM',filenames);
  632. if (path<>'') then
  633. if cs_asm_extern in current_settings.globalswitches then
  634. Replace(result,'$OBJDIR',maybequoted(ScriptFixFileName(path)))
  635. else
  636. Replace(result,'$OBJDIR',ScriptFixFileName(path))
  637. else
  638. Replace(result,'$OBJDIR','.');
  639. if cs_asm_extern in current_settings.globalswitches then
  640. Replace(result,'$JASMINJAR',maybequoted(ScriptFixFileName(jasminjar)))
  641. else
  642. Replace(result,'$JASMINJAR',ScriptFixFileName(jasminjar));
  643. Replace(result,'$EXTRAOPT',asmextraopt);
  644. end;
  645. procedure TJasminAssembler.NewAsmFileForStructDef(obj: tabstractrecorddef);
  646. begin
  647. if not writer.ClearIfEmpty then
  648. begin
  649. writer.AsmClose;
  650. asmfiles.Concat(AsmFileName);
  651. end;
  652. AsmFileName:=obj.jvm_full_typename(false);
  653. AsmFileName:=Path+FixFileName(AsmFileName)+target_info.asmext;
  654. writer.AsmCreate(cut_normal);
  655. end;
  656. function TJasminAssembler.VisibilityToStr(vis: tvisibility): ansistring;
  657. begin
  658. case vis of
  659. vis_hidden,
  660. vis_strictprivate:
  661. result:='private ';
  662. { protected in Java means "accessible by subclasses *and* by classes
  663. in the same package" -> similar to regular "protected" in Pascal;
  664. "strict protected" is actually more strict in Pascal than in Java,
  665. but there's not much we can do about that }
  666. vis_protected,
  667. vis_strictprotected:
  668. result:='protected ';
  669. vis_private:
  670. { pick default visibility = "package" visibility; required because
  671. other classes in the same unit can also access these symbols }
  672. result:='';
  673. vis_public:
  674. result:='public '
  675. else
  676. internalerror(2010122609);
  677. end;
  678. end;
  679. function TJasminAssembler.MethodDefinition(pd: tprocdef): ansistring;
  680. begin
  681. result:=VisibilityToStr(pd.visibility);
  682. if (pd.procsym.owner.symtabletype in [globalsymtable,staticsymtable,localsymtable]) or
  683. (po_classmethod in pd.procoptions) then
  684. result:=result+'static ';
  685. if (po_abstractmethod in pd.procoptions) or
  686. is_javainterface(tdef(pd.owner.defowner)) then
  687. result:=result+'abstract ';
  688. if (pd.procsym.owner.symtabletype in [globalsymtable,staticsymtable,localsymtable]) or
  689. (po_finalmethod in pd.procoptions) or
  690. (not(po_virtualmethod in pd.procoptions) and
  691. not(po_classmethod in pd.procoptions) and
  692. not(pd.proctypeoption in [potype_constructor,potype_class_constructor])) then
  693. result:=result+'final ';
  694. result:=result+tcpuprocdef(pd).jvmmangledbasename(false);
  695. end;
  696. function TJasminAssembler.ConstValue(csym: tconstsym): ansistring;
  697. begin
  698. case csym.consttyp of
  699. constord:
  700. { always interpret as signed value, because the JVM does not
  701. support unsigned values }
  702. case csym.constdef.size of
  703. 1:result:=tostr(shortint(csym.value.valueord.svalue));
  704. 2:result:=tostr(smallint(csym.value.valueord.svalue));
  705. 4:result:=tostr(longint(csym.value.valueord.svalue));
  706. 8:result:=tostr(csym.value.valueord.svalue);
  707. else
  708. internalerror(2014082050);
  709. end;
  710. conststring:
  711. result:=constastr(pchar(csym.value.valueptr),csym.value.len);
  712. constreal:
  713. case tfloatdef(csym.constdef).floattype of
  714. s32real:
  715. result:=constsingle(pbestreal(csym.value.valueptr)^);
  716. s64real:
  717. result:=constdouble(pbestreal(csym.value.valueptr)^);
  718. else
  719. internalerror(2011021204);
  720. end;
  721. constset:
  722. result:='TODO: add support for constant sets';
  723. constpointer:
  724. { can only be null, but that's the default value and should not
  725. be written; there's no primitive type that can hold nill }
  726. internalerror(2011021201);
  727. constnil:
  728. internalerror(2011021202);
  729. constresourcestring:
  730. result:='TODO: add support for constant resource strings';
  731. constwstring:
  732. result:=constwstr(pcompilerwidestring(csym.value.valueptr)^.data,pcompilerwidestring(csym.value.valueptr)^.len);
  733. constguid:
  734. result:='TODO: add support for constant guids';
  735. else
  736. internalerror(2011021205);
  737. end;
  738. end;
  739. function TJasminAssembler.ConstAssignmentValue(csym: tconstsym): ansistring;
  740. begin
  741. result:='';
  742. { nil is the default value -> don't write explicitly }
  743. case csym.consttyp of
  744. constpointer:
  745. begin
  746. if csym.value.valueordptr<>0 then
  747. internalerror(2011021206);
  748. end;
  749. constnil:
  750. ;
  751. else
  752. begin
  753. { enums and sets are initialized as typed constants }
  754. if not assigned(csym.constdef) or
  755. not(csym.constdef.typ in [enumdef,setdef]) then
  756. result:=' = '+ConstValue(csym);
  757. end;
  758. end;
  759. end;
  760. function TJasminAssembler.ConstDefinition(sym: tconstsym): ansistring;
  761. begin
  762. result:=VisibilityToStr(sym.visibility);
  763. { formal constants are always class-level, not instance-level }
  764. result:=result+'static final ';
  765. if sp_internal in sym.symoptions then
  766. result:=result+'synthetic ';
  767. result:=result+jvmmangledbasename(sym,true);
  768. result:=result+ConstAssignmentValue(tconstsym(sym));
  769. end;
  770. function TJasminAssembler.FieldDefinition(sym: tabstractvarsym): ansistring;
  771. begin
  772. case sym.typ of
  773. staticvarsym:
  774. begin
  775. if sym.owner.symtabletype=globalsymtable then
  776. result:='public '
  777. else
  778. { package visbility }
  779. result:='';
  780. end;
  781. fieldvarsym,
  782. absolutevarsym:
  783. result:=VisibilityToStr(tstoredsym(sym).visibility);
  784. else
  785. internalerror(2011011204);
  786. end;
  787. if (sym.typ=staticvarsym) or
  788. (sp_static in sym.symoptions) then
  789. result:=result+'static ';
  790. if sym.varspez in [vs_const,vs_final] then
  791. result:=result+'final ';
  792. if sp_internal in sym.symoptions then
  793. result:=result+'synthetic ';
  794. { mark the class fields of enum classes that contain the initialised
  795. enum instances as "enum" (recognise them by the fact that their type
  796. is the same as their parent class, and that this parent class is
  797. marked as oo_is_enum_class) }
  798. if assigned(sym.owner.defowner) and
  799. (tdef(sym.owner.defowner).typ=objectdef) and
  800. (oo_is_enum_class in tobjectdef(sym.owner.defowner).objectoptions) and
  801. (sym.typ=staticvarsym) and
  802. (tstaticvarsym(sym).vardef=tdef(sym.owner.defowner)) then
  803. result:=result+'enum ';
  804. result:=result+jvmmangledbasename(sym,true);
  805. end;
  806. function TJasminAssembler.InnerStructDef(obj: tabstractrecorddef): ansistring;
  807. var
  808. extname: pshortstring;
  809. kindname: ansistring;
  810. begin
  811. if not(obj.owner.defowner.typ in [objectdef,recorddef]) then
  812. internalerror(2011021701);
  813. { Nested classes in the Pascal sense are equivalent to "static"
  814. inner classes in Java -- will be changed when support for
  815. Java-style non-static classes is added }
  816. case obj.typ of
  817. recorddef:
  818. begin
  819. kindname:='class static ';
  820. extname:=obj.symtable.realname;
  821. end;
  822. objectdef:
  823. begin
  824. extname:=tobjectdef(obj).objextname;
  825. case tobjectdef(obj).objecttype of
  826. odt_javaclass:
  827. kindname:='class static ';
  828. odt_interfacejava:
  829. kindname:='interface static abstract ';
  830. else
  831. internalerror(2011021702);
  832. end;
  833. end;
  834. else
  835. internalerror(2011032809);
  836. end;
  837. result:=
  838. '.inner '+
  839. kindname+
  840. VisibilityToStr(obj.typesym.visibility)+
  841. extname^+
  842. ' inner '+
  843. obj.jvm_full_typename(true)+
  844. ' outer '+
  845. tabstractrecorddef(obj.owner.defowner).jvm_full_typename(true);
  846. end;
  847. procedure TJasminAssembler.WriteProcDef(pd: tprocdef);
  848. begin
  849. if not assigned(tcpuprocdef(pd).exprasmlist) and
  850. not(po_abstractmethod in pd.procoptions) and
  851. (not is_javainterface(pd.struct) or
  852. (pd.proctypeoption in [potype_unitinit,potype_unitfinalize])) then
  853. exit;
  854. writer.AsmWrite('.method ');
  855. writer.AsmWriteln(MethodDefinition(pd));
  856. if jvmtypeneedssignature(pd) then
  857. begin
  858. writer.AsmWrite('.signature "');
  859. writer.AsmWrite(tcpuprocdef(pd).jvmmangledbasename(true));
  860. writer.AsmWriteln('"');
  861. end;
  862. WriteTree(tcpuprocdef(pd).exprasmlist);
  863. writer.AsmWriteln('.end method');
  864. writer.AsmLn;
  865. end;
  866. procedure TJasminAssembler.WriteFieldSym(sym: tabstractvarsym);
  867. begin
  868. { internal static field definition alias -> skip }
  869. if (sym.owner.symtabletype in [recordsymtable,ObjectSymtable]) and
  870. (sym.typ=staticvarsym) then
  871. exit;
  872. { external or threadvar definition -> no definition here }
  873. if ([vo_is_external,vo_is_thread_var]*sym.varoptions)<>[] then
  874. exit;
  875. writer.AsmWrite('.field ');
  876. writer.AsmWriteln(FieldDefinition(sym));
  877. end;
  878. procedure TJasminAssembler.WriteConstSym(sym: tconstsym);
  879. begin
  880. writer.AsmWrite('.field ');
  881. writer.AsmWriteln(ConstDefinition(sym));
  882. end;
  883. procedure TJasminAssembler.WriteSymtableVarSyms(st: TSymtable);
  884. var
  885. sym : tsym;
  886. i,j : longint;
  887. begin
  888. if not assigned(st) then
  889. exit;
  890. for i:=0 to st.SymList.Count-1 do
  891. begin
  892. sym:=tsym(st.SymList[i]);
  893. case sym.typ of
  894. staticvarsym,
  895. fieldvarsym:
  896. begin
  897. WriteFieldSym(tabstractvarsym(sym));
  898. if (sym.typ=staticvarsym) and
  899. assigned(tstaticvarsym(sym).defaultconstsym) then
  900. WriteFieldSym(tabstractvarsym(tstaticvarsym(sym).defaultconstsym));
  901. end;
  902. constsym:
  903. begin
  904. { multiple procedures can have constants with the same name }
  905. if not assigned(sym.owner.defowner) or
  906. (tdef(sym.owner.defowner).typ<>procdef) then
  907. WriteConstSym(tconstsym(sym));
  908. end;
  909. procsym:
  910. begin
  911. for j:=0 to tprocsym(sym).procdeflist.count-1 do
  912. if not(df_generic in tprocdef(tprocsym(sym).procdeflist[j]).defoptions) then
  913. WriteSymtableVarSyms(tprocdef(tprocsym(sym).procdeflist[j]).localst);
  914. end;
  915. end;
  916. end;
  917. end;
  918. procedure TJasminAssembler.WriteSymtableProcdefs(st: TSymtable);
  919. var
  920. i : longint;
  921. def : tdef;
  922. begin
  923. if not assigned(st) then
  924. exit;
  925. for i:=0 to st.DefList.Count-1 do
  926. begin
  927. def:=tdef(st.DefList[i]);
  928. case def.typ of
  929. procdef :
  930. begin
  931. { methods are also in the static/globalsymtable of the unit
  932. -> make sure they are only written for the objectdefs that
  933. own them }
  934. if (not(st.symtabletype in [staticsymtable,globalsymtable]) or
  935. (def.owner=st)) and
  936. not(df_generic in def.defoptions) then
  937. begin
  938. WriteProcDef(tprocdef(def));
  939. if assigned(tprocdef(def).localst) then
  940. WriteSymtableProcdefs(tprocdef(def).localst);
  941. end;
  942. end;
  943. end;
  944. end;
  945. end;
  946. procedure TJasminAssembler.WriteSymtableStructDefs(st: TSymtable);
  947. var
  948. i : longint;
  949. def : tdef;
  950. obj : tabstractrecorddef;
  951. nestedstructs: tfpobjectlist;
  952. begin
  953. if not assigned(st) then
  954. exit;
  955. nestedstructs:=tfpobjectlist.create(false);
  956. for i:=0 to st.DefList.Count-1 do
  957. begin
  958. def:=tdef(st.DefList[i]);
  959. if df_generic in def.defoptions then
  960. continue;
  961. case def.typ of
  962. objectdef:
  963. if not(oo_is_external in tobjectdef(def).objectoptions) then
  964. nestedstructs.add(def);
  965. recorddef:
  966. nestedstructs.add(def);
  967. end;
  968. end;
  969. for i:=0 to nestedstructs.count-1 do
  970. begin
  971. obj:=tabstractrecorddef(nestedstructs[i]);
  972. NewAsmFileForStructDef(obj);
  973. WriteExtraHeader(obj);
  974. WriteSymtableVarSyms(obj.symtable);
  975. writer.AsmLn;
  976. WriteSymtableProcDefs(obj.symtable);
  977. WriteSymtableStructDefs(obj.symtable);
  978. end;
  979. nestedstructs.free;
  980. end;
  981. function TJasminAssembler.CreateNewAsmWriter: TExternalAssemblerOutputFile;
  982. begin
  983. Result:=TJasminAssemblerOutputFile.Create(self);
  984. end;
  985. constructor TJasminAssembler.CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean);
  986. begin
  987. inherited;
  988. InstrWriter:=TJasminInstrWriter.Create(self);
  989. asmfiles:=TCmdStrList.Create;
  990. end;
  991. procedure TJasminAssembler.WriteAsmList;
  992. begin
  993. { the code for Java methods needs to be emitted class per class,
  994. so instead of iterating over all asmlists, we iterate over all types
  995. and global variables (a unit becomes a class, with its global
  996. variables static fields) }
  997. writer.MarkEmpty;
  998. WriteExtraHeader(nil);
  999. { print all global variables }
  1000. WriteSymtableVarSyms(current_module.globalsymtable);
  1001. WriteSymtableVarSyms(current_module.localsymtable);
  1002. writer.AsmLn;
  1003. { print all global procedures/functions }
  1004. WriteSymtableProcdefs(current_module.globalsymtable);
  1005. WriteSymtableProcdefs(current_module.localsymtable);
  1006. WriteSymtableStructDefs(current_module.globalsymtable);
  1007. WriteSymtableStructDefs(current_module.localsymtable);
  1008. writer.AsmLn;
  1009. end;
  1010. {****************************************************************************}
  1011. { Jasmin Instruction Writer }
  1012. {****************************************************************************}
  1013. constructor TJasminInstrWriter.create(_owner: TJasminAssembler);
  1014. begin
  1015. inherited create;
  1016. owner := _owner;
  1017. end;
  1018. function getreferencestring(var ref : treference) : ansistring;
  1019. begin
  1020. if (ref.arrayreftype<>art_none) or
  1021. (ref.index<>NR_NO) then
  1022. internalerror(2010122809);
  1023. if assigned(ref.symbol) then
  1024. begin
  1025. // global symbol or field -> full type and name
  1026. // ref.base can be <> NR_NO in case an instance field is loaded.
  1027. // This register is not part of this instruction, it will have
  1028. // been placed on the stack by the previous one.
  1029. if (ref.offset<>0) then
  1030. internalerror(2010122811);
  1031. result:=ref.symbol.name;
  1032. end
  1033. else
  1034. begin
  1035. // local symbol -> stack slot, stored in offset
  1036. if ref.base<>NR_STACK_POINTER_REG then
  1037. internalerror(2010122810);
  1038. result:=tostr(ref.offset);
  1039. end;
  1040. end;
  1041. function getopstr(const o:toper) : ansistring;
  1042. var
  1043. d: double;
  1044. s: single;
  1045. begin
  1046. case o.typ of
  1047. top_reg:
  1048. // should have been translated into a memory location by the
  1049. // register allocator)
  1050. if (cs_no_regalloc in current_settings.globalswitches) then
  1051. getopstr:=std_regname(o.reg)
  1052. else
  1053. internalerror(2010122803);
  1054. top_const:
  1055. str(o.val,result);
  1056. top_ref:
  1057. getopstr:=getreferencestring(o.ref^);
  1058. top_single:
  1059. begin
  1060. result:=constsingle(o.sval);
  1061. end;
  1062. top_double:
  1063. begin
  1064. result:=constdouble(o.dval);
  1065. end;
  1066. top_string:
  1067. begin
  1068. result:=constastr(o.pcval,o.pcvallen);
  1069. end;
  1070. top_wstring:
  1071. begin
  1072. result:=constwstr(o.pwstrval^.data,getlengthwidestring(o.pwstrval));
  1073. end
  1074. else
  1075. internalerror(2010122802);
  1076. end;
  1077. end;
  1078. procedure TJasminInstrWriter.WriteInstruction(hp: tai);
  1079. var
  1080. s: ansistring;
  1081. i: byte;
  1082. sep: ansistring;
  1083. begin
  1084. s:=#9+jas_op2str[taicpu(hp).opcode];
  1085. if taicpu(hp).ops<>0 then
  1086. begin
  1087. sep:=#9;
  1088. for i:=0 to taicpu(hp).ops-1 do
  1089. begin
  1090. s:=s+sep+getopstr(taicpu(hp).oper[i]^);
  1091. sep:=' ';
  1092. end;
  1093. end;
  1094. owner.writer.AsmWriteLn(s);
  1095. end;
  1096. {****************************************************************************}
  1097. { Jasmin Instruction Writer }
  1098. {****************************************************************************}
  1099. const
  1100. as_jvm_jasmin_info : tasminfo =
  1101. (
  1102. id : as_jvm_jasmin;
  1103. idtxt : 'Jasmin';
  1104. asmbin : 'java';
  1105. asmcmd : '-jar $JASMINJAR $ASM $EXTRAOPT -d $OBJDIR';
  1106. supported_targets : [system_jvm_java32,system_jvm_android32];
  1107. flags : [];
  1108. labelprefix : 'L';
  1109. comment : ' ; ';
  1110. dollarsign : '$';
  1111. );
  1112. begin
  1113. RegisterAssembler(as_jvm_jasmin_info,TJasminAssembler);
  1114. end.