agjasmin.pas 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247
  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. else
  555. ;
  556. end;
  557. { superclass }
  558. if assigned(superclass) then
  559. begin
  560. writer.AsmWrite('.super ');
  561. if assigned(superclass.import_lib) then
  562. writer.AsmWrite(superclass.import_lib^+'/');
  563. writer.AsmWriteln(superclass.objextname^);
  564. end;
  565. { implemented interfaces }
  566. if (obj.typ=objectdef) and
  567. assigned(tobjectdef(obj).ImplementedInterfaces) then
  568. begin
  569. for i:=0 to tobjectdef(obj).ImplementedInterfaces.count-1 do
  570. begin
  571. intf:=TImplementedInterface(tobjectdef(obj).ImplementedInterfaces[i]).IntfDef;
  572. writer.AsmWrite('.implements ');
  573. writer.AsmWriteLn(intf.jvm_full_typename(true));
  574. end;
  575. end;
  576. { signature for enum classes (must come after superclass and
  577. implemented interfaces) }
  578. if (obj.typ=objectdef) and
  579. (oo_is_enum_class in tobjectdef(obj).objectoptions) then
  580. writer.AsmWriteln('.signature "Ljava/lang/Enum<L'+obj.jvm_full_typename(true)+';>;"');
  581. { in case of nested class: relation to parent class }
  582. if obj.owner.symtabletype in [objectsymtable,recordsymtable] then
  583. writer.AsmWriteln(InnerStructDef(obj));
  584. { add all nested classes }
  585. for i:=0 to obj.symtable.deflist.count-1 do
  586. if (is_java_class_or_interface(tdef(obj.symtable.deflist[i])) or
  587. (tdef(obj.symtable.deflist[i]).typ=recorddef)) and
  588. not(df_generic in tdef(obj.symtable.deflist[i]).defoptions) then
  589. writer.AsmWriteln(InnerStructDef(tabstractrecorddef(obj.symtable.deflist[i])));
  590. end;
  591. writer.AsmLn;
  592. end;
  593. procedure TJasminAssembler.WriteInstruction(hp: tai);
  594. begin
  595. InstrWriter.WriteInstruction(hp);
  596. end;
  597. function TJasminAssembler.MakeCmdLine: TCmdStr;
  598. const
  599. jasminjarname = 'jasmin.jar';
  600. var
  601. filenames: tcmdstr;
  602. asmfile: tcmdstrlistitem;
  603. jasminjarfound: boolean;
  604. begin
  605. if jasminjar='' then
  606. begin
  607. jasminjarfound:=false;
  608. if utilsdirectory<>'' then
  609. jasminjarfound:=FindFile(jasminjarname,utilsdirectory,false,jasminjar);
  610. if not jasminjarfound then
  611. jasminjarfound:=FindFileInExeLocations(jasminjarname,false,jasminjar);
  612. if (not jasminjarfound) and not(cs_asm_extern in current_settings.globalswitches) then
  613. begin
  614. Message1(exec_e_assembler_not_found,jasminjarname);
  615. current_settings.globalswitches:=current_settings.globalswitches+[cs_asm_extern];
  616. end;
  617. if jasminjarfound then
  618. Message1(exec_t_using_assembler,jasminjar);
  619. end;
  620. result:=asminfo^.asmcmd;
  621. filenames:=ScriptFixFileName(AsmFileName);
  622. if cs_asm_extern in current_settings.globalswitches then
  623. filenames:=maybequoted(filenames);
  624. asmfile:=tcmdstrlistitem(asmfiles.First);
  625. while assigned(asmfile) do
  626. begin
  627. if cs_asm_extern in current_settings.globalswitches then
  628. filenames:=filenames+' '+maybequoted(ScriptFixFileName(asmfile.str))
  629. else
  630. filenames:=filenames+' '+ScriptFixFileName(asmfile.str);
  631. asmfile:=tcmdstrlistitem(asmfile.next);
  632. end;
  633. Replace(result,'$ASM',filenames);
  634. if (path<>'') then
  635. if cs_asm_extern in current_settings.globalswitches then
  636. Replace(result,'$OBJDIR',maybequoted(ScriptFixFileName(path)))
  637. else
  638. Replace(result,'$OBJDIR',ScriptFixFileName(path))
  639. else
  640. Replace(result,'$OBJDIR','.');
  641. if cs_asm_extern in current_settings.globalswitches then
  642. Replace(result,'$JASMINJAR',maybequoted(ScriptFixFileName(jasminjar)))
  643. else
  644. Replace(result,'$JASMINJAR',ScriptFixFileName(jasminjar));
  645. Replace(result,'$EXTRAOPT',asmextraopt);
  646. end;
  647. procedure TJasminAssembler.NewAsmFileForStructDef(obj: tabstractrecorddef);
  648. begin
  649. if not writer.ClearIfEmpty then
  650. begin
  651. writer.AsmClose;
  652. asmfiles.Concat(AsmFileName);
  653. end;
  654. AsmFileName:=obj.jvm_full_typename(false);
  655. AsmFileName:=Path+FixFileName(AsmFileName)+target_info.asmext;
  656. writer.AsmCreate(cut_normal);
  657. end;
  658. function TJasminAssembler.VisibilityToStr(vis: tvisibility): ansistring;
  659. begin
  660. case vis of
  661. vis_hidden,
  662. vis_strictprivate:
  663. result:='private ';
  664. { protected in Java means "accessible by subclasses *and* by classes
  665. in the same package" -> similar to regular "protected" in Pascal;
  666. "strict protected" is actually more strict in Pascal than in Java,
  667. but there's not much we can do about that }
  668. vis_protected,
  669. vis_strictprotected:
  670. result:='protected ';
  671. vis_private:
  672. { pick default visibility = "package" visibility; required because
  673. other classes in the same unit can also access these symbols }
  674. result:='';
  675. vis_public:
  676. result:='public '
  677. else
  678. internalerror(2010122609);
  679. end;
  680. end;
  681. function TJasminAssembler.MethodDefinition(pd: tprocdef): ansistring;
  682. begin
  683. result:=VisibilityToStr(pd.visibility);
  684. if (pd.procsym.owner.symtabletype in [globalsymtable,staticsymtable,localsymtable]) or
  685. (po_classmethod in pd.procoptions) then
  686. result:=result+'static ';
  687. if (po_abstractmethod in pd.procoptions) or
  688. is_javainterface(tdef(pd.owner.defowner)) then
  689. result:=result+'abstract ';
  690. if (pd.procsym.owner.symtabletype in [globalsymtable,staticsymtable,localsymtable]) or
  691. (po_finalmethod in pd.procoptions) or
  692. (not(po_virtualmethod in pd.procoptions) and
  693. not(po_classmethod in pd.procoptions) and
  694. not(pd.proctypeoption in [potype_constructor,potype_class_constructor])) then
  695. result:=result+'final ';
  696. result:=result+tcpuprocdef(pd).jvmmangledbasename(false);
  697. end;
  698. function TJasminAssembler.ConstValue(csym: tconstsym): ansistring;
  699. begin
  700. case csym.consttyp of
  701. constord:
  702. { always interpret as signed value, because the JVM does not
  703. support unsigned values }
  704. case csym.constdef.size of
  705. 1:result:=tostr(shortint(csym.value.valueord.svalue));
  706. 2:result:=tostr(smallint(csym.value.valueord.svalue));
  707. 4:result:=tostr(longint(csym.value.valueord.svalue));
  708. 8:result:=tostr(csym.value.valueord.svalue);
  709. else
  710. internalerror(2014082050);
  711. end;
  712. conststring:
  713. result:=constastr(pchar(csym.value.valueptr),csym.value.len);
  714. constreal:
  715. case tfloatdef(csym.constdef).floattype of
  716. s32real:
  717. result:=constsingle(pbestreal(csym.value.valueptr)^);
  718. s64real:
  719. result:=constdouble(pbestreal(csym.value.valueptr)^);
  720. else
  721. internalerror(2011021204);
  722. end;
  723. constset:
  724. result:='TODO: add support for constant sets';
  725. constpointer:
  726. { can only be null, but that's the default value and should not
  727. be written; there's no primitive type that can hold nill }
  728. internalerror(2011021201);
  729. constnil:
  730. internalerror(2011021202);
  731. constresourcestring:
  732. result:='TODO: add support for constant resource strings';
  733. constwstring:
  734. result:=constwstr(pcompilerwidestring(csym.value.valueptr)^.data,pcompilerwidestring(csym.value.valueptr)^.len);
  735. constguid:
  736. result:='TODO: add support for constant guids';
  737. else
  738. internalerror(2011021205);
  739. end;
  740. end;
  741. function TJasminAssembler.ConstAssignmentValue(csym: tconstsym): ansistring;
  742. begin
  743. result:='';
  744. { nil is the default value -> don't write explicitly }
  745. case csym.consttyp of
  746. constpointer:
  747. begin
  748. if csym.value.valueordptr<>0 then
  749. internalerror(2011021206);
  750. end;
  751. constnil:
  752. ;
  753. else
  754. begin
  755. { enums and sets are initialized as typed constants }
  756. if not assigned(csym.constdef) or
  757. not(csym.constdef.typ in [enumdef,setdef]) then
  758. result:=' = '+ConstValue(csym);
  759. end;
  760. end;
  761. end;
  762. function TJasminAssembler.ConstDefinition(sym: tconstsym): ansistring;
  763. begin
  764. result:=VisibilityToStr(sym.visibility);
  765. { formal constants are always class-level, not instance-level }
  766. result:=result+'static final ';
  767. if sp_internal in sym.symoptions then
  768. result:=result+'synthetic ';
  769. result:=result+jvmmangledbasename(sym,true);
  770. result:=result+ConstAssignmentValue(tconstsym(sym));
  771. end;
  772. function TJasminAssembler.FieldDefinition(sym: tabstractvarsym): ansistring;
  773. begin
  774. case sym.typ of
  775. staticvarsym:
  776. begin
  777. if sym.owner.symtabletype=globalsymtable then
  778. result:='public '
  779. else
  780. { package visbility }
  781. result:='';
  782. end;
  783. fieldvarsym,
  784. absolutevarsym:
  785. result:=VisibilityToStr(tstoredsym(sym).visibility);
  786. else
  787. internalerror(2011011204);
  788. end;
  789. if (sym.typ=staticvarsym) or
  790. (sp_static in sym.symoptions) then
  791. result:=result+'static ';
  792. if sym.varspez in [vs_const,vs_final] then
  793. result:=result+'final ';
  794. if sp_internal in sym.symoptions then
  795. result:=result+'synthetic ';
  796. { mark the class fields of enum classes that contain the initialised
  797. enum instances as "enum" (recognise them by the fact that their type
  798. is the same as their parent class, and that this parent class is
  799. marked as oo_is_enum_class) }
  800. if assigned(sym.owner.defowner) and
  801. (tdef(sym.owner.defowner).typ=objectdef) and
  802. (oo_is_enum_class in tobjectdef(sym.owner.defowner).objectoptions) and
  803. (sym.typ=staticvarsym) and
  804. (tstaticvarsym(sym).vardef=tdef(sym.owner.defowner)) then
  805. result:=result+'enum ';
  806. result:=result+jvmmangledbasename(sym,true);
  807. end;
  808. function TJasminAssembler.InnerStructDef(obj: tabstractrecorddef): ansistring;
  809. var
  810. extname: pshortstring;
  811. kindname: ansistring;
  812. begin
  813. if not(obj.owner.defowner.typ in [objectdef,recorddef]) then
  814. internalerror(2011021701);
  815. { Nested classes in the Pascal sense are equivalent to "static"
  816. inner classes in Java -- will be changed when support for
  817. Java-style non-static classes is added }
  818. case obj.typ of
  819. recorddef:
  820. begin
  821. kindname:='class static ';
  822. extname:=obj.symtable.realname;
  823. end;
  824. objectdef:
  825. begin
  826. extname:=tobjectdef(obj).objextname;
  827. case tobjectdef(obj).objecttype of
  828. odt_javaclass:
  829. kindname:='class static ';
  830. odt_interfacejava:
  831. kindname:='interface static abstract ';
  832. else
  833. internalerror(2011021702);
  834. end;
  835. end;
  836. else
  837. internalerror(2011032809);
  838. end;
  839. result:=
  840. '.inner '+
  841. kindname+
  842. VisibilityToStr(obj.typesym.visibility)+
  843. extname^+
  844. ' inner '+
  845. obj.jvm_full_typename(true)+
  846. ' outer '+
  847. tabstractrecorddef(obj.owner.defowner).jvm_full_typename(true);
  848. end;
  849. procedure TJasminAssembler.WriteProcDef(pd: tprocdef);
  850. begin
  851. if not assigned(tcpuprocdef(pd).exprasmlist) and
  852. not(po_abstractmethod in pd.procoptions) and
  853. (not is_javainterface(pd.struct) or
  854. (pd.proctypeoption in [potype_unitinit,potype_unitfinalize])) then
  855. exit;
  856. writer.AsmWrite('.method ');
  857. writer.AsmWriteln(MethodDefinition(pd));
  858. if jvmtypeneedssignature(pd) then
  859. begin
  860. writer.AsmWrite('.signature "');
  861. writer.AsmWrite(tcpuprocdef(pd).jvmmangledbasename(true));
  862. writer.AsmWriteln('"');
  863. end;
  864. WriteTree(tcpuprocdef(pd).exprasmlist);
  865. writer.AsmWriteln('.end method');
  866. writer.AsmLn;
  867. end;
  868. procedure TJasminAssembler.WriteFieldSym(sym: tabstractvarsym);
  869. begin
  870. { internal static field definition alias -> skip }
  871. if (sym.owner.symtabletype in [recordsymtable,ObjectSymtable]) and
  872. (sym.typ=staticvarsym) then
  873. exit;
  874. { external or threadvar definition -> no definition here }
  875. if ([vo_is_external,vo_is_thread_var]*sym.varoptions)<>[] then
  876. exit;
  877. writer.AsmWrite('.field ');
  878. writer.AsmWriteln(FieldDefinition(sym));
  879. end;
  880. procedure TJasminAssembler.WriteConstSym(sym: tconstsym);
  881. begin
  882. writer.AsmWrite('.field ');
  883. writer.AsmWriteln(ConstDefinition(sym));
  884. end;
  885. procedure TJasminAssembler.WriteSymtableVarSyms(st: TSymtable);
  886. var
  887. sym : tsym;
  888. i,j : longint;
  889. begin
  890. if not assigned(st) then
  891. exit;
  892. for i:=0 to st.SymList.Count-1 do
  893. begin
  894. sym:=tsym(st.SymList[i]);
  895. case sym.typ of
  896. staticvarsym,
  897. fieldvarsym:
  898. begin
  899. WriteFieldSym(tabstractvarsym(sym));
  900. if (sym.typ=staticvarsym) and
  901. assigned(tstaticvarsym(sym).defaultconstsym) then
  902. WriteFieldSym(tabstractvarsym(tstaticvarsym(sym).defaultconstsym));
  903. end;
  904. constsym:
  905. begin
  906. { multiple procedures can have constants with the same name }
  907. if not assigned(sym.owner.defowner) or
  908. (tdef(sym.owner.defowner).typ<>procdef) then
  909. WriteConstSym(tconstsym(sym));
  910. end;
  911. procsym:
  912. begin
  913. for j:=0 to tprocsym(sym).procdeflist.count-1 do
  914. if not(df_generic in tprocdef(tprocsym(sym).procdeflist[j]).defoptions) then
  915. WriteSymtableVarSyms(tprocdef(tprocsym(sym).procdeflist[j]).localst);
  916. end;
  917. else
  918. ;
  919. end;
  920. end;
  921. end;
  922. procedure TJasminAssembler.WriteSymtableProcdefs(st: TSymtable);
  923. var
  924. i : longint;
  925. def : tdef;
  926. begin
  927. if not assigned(st) then
  928. exit;
  929. for i:=0 to st.DefList.Count-1 do
  930. begin
  931. def:=tdef(st.DefList[i]);
  932. case def.typ of
  933. procdef :
  934. begin
  935. { methods are also in the static/globalsymtable of the unit
  936. -> make sure they are only written for the objectdefs that
  937. own them }
  938. if (not(st.symtabletype in [staticsymtable,globalsymtable]) or
  939. (def.owner=st)) and
  940. not(df_generic in def.defoptions) then
  941. begin
  942. WriteProcDef(tprocdef(def));
  943. if assigned(tprocdef(def).localst) then
  944. WriteSymtableProcdefs(tprocdef(def).localst);
  945. end;
  946. end;
  947. else
  948. ;
  949. end;
  950. end;
  951. end;
  952. procedure TJasminAssembler.WriteSymtableStructDefs(st: TSymtable);
  953. var
  954. i : longint;
  955. def : tdef;
  956. obj : tabstractrecorddef;
  957. nestedstructs: tfpobjectlist;
  958. begin
  959. if not assigned(st) then
  960. exit;
  961. nestedstructs:=tfpobjectlist.create(false);
  962. for i:=0 to st.DefList.Count-1 do
  963. begin
  964. def:=tdef(st.DefList[i]);
  965. if df_generic in def.defoptions then
  966. continue;
  967. case def.typ of
  968. objectdef:
  969. if not(oo_is_external in tobjectdef(def).objectoptions) then
  970. nestedstructs.add(def);
  971. recorddef:
  972. nestedstructs.add(def);
  973. else
  974. ;
  975. end;
  976. end;
  977. for i:=0 to nestedstructs.count-1 do
  978. begin
  979. obj:=tabstractrecorddef(nestedstructs[i]);
  980. NewAsmFileForStructDef(obj);
  981. WriteExtraHeader(obj);
  982. WriteSymtableVarSyms(obj.symtable);
  983. writer.AsmLn;
  984. WriteSymtableProcDefs(obj.symtable);
  985. WriteSymtableStructDefs(obj.symtable);
  986. end;
  987. nestedstructs.free;
  988. end;
  989. function TJasminAssembler.CreateNewAsmWriter: TExternalAssemblerOutputFile;
  990. begin
  991. Result:=TJasminAssemblerOutputFile.Create(self);
  992. end;
  993. constructor TJasminAssembler.CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean);
  994. begin
  995. inherited;
  996. InstrWriter:=TJasminInstrWriter.Create(self);
  997. asmfiles:=TCmdStrList.Create;
  998. end;
  999. procedure TJasminAssembler.WriteAsmList;
  1000. begin
  1001. { the code for Java methods needs to be emitted class per class,
  1002. so instead of iterating over all asmlists, we iterate over all types
  1003. and global variables (a unit becomes a class, with its global
  1004. variables static fields) }
  1005. writer.MarkEmpty;
  1006. WriteExtraHeader(nil);
  1007. { print all global variables }
  1008. WriteSymtableVarSyms(current_module.globalsymtable);
  1009. WriteSymtableVarSyms(current_module.localsymtable);
  1010. writer.AsmLn;
  1011. { print all global procedures/functions }
  1012. WriteSymtableProcdefs(current_module.globalsymtable);
  1013. WriteSymtableProcdefs(current_module.localsymtable);
  1014. WriteSymtableStructDefs(current_module.globalsymtable);
  1015. WriteSymtableStructDefs(current_module.localsymtable);
  1016. writer.AsmLn;
  1017. end;
  1018. {****************************************************************************}
  1019. { Jasmin Instruction Writer }
  1020. {****************************************************************************}
  1021. constructor TJasminInstrWriter.create(_owner: TJasminAssembler);
  1022. begin
  1023. inherited create;
  1024. owner := _owner;
  1025. end;
  1026. function getreferencestring(var ref : treference) : ansistring;
  1027. begin
  1028. if (ref.arrayreftype<>art_none) or
  1029. (ref.index<>NR_NO) then
  1030. internalerror(2010122809);
  1031. if assigned(ref.symbol) then
  1032. begin
  1033. // global symbol or field -> full type and name
  1034. // ref.base can be <> NR_NO in case an instance field is loaded.
  1035. // This register is not part of this instruction, it will have
  1036. // been placed on the stack by the previous one.
  1037. if (ref.offset<>0) then
  1038. internalerror(2010122811);
  1039. result:=ref.symbol.name;
  1040. end
  1041. else
  1042. begin
  1043. // local symbol -> stack slot, stored in offset
  1044. if ref.base<>NR_STACK_POINTER_REG then
  1045. internalerror(2010122810);
  1046. result:=tostr(ref.offset);
  1047. end;
  1048. end;
  1049. function getopstr(const o:toper) : ansistring;
  1050. var
  1051. d: double;
  1052. s: single;
  1053. begin
  1054. case o.typ of
  1055. top_reg:
  1056. // should have been translated into a memory location by the
  1057. // register allocator)
  1058. if (cs_no_regalloc in current_settings.globalswitches) then
  1059. getopstr:=std_regname(o.reg)
  1060. else
  1061. internalerror(2010122803);
  1062. top_const:
  1063. str(o.val,result);
  1064. top_ref:
  1065. getopstr:=getreferencestring(o.ref^);
  1066. top_single:
  1067. begin
  1068. result:=constsingle(o.sval);
  1069. end;
  1070. top_double:
  1071. begin
  1072. result:=constdouble(o.dval);
  1073. end;
  1074. top_string:
  1075. begin
  1076. result:=constastr(o.pcval,o.pcvallen);
  1077. end;
  1078. top_wstring:
  1079. begin
  1080. result:=constwstr(o.pwstrval^.data,getlengthwidestring(o.pwstrval));
  1081. end
  1082. else
  1083. internalerror(2010122802);
  1084. end;
  1085. end;
  1086. procedure TJasminInstrWriter.WriteInstruction(hp: tai);
  1087. var
  1088. s: ansistring;
  1089. i: byte;
  1090. sep: ansistring;
  1091. begin
  1092. s:=#9+jas_op2str[taicpu(hp).opcode];
  1093. if taicpu(hp).ops<>0 then
  1094. begin
  1095. sep:=#9;
  1096. for i:=0 to taicpu(hp).ops-1 do
  1097. begin
  1098. s:=s+sep+getopstr(taicpu(hp).oper[i]^);
  1099. sep:=' ';
  1100. end;
  1101. end;
  1102. owner.writer.AsmWriteLn(s);
  1103. end;
  1104. {****************************************************************************}
  1105. { Jasmin Instruction Writer }
  1106. {****************************************************************************}
  1107. const
  1108. as_jvm_jasmin_info : tasminfo =
  1109. (
  1110. id : as_jvm_jasmin;
  1111. idtxt : 'Jasmin';
  1112. asmbin : 'java';
  1113. asmcmd : '-jar $JASMINJAR $ASM $EXTRAOPT -d $OBJDIR';
  1114. supported_targets : [system_jvm_java32,system_jvm_android32];
  1115. flags : [];
  1116. labelprefix : 'L';
  1117. comment : ' ; ';
  1118. dollarsign : '$';
  1119. );
  1120. begin
  1121. RegisterAssembler(as_jvm_jasmin_info,TJasminAssembler);
  1122. end.