agjasmin.pas 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205
  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_align :
  316. begin
  317. end;
  318. ait_section :
  319. begin
  320. end;
  321. ait_datablock :
  322. begin
  323. internalerror(2010122701);
  324. end;
  325. ait_const:
  326. begin
  327. writer.AsmWriteln('constant');
  328. // internalerror(2010122702);
  329. end;
  330. ait_realconst :
  331. begin
  332. internalerror(2010122703);
  333. end;
  334. ait_string :
  335. begin
  336. pos:=0;
  337. for i:=1 to tai_string(hp).len do
  338. begin
  339. if pos=0 then
  340. begin
  341. writer.AsmWrite(#9'strconst: '#9'"');
  342. pos:=20;
  343. end;
  344. ch:=tai_string(hp).str[i-1];
  345. case ch of
  346. #0, {This can't be done by range, because a bug in FPC}
  347. #1..#31,
  348. #128..#255 : s:='\'+tostr(ord(ch) shr 6)+tostr((ord(ch) and 63) shr 3)+tostr(ord(ch) and 7);
  349. '"' : s:='\"';
  350. '\' : s:='\\';
  351. else
  352. s:=ch;
  353. end;
  354. writer.AsmWrite(s);
  355. inc(pos,length(s));
  356. if (pos>line_length) or (i=tai_string(hp).len) then
  357. begin
  358. writer.AsmWriteLn('"');
  359. pos:=0;
  360. end;
  361. end;
  362. end;
  363. ait_label :
  364. begin
  365. if (tai_label(hp).labsym.is_used) then
  366. begin
  367. writer.AsmWrite(tai_label(hp).labsym.name);
  368. writer.AsmWriteLn(':');
  369. end;
  370. end;
  371. ait_symbol :
  372. begin
  373. if (tai_symbol(hp).sym.typ = AT_FUNCTION) then
  374. begin
  375. end
  376. else
  377. begin
  378. writer.AsmWrite('data symbol: ');
  379. writer.AsmWriteln(tai_symbol(hp).sym.name);
  380. // internalerror(2010122706);
  381. end;
  382. end;
  383. ait_symbol_end :
  384. begin
  385. end;
  386. ait_instruction :
  387. begin
  388. WriteInstruction(hp);
  389. end;
  390. ait_force_line,
  391. ait_function_name : ;
  392. ait_cutobject :
  393. begin
  394. end;
  395. ait_marker :
  396. if tai_marker(hp).kind=mark_NoLineInfoStart then
  397. inc(InlineLevel)
  398. else if tai_marker(hp).kind=mark_NoLineInfoEnd then
  399. dec(InlineLevel);
  400. ait_directive :
  401. begin
  402. { the CPU directive is probably not supported by the JVM assembler,
  403. so it's commented out }
  404. if tai_directive(hp).directive=asd_cpu then
  405. writer.AsmWrite(asminfo^.comment);
  406. writer.AsmWrite('.'+directivestr[tai_directive(hp).directive]+' ');
  407. if tai_directive(hp).name<>'' then
  408. writer.AsmWrite(tai_directive(hp).name);
  409. writer.AsmLn;
  410. end;
  411. ait_jvar:
  412. begin
  413. writer.AsmWrite('.var ');
  414. writer.AsmWrite(tostr(tai_jvar(hp).stackslot));
  415. writer.AsmWrite(' is ');
  416. writer.AsmWrite(tai_jvar(hp).desc^);
  417. writer.AsmWrite(' from ');
  418. writer.AsmWrite(tai_jvar(hp).startlab.name);
  419. writer.AsmWrite(' to ');
  420. writer.AsmWriteLn(tai_jvar(hp).stoplab.name);
  421. end;
  422. ait_jcatch:
  423. begin
  424. writer.AsmWrite('.catch ');
  425. writer.AsmWrite(tai_jcatch(hp).name^);
  426. writer.AsmWrite(' from ');
  427. writer.AsmWrite(tai_jcatch(hp).startlab.name);
  428. writer.AsmWrite(' to ');
  429. writer.AsmWrite(tai_jcatch(hp).stoplab.name);
  430. writer.AsmWrite(' using ');
  431. writer.AsmWriteLn(tai_jcatch(hp).handlerlab.name);
  432. end;
  433. else
  434. if not WriteComments(hp) then
  435. internalerror(2010122707);
  436. end;
  437. hp:=tai(hp.next);
  438. end;
  439. end;
  440. procedure TJasminAssembler.WriteExtraHeader(obj: tabstractrecorddef);
  441. var
  442. superclass,
  443. intf: tobjectdef;
  444. n: ansistring;
  445. i: longint;
  446. toplevelowner: tsymtable;
  447. begin
  448. superclass:=nil;
  449. { JVM 1.5+ }
  450. writer.AsmWriteLn('.bytecode 49.0');
  451. // include files are not support by Java, and the directory of the main
  452. // source file must not be specified
  453. if current_module.mainsource<>'' then
  454. n:=ExtractFileName(current_module.mainsource)
  455. else
  456. n:=InputFileName;
  457. writer.AsmWriteLn('.source '+ExtractFileName(n));
  458. { class/interface name }
  459. if not assigned(obj) then
  460. begin
  461. { fake class type for unit -> name=unitname and
  462. superclass=java.lang.object, make final so you cannot descend
  463. from it }
  464. writer.AsmWrite('.class final public ');
  465. if assigned(current_module.namespace) then
  466. writer.AsmWrite(current_module.namespace^+'.');
  467. writer.AsmWriteln(current_module.realmodulename^);
  468. writer.AsmWriteLn('.super java/lang/Object');
  469. end
  470. else
  471. begin
  472. toplevelowner:=obj.owner;
  473. while not(toplevelowner.symtabletype in [staticsymtable,globalsymtable]) do
  474. toplevelowner:=toplevelowner.defowner.owner;
  475. case obj.typ of
  476. recorddef:
  477. begin
  478. { can't inherit from records }
  479. writer.AsmWrite('.class final ');
  480. if toplevelowner.symtabletype=globalsymtable then
  481. writer.AsmWrite('public ');
  482. writer.AsmWriteln(obj.jvm_full_typename(true));
  483. superclass:=java_fpcbaserecordtype;
  484. end;
  485. objectdef:
  486. begin
  487. case tobjectdef(obj).objecttype of
  488. odt_javaclass:
  489. begin
  490. writer.AsmWrite('.class ');
  491. if oo_is_sealed in tobjectdef(obj).objectoptions then
  492. writer.AsmWrite('final ');
  493. if (oo_is_abstract in tobjectdef(obj).objectoptions) or
  494. (tobjectdef(obj).abstractcnt<>0) then
  495. writer.AsmWrite('abstract ');
  496. if toplevelowner.symtabletype=globalsymtable then
  497. writer.AsmWrite('public ');
  498. if (oo_is_enum_class in tobjectdef(obj).objectoptions) then
  499. writer.AsmWrite('enum ');
  500. writer.AsmWriteln(obj.jvm_full_typename(true));
  501. superclass:=tobjectdef(obj).childof;
  502. end;
  503. odt_interfacejava:
  504. begin
  505. writer.AsmWrite('.interface abstract ');
  506. if toplevelowner.symtabletype=globalsymtable then
  507. writer.AsmWrite('public ');
  508. writer.AsmWriteLn(obj.jvm_full_typename(true));
  509. { interfaces must always specify Java.lang.object as
  510. superclass }
  511. superclass:=java_jlobject;
  512. end
  513. else
  514. internalerror(2011010906);
  515. end;
  516. end;
  517. else
  518. ;
  519. end;
  520. { superclass }
  521. if assigned(superclass) then
  522. begin
  523. writer.AsmWrite('.super ');
  524. if assigned(superclass.import_lib) then
  525. writer.AsmWrite(superclass.import_lib^+'/');
  526. writer.AsmWriteln(superclass.objextname^);
  527. end;
  528. { implemented interfaces }
  529. if (obj.typ=objectdef) and
  530. assigned(tobjectdef(obj).ImplementedInterfaces) then
  531. begin
  532. for i:=0 to tobjectdef(obj).ImplementedInterfaces.count-1 do
  533. begin
  534. intf:=TImplementedInterface(tobjectdef(obj).ImplementedInterfaces[i]).IntfDef;
  535. writer.AsmWrite('.implements ');
  536. writer.AsmWriteLn(intf.jvm_full_typename(true));
  537. end;
  538. end;
  539. { signature for enum classes (must come after superclass and
  540. implemented interfaces) }
  541. if (obj.typ=objectdef) and
  542. (oo_is_enum_class in tobjectdef(obj).objectoptions) then
  543. writer.AsmWriteln('.signature "Ljava/lang/Enum<L'+obj.jvm_full_typename(true)+';>;"');
  544. { in case of nested class: relation to parent class }
  545. if obj.owner.symtabletype in [objectsymtable,recordsymtable] then
  546. writer.AsmWriteln(InnerStructDef(obj));
  547. { add all nested classes }
  548. for i:=0 to obj.symtable.deflist.count-1 do
  549. if (is_java_class_or_interface(tdef(obj.symtable.deflist[i])) or
  550. (tdef(obj.symtable.deflist[i]).typ=recorddef)) and
  551. not(df_generic in tdef(obj.symtable.deflist[i]).defoptions) then
  552. writer.AsmWriteln(InnerStructDef(tabstractrecorddef(obj.symtable.deflist[i])));
  553. end;
  554. writer.AsmLn;
  555. end;
  556. procedure TJasminAssembler.WriteInstruction(hp: tai);
  557. begin
  558. InstrWriter.WriteInstruction(hp);
  559. end;
  560. function TJasminAssembler.MakeCmdLine: TCmdStr;
  561. const
  562. jasminjarname = 'jasmin.jar';
  563. var
  564. filenames: tcmdstr;
  565. asmfile: tcmdstrlistitem;
  566. jasminjarfound: boolean;
  567. begin
  568. if jasminjar='' then
  569. begin
  570. jasminjarfound:=false;
  571. if utilsdirectory<>'' then
  572. jasminjarfound:=FindFile(jasminjarname,utilsdirectory,false,jasminjar);
  573. if not jasminjarfound then
  574. jasminjarfound:=FindFileInExeLocations(jasminjarname,false,jasminjar);
  575. if (not jasminjarfound) and not(cs_asm_extern in current_settings.globalswitches) then
  576. begin
  577. Message1(exec_e_assembler_not_found,jasminjarname);
  578. current_settings.globalswitches:=current_settings.globalswitches+[cs_asm_extern];
  579. end;
  580. if jasminjarfound then
  581. Message1(exec_t_using_assembler,jasminjar);
  582. end;
  583. result:=asminfo^.asmcmd;
  584. filenames:=ScriptFixFileName(AsmFileName);
  585. if cs_asm_extern in current_settings.globalswitches then
  586. filenames:=maybequoted(filenames);
  587. asmfile:=tcmdstrlistitem(asmfiles.First);
  588. while assigned(asmfile) do
  589. begin
  590. if cs_asm_extern in current_settings.globalswitches then
  591. filenames:=filenames+' '+maybequoted(ScriptFixFileName(asmfile.str))
  592. else
  593. filenames:=filenames+' '+ScriptFixFileName(asmfile.str);
  594. asmfile:=tcmdstrlistitem(asmfile.next);
  595. end;
  596. Replace(result,'$ASM',filenames);
  597. if (path<>'') then
  598. if cs_asm_extern in current_settings.globalswitches then
  599. Replace(result,'$OBJDIR',maybequoted(ScriptFixFileName(path)))
  600. else
  601. Replace(result,'$OBJDIR',ScriptFixFileName(path))
  602. else
  603. Replace(result,'$OBJDIR','.');
  604. if cs_asm_extern in current_settings.globalswitches then
  605. Replace(result,'$JASMINJAR',maybequoted(ScriptFixFileName(jasminjar)))
  606. else
  607. Replace(result,'$JASMINJAR',ScriptFixFileName(jasminjar));
  608. Replace(result,'$EXTRAOPT',asmextraopt);
  609. end;
  610. procedure TJasminAssembler.NewAsmFileForStructDef(obj: tabstractrecorddef);
  611. begin
  612. if not writer.ClearIfEmpty then
  613. begin
  614. writer.AsmClose;
  615. asmfiles.Concat(AsmFileName);
  616. end;
  617. AsmFileName:=obj.jvm_full_typename(false);
  618. AsmFileName:=Path+FixFileName(AsmFileName)+target_info.asmext;
  619. writer.AsmCreate(cut_normal);
  620. end;
  621. function TJasminAssembler.VisibilityToStr(vis: tvisibility): ansistring;
  622. begin
  623. case vis of
  624. vis_hidden,
  625. vis_strictprivate:
  626. result:='private ';
  627. { protected in Java means "accessible by subclasses *and* by classes
  628. in the same package" -> similar to regular "protected" in Pascal;
  629. "strict protected" is actually more strict in Pascal than in Java,
  630. but there's not much we can do about that }
  631. vis_protected,
  632. vis_strictprotected:
  633. result:='protected ';
  634. vis_private:
  635. { pick default visibility = "package" visibility; required because
  636. other classes in the same unit can also access these symbols }
  637. result:='';
  638. vis_public:
  639. result:='public '
  640. else
  641. internalerror(2010122609);
  642. end;
  643. end;
  644. function TJasminAssembler.MethodDefinition(pd: tprocdef): ansistring;
  645. begin
  646. result:=VisibilityToStr(pd.visibility);
  647. if (pd.procsym.owner.symtabletype in [globalsymtable,staticsymtable,localsymtable]) or
  648. (po_classmethod in pd.procoptions) then
  649. result:=result+'static ';
  650. if (po_abstractmethod in pd.procoptions) or
  651. is_javainterface(tdef(pd.owner.defowner)) then
  652. result:=result+'abstract ';
  653. if (pd.procsym.owner.symtabletype in [globalsymtable,staticsymtable,localsymtable]) or
  654. (po_finalmethod in pd.procoptions) or
  655. (not(po_virtualmethod in pd.procoptions) and
  656. not(po_classmethod in pd.procoptions) and
  657. not(pd.proctypeoption in [potype_constructor,potype_class_constructor])) then
  658. result:=result+'final ';
  659. result:=result+tcpuprocdef(pd).jvmmangledbasename(false);
  660. end;
  661. function TJasminAssembler.ConstValue(csym: tconstsym): ansistring;
  662. begin
  663. case csym.consttyp of
  664. constord:
  665. { always interpret as signed value, because the JVM does not
  666. support unsigned values }
  667. case csym.constdef.size of
  668. 1:result:=tostr(shortint(csym.value.valueord.svalue));
  669. 2:result:=tostr(smallint(csym.value.valueord.svalue));
  670. 4:result:=tostr(longint(csym.value.valueord.svalue));
  671. 8:result:=tostr(csym.value.valueord.svalue);
  672. else
  673. internalerror(2014082050);
  674. end;
  675. conststring:
  676. result:=constastr(pchar(csym.value.valueptr),csym.value.len);
  677. constreal:
  678. case tfloatdef(csym.constdef).floattype of
  679. s32real:
  680. result:=constsingle(pbestreal(csym.value.valueptr)^);
  681. s64real:
  682. result:=constdouble(pbestreal(csym.value.valueptr)^);
  683. else
  684. internalerror(2011021204);
  685. end;
  686. constset:
  687. result:='TODO: add support for constant sets';
  688. constpointer:
  689. { can only be null, but that's the default value and should not
  690. be written; there's no primitive type that can hold nill }
  691. internalerror(2011021201);
  692. constnil:
  693. internalerror(2011021202);
  694. constresourcestring:
  695. result:='TODO: add support for constant resource strings';
  696. constwstring:
  697. result:=constwstr(pcompilerwidestring(csym.value.valueptr)^.data,pcompilerwidestring(csym.value.valueptr)^.len);
  698. constguid:
  699. result:='TODO: add support for constant guids';
  700. else
  701. internalerror(2011021205);
  702. end;
  703. end;
  704. function TJasminAssembler.ConstAssignmentValue(csym: tconstsym): ansistring;
  705. begin
  706. result:='';
  707. { nil is the default value -> don't write explicitly }
  708. case csym.consttyp of
  709. constpointer:
  710. begin
  711. if csym.value.valueordptr<>0 then
  712. internalerror(2011021206);
  713. end;
  714. constnil:
  715. ;
  716. else
  717. begin
  718. { enums and sets are initialized as typed constants }
  719. if not assigned(csym.constdef) or
  720. not(csym.constdef.typ in [enumdef,setdef]) then
  721. result:=' = '+ConstValue(csym);
  722. end;
  723. end;
  724. end;
  725. function TJasminAssembler.ConstDefinition(sym: tconstsym): ansistring;
  726. begin
  727. result:=VisibilityToStr(sym.visibility);
  728. { formal constants are always class-level, not instance-level }
  729. result:=result+'static final ';
  730. if sp_internal in sym.symoptions then
  731. result:=result+'synthetic ';
  732. result:=result+jvmmangledbasename(sym,true);
  733. result:=result+ConstAssignmentValue(tconstsym(sym));
  734. end;
  735. function TJasminAssembler.FieldDefinition(sym: tabstractvarsym): ansistring;
  736. begin
  737. case sym.typ of
  738. staticvarsym:
  739. begin
  740. if sym.owner.symtabletype=globalsymtable then
  741. result:='public '
  742. else
  743. { package visbility }
  744. result:='';
  745. end;
  746. fieldvarsym,
  747. absolutevarsym:
  748. result:=VisibilityToStr(tstoredsym(sym).visibility);
  749. else
  750. internalerror(2011011204);
  751. end;
  752. if (sym.typ=staticvarsym) or
  753. (sp_static in sym.symoptions) then
  754. result:=result+'static ';
  755. if sym.varspez in [vs_const,vs_final] then
  756. result:=result+'final ';
  757. if sp_internal in sym.symoptions then
  758. result:=result+'synthetic ';
  759. { mark the class fields of enum classes that contain the initialised
  760. enum instances as "enum" (recognise them by the fact that their type
  761. is the same as their parent class, and that this parent class is
  762. marked as oo_is_enum_class) }
  763. if assigned(sym.owner.defowner) and
  764. (tdef(sym.owner.defowner).typ=objectdef) and
  765. (oo_is_enum_class in tobjectdef(sym.owner.defowner).objectoptions) and
  766. (sym.typ=staticvarsym) and
  767. (tstaticvarsym(sym).vardef=tdef(sym.owner.defowner)) then
  768. result:=result+'enum ';
  769. result:=result+jvmmangledbasename(sym,true);
  770. end;
  771. function TJasminAssembler.InnerStructDef(obj: tabstractrecorddef): ansistring;
  772. var
  773. extname: pshortstring;
  774. kindname: ansistring;
  775. begin
  776. if not(obj.owner.defowner.typ in [objectdef,recorddef]) then
  777. internalerror(2011021704);
  778. { Nested classes in the Pascal sense are equivalent to "static"
  779. inner classes in Java -- will be changed when support for
  780. Java-style non-static classes is added }
  781. case obj.typ of
  782. recorddef:
  783. begin
  784. kindname:='class static ';
  785. extname:=obj.symtable.realname;
  786. end;
  787. objectdef:
  788. begin
  789. extname:=tobjectdef(obj).objextname;
  790. case tobjectdef(obj).objecttype of
  791. odt_javaclass:
  792. kindname:='class static ';
  793. odt_interfacejava:
  794. kindname:='interface static abstract ';
  795. else
  796. internalerror(2011021702);
  797. end;
  798. end;
  799. else
  800. internalerror(2011032809);
  801. end;
  802. result:=
  803. '.inner '+
  804. kindname+
  805. VisibilityToStr(obj.typesym.visibility)+
  806. extname^+
  807. ' inner '+
  808. obj.jvm_full_typename(true)+
  809. ' outer '+
  810. tabstractrecorddef(obj.owner.defowner).jvm_full_typename(true);
  811. end;
  812. procedure TJasminAssembler.WriteProcDef(pd: tprocdef);
  813. begin
  814. if not assigned(tcpuprocdef(pd).exprasmlist) and
  815. not(po_abstractmethod in pd.procoptions) and
  816. (not is_javainterface(pd.struct) or
  817. (pd.proctypeoption in [potype_unitinit,potype_unitfinalize])) then
  818. exit;
  819. writer.AsmWrite('.method ');
  820. writer.AsmWriteln(MethodDefinition(pd));
  821. if jvmtypeneedssignature(pd) then
  822. begin
  823. writer.AsmWrite('.signature "');
  824. writer.AsmWrite(tcpuprocdef(pd).jvmmangledbasename(true));
  825. writer.AsmWriteln('"');
  826. end;
  827. WriteTree(tcpuprocdef(pd).exprasmlist);
  828. writer.AsmWriteln('.end method');
  829. writer.AsmLn;
  830. end;
  831. procedure TJasminAssembler.WriteFieldSym(sym: tabstractvarsym);
  832. begin
  833. { internal static field definition alias -> skip }
  834. if (sym.owner.symtabletype in [recordsymtable,ObjectSymtable]) and
  835. (sym.typ=staticvarsym) then
  836. exit;
  837. { external or threadvar definition -> no definition here }
  838. if ([vo_is_external,vo_is_thread_var]*sym.varoptions)<>[] then
  839. exit;
  840. writer.AsmWrite('.field ');
  841. writer.AsmWriteln(FieldDefinition(sym));
  842. end;
  843. procedure TJasminAssembler.WriteConstSym(sym: tconstsym);
  844. begin
  845. writer.AsmWrite('.field ');
  846. writer.AsmWriteln(ConstDefinition(sym));
  847. end;
  848. procedure TJasminAssembler.WriteSymtableVarSyms(st: TSymtable);
  849. var
  850. sym : tsym;
  851. i,j : longint;
  852. begin
  853. if not assigned(st) then
  854. exit;
  855. for i:=0 to st.SymList.Count-1 do
  856. begin
  857. sym:=tsym(st.SymList[i]);
  858. case sym.typ of
  859. staticvarsym,
  860. fieldvarsym:
  861. begin
  862. WriteFieldSym(tabstractvarsym(sym));
  863. if (sym.typ=staticvarsym) and
  864. assigned(tstaticvarsym(sym).defaultconstsym) then
  865. WriteFieldSym(tabstractvarsym(tstaticvarsym(sym).defaultconstsym));
  866. end;
  867. constsym:
  868. begin
  869. { multiple procedures can have constants with the same name }
  870. if not assigned(sym.owner.defowner) or
  871. (tdef(sym.owner.defowner).typ<>procdef) then
  872. WriteConstSym(tconstsym(sym));
  873. end;
  874. procsym:
  875. begin
  876. for j:=0 to tprocsym(sym).procdeflist.count-1 do
  877. if not(df_generic in tprocdef(tprocsym(sym).procdeflist[j]).defoptions) then
  878. WriteSymtableVarSyms(tprocdef(tprocsym(sym).procdeflist[j]).localst);
  879. end;
  880. else
  881. ;
  882. end;
  883. end;
  884. end;
  885. procedure TJasminAssembler.WriteSymtableProcdefs(st: TSymtable);
  886. var
  887. i : longint;
  888. def : tdef;
  889. begin
  890. if not assigned(st) then
  891. exit;
  892. for i:=0 to st.DefList.Count-1 do
  893. begin
  894. def:=tdef(st.DefList[i]);
  895. case def.typ of
  896. procdef :
  897. begin
  898. { methods are also in the static/globalsymtable of the unit
  899. -> make sure they are only written for the objectdefs that
  900. own them }
  901. if (not(st.symtabletype in [staticsymtable,globalsymtable]) or
  902. (def.owner=st)) and
  903. not(df_generic in def.defoptions) then
  904. begin
  905. WriteProcDef(tprocdef(def));
  906. if assigned(tprocdef(def).localst) then
  907. WriteSymtableProcdefs(tprocdef(def).localst);
  908. end;
  909. end;
  910. else
  911. ;
  912. end;
  913. end;
  914. end;
  915. procedure TJasminAssembler.WriteSymtableStructDefs(st: TSymtable);
  916. var
  917. i : longint;
  918. def : tdef;
  919. obj : tabstractrecorddef;
  920. nestedstructs: tfpobjectlist;
  921. begin
  922. if not assigned(st) then
  923. exit;
  924. nestedstructs:=tfpobjectlist.create(false);
  925. for i:=0 to st.DefList.Count-1 do
  926. begin
  927. def:=tdef(st.DefList[i]);
  928. if df_generic in def.defoptions then
  929. continue;
  930. case def.typ of
  931. objectdef:
  932. if not(oo_is_external in tobjectdef(def).objectoptions) then
  933. nestedstructs.add(def);
  934. recorddef:
  935. nestedstructs.add(def);
  936. else
  937. ;
  938. end;
  939. end;
  940. for i:=0 to nestedstructs.count-1 do
  941. begin
  942. obj:=tabstractrecorddef(nestedstructs[i]);
  943. NewAsmFileForStructDef(obj);
  944. WriteExtraHeader(obj);
  945. WriteSymtableVarSyms(obj.symtable);
  946. writer.AsmLn;
  947. WriteSymtableProcDefs(obj.symtable);
  948. WriteSymtableStructDefs(obj.symtable);
  949. end;
  950. nestedstructs.free;
  951. end;
  952. function TJasminAssembler.CreateNewAsmWriter: TExternalAssemblerOutputFile;
  953. begin
  954. Result:=TJasminAssemblerOutputFile.Create(self);
  955. end;
  956. constructor TJasminAssembler.CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean);
  957. begin
  958. inherited;
  959. InstrWriter:=TJasminInstrWriter.Create(self);
  960. asmfiles:=TCmdStrList.Create;
  961. end;
  962. procedure TJasminAssembler.WriteAsmList;
  963. begin
  964. { the code for Java methods needs to be emitted class per class,
  965. so instead of iterating over all asmlists, we iterate over all types
  966. and global variables (a unit becomes a class, with its global
  967. variables static fields) }
  968. writer.MarkEmpty;
  969. WriteExtraHeader(nil);
  970. { print all global variables }
  971. WriteSymtableVarSyms(current_module.globalsymtable);
  972. WriteSymtableVarSyms(current_module.localsymtable);
  973. writer.AsmLn;
  974. { print all global procedures/functions }
  975. WriteSymtableProcdefs(current_module.globalsymtable);
  976. WriteSymtableProcdefs(current_module.localsymtable);
  977. WriteSymtableStructDefs(current_module.globalsymtable);
  978. WriteSymtableStructDefs(current_module.localsymtable);
  979. writer.AsmLn;
  980. end;
  981. {****************************************************************************}
  982. { Jasmin Instruction Writer }
  983. {****************************************************************************}
  984. constructor TJasminInstrWriter.create(_owner: TJasminAssembler);
  985. begin
  986. inherited create;
  987. owner := _owner;
  988. end;
  989. function getreferencestring(var ref : treference) : ansistring;
  990. begin
  991. if (ref.arrayreftype<>art_none) or
  992. (ref.index<>NR_NO) then
  993. internalerror(2010122809);
  994. if assigned(ref.symbol) then
  995. begin
  996. // global symbol or field -> full type and name
  997. // ref.base can be <> NR_NO in case an instance field is loaded.
  998. // This register is not part of this instruction, it will have
  999. // been placed on the stack by the previous one.
  1000. if (ref.offset<>0) then
  1001. internalerror(2010122811);
  1002. result:=ref.symbol.name;
  1003. end
  1004. else
  1005. begin
  1006. // local symbol -> stack slot, stored in offset
  1007. if ref.base<>NR_STACK_POINTER_REG then
  1008. internalerror(2010122810);
  1009. result:=tostr(ref.offset);
  1010. end;
  1011. end;
  1012. function getopstr(const o:toper) : ansistring;
  1013. begin
  1014. case o.typ of
  1015. top_reg:
  1016. // should have been translated into a memory location by the
  1017. // register allocator)
  1018. if (cs_no_regalloc in current_settings.globalswitches) then
  1019. getopstr:=std_regname(o.reg)
  1020. else
  1021. internalerror(2010122803);
  1022. top_const:
  1023. str(o.val,result);
  1024. top_ref:
  1025. getopstr:=getreferencestring(o.ref^);
  1026. top_single:
  1027. begin
  1028. result:=constsingle(o.sval);
  1029. end;
  1030. top_double:
  1031. begin
  1032. result:=constdouble(o.dval);
  1033. end;
  1034. top_string:
  1035. begin
  1036. result:=constastr(o.pcval,o.pcvallen);
  1037. end;
  1038. top_wstring:
  1039. begin
  1040. result:=constwstr(o.pwstrval^.data,getlengthwidestring(o.pwstrval));
  1041. end
  1042. else
  1043. internalerror(2010122802);
  1044. end;
  1045. end;
  1046. procedure TJasminInstrWriter.WriteInstruction(hp: tai);
  1047. var
  1048. s: ansistring;
  1049. i: byte;
  1050. sep: ansistring;
  1051. begin
  1052. s:=#9+jas_op2str[taicpu(hp).opcode];
  1053. if taicpu(hp).ops<>0 then
  1054. begin
  1055. sep:=#9;
  1056. for i:=0 to taicpu(hp).ops-1 do
  1057. begin
  1058. s:=s+sep+getopstr(taicpu(hp).oper[i]^);
  1059. sep:=' ';
  1060. end;
  1061. end;
  1062. owner.writer.AsmWriteLn(s);
  1063. end;
  1064. {****************************************************************************}
  1065. { Jasmin Instruction Writer }
  1066. {****************************************************************************}
  1067. const
  1068. as_jvm_jasmin_info : tasminfo =
  1069. (
  1070. id : as_jvm_jasmin;
  1071. idtxt : 'Jasmin';
  1072. asmbin : 'java';
  1073. asmcmd : '-jar $JASMINJAR $ASM $EXTRAOPT -d $OBJDIR';
  1074. supported_targets : [system_jvm_java32,system_jvm_android32];
  1075. flags : [];
  1076. labelprefix : 'L';
  1077. labelmaxlen : -1;
  1078. comment : ' ; ';
  1079. dollarsign : '$';
  1080. );
  1081. begin
  1082. RegisterAssembler(as_jvm_jasmin_info,TJasminAssembler);
  1083. end.