PageRenderTime 60ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/src/sys/dotnet/fanx/emit/FCodeEmit.cs

https://bitbucket.org/bedlaczech/fan-1.0
C# | 1274 lines | 939 code | 160 blank | 175 comment | 125 complexity | fbebb650429d32ee9a8d0a9212359f55 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. //
  2. // Copyright (c) 2006, Brian Frank and Andy Frank
  3. // Licensed under the Academic Free License version 3.0
  4. //
  5. // History:
  6. // 10 Oct 06 Andy Frank Creation
  7. //
  8. // TODO
  9. // Boolean primitves: 0f8c8ecc9484
  10. // Convert sys to bool: e7279e569e98
  11. using System;
  12. using System.Collections;
  13. using System.Text;
  14. using PERWAPI;
  15. using Fanx.Fcode;
  16. using Fanx.Util;
  17. namespace Fanx.Emit
  18. {
  19. /// <summary>
  20. /// FCodeEmit translates FCode fcode to IL bytecode.
  21. /// </summary>
  22. public class FCodeEmit
  23. {
  24. //////////////////////////////////////////////////////////////////////////
  25. // Constructor
  26. //////////////////////////////////////////////////////////////////////////
  27. public FCodeEmit(FTypeEmit parent, FMethod fmethod, CILInstructions code)
  28. : this(parent, fmethod.m_code, code,
  29. initRegs(parent.pod, fmethod.isStatic(), fmethod.m_vars),
  30. parent.pod.typeRef(fmethod.m_ret))
  31. {
  32. this.fmethod = fmethod;
  33. this.vars = fmethod.m_vars;
  34. this.isStatic = (fmethod.m_flags & FConst.Static) != 0;
  35. this.paramCount = fmethod.m_paramCount;
  36. if (!isStatic) paramCount++;
  37. }
  38. public FCodeEmit(FTypeEmit parent, FBuf fcode, CILInstructions code, Reg[] regs, FTypeRef ret)
  39. {
  40. this.pod = parent.pod;
  41. this.emitter = parent.emitter;
  42. this.parent = parent;
  43. this.buf = fcode.m_buf;
  44. this.len = fcode.m_len;
  45. this.code = code;
  46. this.podClass = FanUtil.toDotnetTypeName(pod.m_podName, "$Pod", false);
  47. this.jumps = new Jumps(code);
  48. this.regs = regs;
  49. this.ret = ret;
  50. }
  51. //////////////////////////////////////////////////////////////////////////
  52. // Overrides
  53. //////////////////////////////////////////////////////////////////////////
  54. /// <summary>
  55. /// Translate fcode to IL.
  56. /// </summary>
  57. // TODO - this is all fucked up - need to back and fix
  58. // so that this is not an atomic operation - need to be
  59. // able to tack on additional IL after its 'closed' -
  60. // correct scoping falls out of that fix as well I think.
  61. public void emit() { emit(true); }
  62. public void emit(bool debug)
  63. {
  64. if (debug) code.OpenScope();
  65. makeLineNumbers();
  66. makeErrTable();
  67. emitInstructions();
  68. if (debug) code.CloseScope();
  69. }
  70. /// <summary>
  71. /// Map fcode instructions to IL instructions.
  72. /// </summary>
  73. private void emitInstructions()
  74. {
  75. while (pos < len)
  76. {
  77. startPos = pos;
  78. int opcode = consumeOp();
  79. switch (opcode)
  80. {
  81. case FConst.Nop: break;
  82. case FConst.LoadNull: code.Inst(Op.ldnull); break;
  83. case FConst.LoadFalse: loadFalse(); break;
  84. case FConst.LoadTrue: loadTrue(); break;
  85. case FConst.LoadInt: loadInt(); break;
  86. case FConst.LoadFloat: loadFloat(); break;
  87. case FConst.LoadDecimal: loadDecimal(); break;
  88. case FConst.LoadStr: loadStr(); break;
  89. case FConst.LoadDuration: loadDuration(); break;
  90. case FConst.LoadUri: loadUri(); break;
  91. case FConst.LoadType: loadType(); break;
  92. case FConst.LoadVar: loadVar(); break;
  93. case FConst.StoreVar: storeVar(); break;
  94. case FConst.LoadInstance: loadInstance(); break;
  95. case FConst.StoreInstance: storeInstance(); break;
  96. case FConst.LoadStatic: loadStatic(); break;
  97. case FConst.StoreStatic: storeStatic(); break;
  98. case FConst.LoadMixinStatic: loadMixinStatic(); break;
  99. case FConst.StoreMixinStatic: storeMixinStatic(); break;
  100. case FConst.CallNew: callNew(); break;
  101. case FConst.CallCtor: callCtor(); break;
  102. case FConst.CallStatic: callStatic(); break;
  103. case FConst.CallVirtual: callVirtual(); break;
  104. case FConst.CallNonVirtual: callNonVirtual(); break;
  105. case FConst.CallMixinStatic: callMixinStatic(); break;
  106. case FConst.CallMixinVirtual: callMixinVirtual(); break;
  107. case FConst.CallMixinNonVirtual: callMixinNonVirtual(); break;
  108. case FConst.Jump: jump(); break;
  109. case FConst.JumpTrue: jumpTrue(); break;
  110. case FConst.JumpFalse: jumpFalse(); break;
  111. case FConst.CompareEQ: compareEQ(); break;
  112. case FConst.CompareNE: compareNE(); break;
  113. case FConst.Compare: compare(); break;
  114. case FConst.CompareLT: compareLT(); break;
  115. case FConst.CompareLE: compareLE(); break;
  116. case FConst.CompareGE: compareGE(); break;
  117. case FConst.CompareGT: compareGT(); break;
  118. case FConst.CompareSame: compareSame(); break;
  119. case FConst.CompareNotSame: compareNotSame(); break;
  120. case FConst.CompareNull: compareNull(); break;
  121. case FConst.CompareNotNull: compareNotNull(); break;
  122. case FConst.Return: returnOp(); break;
  123. case FConst.Pop: pop(); break;
  124. case FConst.Dup: dup(); break;
  125. case FConst.Is: @is(); break;
  126. case FConst.As: @as(); break;
  127. case FConst.Coerce: coerce(); break;
  128. case FConst.Switch: tableswitch(); break;
  129. case FConst.Throw: doThrow(); break;
  130. case FConst.Leave: doLeave(); break;
  131. case FConst.JumpFinally: u2(); break; // not used in .NET, so just eat the branch loc
  132. case FConst.CatchAllStart: catchAllStart(); break;
  133. case FConst.CatchErrStart: catchErrStart(); break;
  134. case FConst.CatchEnd: catchEnd(); break;
  135. case FConst.FinallyStart: finallyStart(); break;
  136. case FConst.FinallyEnd: finallyEnd(); break;
  137. default: throw new Exception(opcode < FConst.OpNames.Length ? FConst.OpNames[opcode] : "bad opcode=" + opcode);
  138. }
  139. }
  140. }
  141. /// <summary>
  142. /// Read out the line numbers and stick them in a hashmap.
  143. /// </summary>
  144. private void makeLineNumbers()
  145. {
  146. // source file
  147. string srcFile = parent.type.m_attrs.m_sourceFile;
  148. if (srcFile != null)
  149. {
  150. code.DefaultSourceFile = SourceFile.GetSourceFile(
  151. srcFile, // source file
  152. System.Guid.Empty, // lang
  153. System.Guid.Empty, // vend
  154. System.Guid.Empty); // docu
  155. }
  156. // line numbers
  157. if (fmethod == null) return;
  158. FBuf flines = fmethod.m_attrs.m_lineNums;
  159. if (flines != null)
  160. {
  161. int len = flines.m_len;
  162. byte[] buf = flines.m_buf;
  163. for (int i=2; i<len; i+=4)
  164. {
  165. int pc = (buf[i] & 0xFF) << 8 | (buf[i+1] & 0xFF);
  166. int line = (buf[i+2] & 0xFF) << 8 | (buf[i+3] & 0xFF);
  167. lineNums[pc] = line;
  168. }
  169. }
  170. }
  171. /// <summary>
  172. /// Process error table (if specified). We handle catches of Err using
  173. /// a catch any (0 class index). We also need to add extra entries into
  174. /// the exception table for special exceptions - for example NullErr get's
  175. /// mapped as fan.sys.NullErr+Val and java.lang.NullPointerException.
  176. /// </summary>
  177. private void makeErrTable()
  178. {
  179. if (fmethod == null) return;
  180. FBuf ferrs = fmethod.m_attrs.m_errTable;
  181. if (ferrs == null) return;
  182. int len = ferrs.m_len;
  183. byte[] buf = ferrs.m_buf;
  184. int count = (len-2)/8;
  185. tryStart = new int[count];
  186. tryEnd = new int[count];
  187. tryJump = new int[count];
  188. tryErr = new int[count];
  189. for (int i=2, j=0; i<len; i+=8)
  190. {
  191. tryStart[j] = (buf[i+0] & 0xFF) << 8 | (buf[i+1] & 0xFF);
  192. tryEnd[j] = (buf[i+2] & 0xFF) << 8 | (buf[i+3] & 0xFF);
  193. tryJump[j] = (buf[i+4] & 0xFF) << 8 | (buf[i+5] & 0xFF);
  194. tryErr[j] = (buf[i+6] & 0xFF) << 8 | (buf[i+7] & 0xFF);
  195. j++;
  196. }
  197. }
  198. //////////////////////////////////////////////////////////////////////////
  199. // Load/Store
  200. //////////////////////////////////////////////////////////////////////////
  201. private void loadFalse()
  202. {
  203. code.IntInst(IntOp.ldc_i4, 0);
  204. }
  205. private void loadTrue()
  206. {
  207. code.IntInst(IntOp.ldc_i4, 1);
  208. }
  209. private void loadInt()
  210. {
  211. int index = u2();
  212. PERWAPI.Field field = emitter.findField(podClass, "I" + index, "System.Int64");
  213. code.FieldInst(FieldOp.ldsfld, field);
  214. }
  215. private void loadFloat()
  216. {
  217. int index = u2();
  218. PERWAPI.Field field = emitter.findField(podClass, "F" + index, "System.Double");
  219. code.FieldInst(FieldOp.ldsfld, field);
  220. }
  221. private void loadDecimal()
  222. {
  223. int index = u2();
  224. PERWAPI.Field field = emitter.findField(podClass, "D" + index, "Fan.Sys.BigDecimal");
  225. code.FieldInst(FieldOp.ldsfld, field);
  226. }
  227. private void loadStr()
  228. {
  229. int index = u2();
  230. PERWAPI.Field field = emitter.findField(podClass, "S" + index, "System.String");
  231. code.FieldInst(FieldOp.ldsfld, field);
  232. }
  233. private void loadDuration()
  234. {
  235. int index = u2();
  236. PERWAPI.Field field = emitter.findField(podClass, "Dur" + index, "Fan.Sys.Duration");
  237. code.FieldInst(FieldOp.ldsfld, field);
  238. }
  239. private void loadUri()
  240. {
  241. int index = u2();
  242. PERWAPI.Field field = emitter.findField(podClass, "U" + index, "Fan.Sys.Uri");
  243. code.FieldInst(FieldOp.ldsfld, field);
  244. }
  245. private void loadType()
  246. {
  247. loadType(pod.typeRef(u2()));
  248. }
  249. private void loadType(FTypeRef tref)
  250. {
  251. string podName = tref.podName;
  252. string typeName = tref.typeName;
  253. // if pod is "sys", then we can perform a shortcut and use
  254. // one of the predefined fields in Sys
  255. if (!tref.isGenericInstance() && podName == "sys")
  256. {
  257. PERWAPI.Field field = emitter.findField("Fan.Sys.Sys", typeName + "Type", "Fan.Sys.Type");
  258. code.FieldInst(FieldOp.ldsfld, field);
  259. if (tref.isNullable()) typeToNullable();
  260. return;
  261. }
  262. // lazy allocate my parent's type literal map: sig -> fieldName
  263. if (parent.typeLiteralFields == null) parent.typeLiteralFields = new Hashtable();
  264. Hashtable map = parent.typeLiteralFields;
  265. // types are lazy loaded and then cached in a private static field called
  266. // type$literal$count which will get generated by FTypeEmit (we keep track of signature
  267. // to fieldname in the typeConstFields map)
  268. string sig = tref.signature;
  269. string fieldName = (string)map[sig];
  270. if (fieldName == null)
  271. {
  272. fieldName = "type$literal$" + map.Count;
  273. map[sig] = fieldName;
  274. }
  275. //int fieldRef = emit.field(parent.className + "." + fieldName + ":Lfan/sys/Type;");
  276. //code.op2(GETSTATIC, fieldRef);
  277. //code.op(DUP);
  278. //int nonNull = code.branch(IFNONNULL);
  279. //code.op(POP);
  280. //code.op2(LDC_W, emit.strConst(sig));
  281. //code.op(ICONST_1);
  282. //code.op2(INVOKESTATIC, parent.sysFindType());
  283. //code.op(DUP);
  284. //code.op2(PUTSTATIC, fieldRef);
  285. //code.mark(nonNull);
  286. //emitter.EmitField(string name, string type, FieldAttr attr)
  287. //PERWAPI.Field field = emitter.EmitField("Fan.Sys.Sys", typeName + "Type", "Fan.Sys.Type");
  288. //code.FieldInst(FieldOp.ldsfld, field);
  289. // TODO - store in static field (all that crap above this)
  290. code.ldstr(sig);
  291. Method method = emitter.findMethod("Fan.Sys.Type", "find",
  292. new string[] { "System.String" }, "Fan.Sys.Type");
  293. code.MethInst(MethodOp.call, method);
  294. }
  295. //////////////////////////////////////////////////////////////////////////
  296. // Load Var
  297. //////////////////////////////////////////////////////////////////////////
  298. private void loadVar()
  299. {
  300. Reg reg = this.reg(u2());
  301. loadVar(code, reg.stackType, reg.nindex, paramCount);
  302. }
  303. /// <summary>
  304. /// Load variable onto stack using Java type and java index (which might
  305. /// not map to Fantom index. Return next available java index
  306. /// </summary>
  307. internal static void loadVar(CILInstructions code, int stackType, int index)
  308. {
  309. loadVar(code, stackType, index, Int32.MaxValue);
  310. }
  311. private static void loadVar(CILInstructions code, int stackType, int index, int paramCount)
  312. {
  313. if (index < paramCount)
  314. {
  315. switch (index)
  316. {
  317. case 0: code.Inst(Op.ldarg_0); break;
  318. case 1: code.Inst(Op.ldarg_1); break;
  319. case 2: code.Inst(Op.ldarg_2); break;
  320. case 3: code.Inst(Op.ldarg_3); break;
  321. default: code.IntInst(IntOp.ldarg, index); break;
  322. }
  323. }
  324. else
  325. {
  326. index -= paramCount;
  327. switch (index)
  328. {
  329. case 0: code.Inst(Op.ldloc_0); break;
  330. case 1: code.Inst(Op.ldloc_1); break;
  331. case 2: code.Inst(Op.ldloc_2); break;
  332. case 3: code.Inst(Op.ldloc_3); break;
  333. default: code.IntInst(IntOp.ldloc, index); break;
  334. }
  335. }
  336. }
  337. //////////////////////////////////////////////////////////////////////////
  338. // Store Var
  339. //////////////////////////////////////////////////////////////////////////
  340. private void storeVar()
  341. {
  342. Reg reg = this.reg(u2());
  343. storeVar(reg.stackType, reg.nindex);
  344. }
  345. private void storeVar(int stackType, int index)
  346. {
  347. if (index < paramCount)
  348. {
  349. code.IntInst(IntOp.starg, index);
  350. }
  351. else
  352. {
  353. index -= paramCount;
  354. switch (index)
  355. {
  356. case 0: code.Inst(Op.stloc_0); break;
  357. case 1: code.Inst(Op.stloc_1); break;
  358. case 2: code.Inst(Op.stloc_2); break;
  359. case 3: code.Inst(Op.stloc_3); break;
  360. default: code.IntInst(IntOp.stloc, index); break;
  361. }
  362. }
  363. }
  364. //////////////////////////////////////////////////////////////////////////
  365. // Field
  366. //////////////////////////////////////////////////////////////////////////
  367. private void loadInstance()
  368. {
  369. FPod.NField f = pod.nfield(u2(), false);
  370. PERWAPI.Field field = emitter.findField(f.parentType, f.fieldName, f.fieldType);
  371. code.FieldInst(FieldOp.ldfld, field);
  372. }
  373. private void storeInstance()
  374. {
  375. FPod.NField f = pod.nfield(u2(), false);
  376. PERWAPI.Field field = emitter.findField(f.parentType, f.fieldName, f.fieldType);
  377. code.FieldInst(FieldOp.stfld, field);
  378. }
  379. private void loadStatic()
  380. {
  381. FPod.NField f = pod.nfield(u2(), false);
  382. PERWAPI.Field field = emitter.findField(f.parentType, f.fieldName, f.fieldType);
  383. code.FieldInst(FieldOp.ldsfld, field);
  384. }
  385. private void storeStatic()
  386. {
  387. FPod.NField f = pod.nfield(u2(), false);
  388. PERWAPI.Field field = emitter.findField(f.parentType, f.fieldName, f.fieldType);
  389. code.FieldInst(FieldOp.stsfld, field);
  390. }
  391. private void loadMixinStatic()
  392. {
  393. // mixin fields route to implementation class
  394. FPod.NField f = pod.nfield(u2(), false);
  395. PERWAPI.Field field = emitter.findField(f.parentType+"_", f.fieldName, f.fieldType);
  396. code.FieldInst(FieldOp.ldsfld, field);
  397. }
  398. private void storeMixinStatic()
  399. {
  400. // mixin fields route to implementation class
  401. FPod.NField f = pod.nfield(u2(), false);
  402. PERWAPI.Field field = emitter.findField(f.parentType+"_", f.fieldName, f.fieldType);
  403. code.FieldInst(FieldOp.stsfld, field);
  404. }
  405. //////////////////////////////////////////////////////////////////////////
  406. // Calls
  407. //////////////////////////////////////////////////////////////////////////
  408. private void callNew()
  409. {
  410. // constructors are implemented as static factory methods
  411. FPod.NMethod ncall = pod.ncall(u2(), FConst.CallNew);
  412. Method method = emitter.findMethod(ncall.parentType, ncall.methodName,
  413. ncall.paramTypes, ncall.returnType);
  414. code.MethInst(MethodOp.call, method);
  415. }
  416. private void callCtor()
  417. {
  418. // constructor implementations (without object allow) are
  419. // implemented as static factory methods with "_" appended
  420. int index = u2();
  421. int[] m = pod.methodRef(index).val;
  422. string parent = pod.typeRef(m[0]).nname();
  423. string name = pod.name(m[1]) + "_";
  424. string[] pars = new string[m.Length-3+1];
  425. pars[0] = parent;
  426. for (int i=0; i<pars.Length-1; i++)
  427. pars[i+1] = pod.typeRef(m[i+3]).nname();
  428. Method method = emitter.findMethod(parent, name, pars, "System.Void");
  429. code.MethInst(MethodOp.call, method);
  430. }
  431. private void callStatic()
  432. {
  433. FPod.NMethod ncall = pod.ncall(u2(), FConst.CallStatic);
  434. Method method = emitter.findMethod(ncall.parentType, ncall.methodName,
  435. ncall.paramTypes, ncall.returnType);
  436. code.MethInst(MethodOp.call, method);
  437. }
  438. private void callVirtual()
  439. {
  440. int index = u2();
  441. FPod.NMethod ncall = pod.ncall(index, FConst.CallVirtual);
  442. Method method = emitter.findMethod(ncall.parentType, ncall.methodName,
  443. ncall.paramTypes, ncall.returnType);
  444. if (ncall.isStatic)
  445. {
  446. code.MethInst(MethodOp.call, method);
  447. }
  448. else
  449. {
  450. method.AddCallConv(CallConv.Instance);
  451. code.MethInst(MethodOp.callvirt, method);
  452. }
  453. }
  454. private void callNonVirtual()
  455. {
  456. FPod.NMethod ncall = pod.ncall(u2(), FConst.CallNonVirtual);
  457. Method method = emitter.findMethod(ncall.parentType, ncall.methodName,
  458. ncall.paramTypes, ncall.returnType);
  459. method.AddCallConv(CallConv.Instance);
  460. code.MethInst(MethodOp.call, method);
  461. }
  462. private void callMixinStatic()
  463. {
  464. FPod.NMethod ncall = pod.ncall(u2(), FConst.CallMixinStatic);
  465. Method method = emitter.findMethod(ncall.parentType, ncall.methodName,
  466. ncall.paramTypes, ncall.returnType);
  467. code.MethInst(MethodOp.call, method);
  468. }
  469. private void callMixinVirtual()
  470. {
  471. FPod.NMethod ncall = pod.ncall(u2(), FConst.CallMixinVirtual);
  472. Method method = emitter.findMethod(ncall.parentType, ncall.methodName,
  473. ncall.paramTypes, ncall.returnType);
  474. method.AddCallConv(CallConv.Instance);
  475. code.MethInst(MethodOp.callvirt, method);
  476. }
  477. private void callMixinNonVirtual()
  478. {
  479. FPod.NMethod ncall = pod.ncall(u2(), FConst.CallMixinNonVirtual);
  480. string parent = ncall.parentType;
  481. string name = ncall.methodName;
  482. string ret = ncall.returnType;
  483. string[] pars = new string[ncall.paramTypes.Length+1];
  484. pars[0] = parent;
  485. for (int i=1; i<pars.Length; i++)
  486. pars[i] = ncall.paramTypes[i-1];
  487. Method method = emitter.findMethod(parent+"_", name, pars, ret);
  488. code.MethInst(MethodOp.call, method);
  489. }
  490. //////////////////////////////////////////////////////////////////////////
  491. // Jump
  492. //////////////////////////////////////////////////////////////////////////
  493. private void jumpTrue()
  494. {
  495. code.Branch(BranchOp.brtrue, jumps.add(u2()));
  496. }
  497. private void jumpFalse()
  498. {
  499. code.Branch(BranchOp.brfalse, jumps.add(u2()));
  500. }
  501. private void jump()
  502. {
  503. code.Branch(BranchOp.br, jumps.add(u2()));
  504. }
  505. //////////////////////////////////////////////////////////////////////////
  506. // Compare
  507. //////////////////////////////////////////////////////////////////////////
  508. private void compareEQ()
  509. {
  510. FTypeRef lhs = pod.typeRef(u2());
  511. FTypeRef rhs = pod.typeRef(u2());
  512. // if this is a.equals(b) and we know a is non-null, then just call equals
  513. if (lhs.isRef() && !lhs.isNullable() && rhs.isRef())
  514. {
  515. PERWAPI.Method m = emitter.findMethod("System.Object", "Equals",
  516. new string[] { "System.Object" }, "System.Boolean");
  517. m.AddCallConv(CallConv.Instance);
  518. code.MethInst(MethodOp.callvirt, m);
  519. return;
  520. }
  521. doCompare("EQ", lhs, rhs);
  522. }
  523. private void compareNE() { doCompare("NE"); }
  524. private void compareLT() { doCompare("LT"); }
  525. private void compareLE() { doCompare("LE"); }
  526. private void compareGE() { doCompare("GE"); }
  527. private void compareGT() { doCompare("GT"); }
  528. private void compare() { doCompare(""); }
  529. private void doCompare(string suffix)
  530. {
  531. doCompare(suffix, pod.typeRef(u2()), pod.typeRef(u2()));
  532. }
  533. private void doCompare(string suffix, FTypeRef lhs, FTypeRef rhs)
  534. {
  535. // get lhs and rhs types
  536. string[] args = new string[]
  537. {
  538. lhs.isRef() ? "System.Object" : lhs.nname(),
  539. rhs.isRef() ? "System.Object" : rhs.nname()
  540. };
  541. string ret = (suffix == "") ? "System.Int64" : "System.Boolean";
  542. PERWAPI.Method m = emitter.findMethod("Fanx.Util.OpUtil", "compare"+suffix, args, ret);
  543. code.MethInst(MethodOp.call, m);
  544. }
  545. private void compareSame()
  546. {
  547. int peek = peekOp();
  548. switch (peek)
  549. {
  550. case FConst.JumpFalse:
  551. consumeOp();
  552. code.Branch(BranchOp.bne_un, jumps.add(u2()));
  553. break;
  554. case FConst.JumpTrue:
  555. consumeOp();
  556. code.Branch(BranchOp.beq, jumps.add(u2()));
  557. break;
  558. default:
  559. if (parent.CompareSame == null)
  560. parent.CompareSame = emitter.findMethod("Fanx.Util.OpUtil", "compareSame",
  561. new string[] { "System.Object", "System.Object" }, "System.Boolean");
  562. code.MethInst(MethodOp.call, parent.CompareSame);
  563. break;
  564. }
  565. }
  566. private void compareNotSame()
  567. {
  568. int peek = peekOp();
  569. switch (peek)
  570. {
  571. case FConst.JumpFalse:
  572. consumeOp();
  573. code.Branch(BranchOp.beq, jumps.add(u2()));
  574. break;
  575. case FConst.JumpTrue:
  576. consumeOp();
  577. code.Branch(BranchOp.bne_un, jumps.add(u2()));
  578. break;
  579. default:
  580. if (parent.CompareNotSame == null)
  581. parent.CompareNotSame = emitter.findMethod("Fanx.Util.OpUtil", "compareNotSame",
  582. new string[] { "System.Object", "System.Object" }, "System.Boolean");
  583. code.MethInst(MethodOp.call, parent.CompareNotSame);
  584. break;
  585. }
  586. }
  587. private void compareNull()
  588. {
  589. u2(); // ignore type
  590. int peek = peekOp();
  591. switch (peek)
  592. {
  593. case FConst.JumpFalse:
  594. consumeOp();
  595. code.Branch(BranchOp.brtrue, jumps.add(u2()));
  596. break;
  597. case FConst.JumpTrue:
  598. consumeOp();
  599. code.Branch(BranchOp.brfalse, jumps.add(u2()));
  600. break;
  601. default:
  602. if (parent.CompareNull == null)
  603. parent.CompareNull = emitter.findMethod("Fanx.Util.OpUtil", "compareNull",
  604. new string[] { "System.Object" }, "System.Boolean");
  605. code.MethInst(MethodOp.call, parent.CompareNull);
  606. break;
  607. }
  608. }
  609. private void compareNotNull()
  610. {
  611. u2(); // ignore type
  612. int peek = peekOp();
  613. switch (peek)
  614. {
  615. case FConst.JumpFalse:
  616. consumeOp();
  617. code.Branch(BranchOp.brfalse, jumps.add(u2()));
  618. break;
  619. case FConst.JumpTrue:
  620. consumeOp();
  621. code.Branch(BranchOp.brtrue, jumps.add(u2()));
  622. break;
  623. default:
  624. if (parent.CompareNotNull == null)
  625. parent.CompareNotNull = emitter.findMethod("Fanx.Util.OpUtil", "compareNotNull",
  626. new string[] { "System.Object" }, "System.Boolean");
  627. code.MethInst(MethodOp.call, parent.CompareNotNull);
  628. break;
  629. }
  630. }
  631. //////////////////////////////////////////////////////////////////////////
  632. // Stack Manipulation
  633. //////////////////////////////////////////////////////////////////////////
  634. private void returnOp()
  635. {
  636. code.Inst(Op.ret);
  637. }
  638. /*
  639. static int returnOp(FTypeRef ret) { return returnOp(ret.stackType); }
  640. static int returnOp(int retStackType)
  641. {
  642. switch (retStackType)
  643. {
  644. case 'A': return ARETURN;
  645. case 'D': return DRETURN;
  646. case 'I': return IRETURN;
  647. case 'J': return LRETURN;
  648. case 'V': return RETURN;
  649. case 'Z': return IRETURN;
  650. default: throw new IllegalStateException(""+(char)retStackType);
  651. }
  652. }
  653. */
  654. private void dup()
  655. {
  656. int typeRef = u2();
  657. code.Inst(Op.dup);
  658. }
  659. private void pop()
  660. {
  661. int typeRef = u2();
  662. code.Inst(Op.pop);
  663. }
  664. //////////////////////////////////////////////////////////////////////////
  665. // Is/As
  666. //////////////////////////////////////////////////////////////////////////
  667. private void @is()
  668. {
  669. FTypeRef typeRef = pod.typeRef(u2());
  670. // if a generic instance, we have to use a method call
  671. // because Fantom types don't map to Java classes exactly;
  672. // otherwise we can use straight bytecode
  673. if (typeRef.isGenericInstance())
  674. {
  675. if (parent.IsViaType == null)
  676. parent.IsViaType = emitter.findMethod("Fanx.Util.OpUtil", "is",
  677. new string[] { "System.Object", "Fan.Sys.Type" }, "System.Boolean");
  678. loadType(typeRef);
  679. code.MethInst(MethodOp.call, parent.IsViaType);
  680. }
  681. else
  682. {
  683. PERWAPI.Type type = emitter.findType(typeRef.nnameBoxed());
  684. code.TypeInst(TypeOp.isinst, type);
  685. code.Inst(Op.ldnull);
  686. code.Inst(Op.cgt_un);
  687. }
  688. }
  689. private void @as()
  690. {
  691. FTypeRef typeRef = pod.typeRef(u2());
  692. PERWAPI.Type type = emitter.findType(typeRef.nnameBoxed());
  693. code.TypeInst(TypeOp.isinst, type);
  694. }
  695. //////////////////////////////////////////////////////////////////////////
  696. // Switch
  697. //////////////////////////////////////////////////////////////////////////
  698. private void tableswitch()
  699. {
  700. int count = u2();
  701. CILLabel[] labels = new CILLabel[count];
  702. for (int i=0; i<count; ++i)
  703. labels[i] = jumps.add(u2());
  704. code.Switch(labels);
  705. }
  706. //////////////////////////////////////////////////////////////////////////
  707. // Coercion
  708. //////////////////////////////////////////////////////////////////////////
  709. private void cast()
  710. {
  711. PERWAPI.Type type = emitter.findType(pod.typeRef(u2()).nname());
  712. code.TypeInst(TypeOp.castclass, type);
  713. }
  714. private void coerce()
  715. {
  716. FTypeRef from = pod.typeRef(u2());
  717. FTypeRef to = pod.typeRef(u2());
  718. // Bool boxing/unboxing
  719. if (from.isBoolPrimitive())
  720. {
  721. if (to.isRef()) { boolBox(); return; }
  722. throw new Exception("Coerce " + from + " => " + to);
  723. }
  724. if (to.isBoolPrimitive())
  725. {
  726. if (from.isRef()) { boolUnbox(!from.isBool()); return; }
  727. throw new Exception("Coerce " + from + " => " + to);
  728. }
  729. // Int boxing/unboxing
  730. if (from.isIntPrimitive())
  731. {
  732. if (to.isRef()) { intBox(); return; }
  733. throw new Exception("Coerce " + from + " => " + to);
  734. }
  735. if (to.isIntPrimitive())
  736. {
  737. if (from.isRef()) { intUnbox(!from.isInt()); return; }
  738. throw new Exception("Coerce " + from + " => " + to);
  739. }
  740. // Float boxing/unboxing
  741. if (from.isFloatPrimitive())
  742. {
  743. if (to.isRef()) { floatBox(); return; }
  744. throw new Exception("Coerce " + from + " => " + to);
  745. }
  746. if (to.isFloatPrimitive())
  747. {
  748. if (from.isRef()) { floatUnbox(!from.isFloat()); return; }
  749. throw new Exception("Coerce " + from + " => " + to);
  750. }
  751. // check nullable => non-nullable
  752. if (from.isNullable() && !to.isNullable())
  753. {
  754. CILLabel nonnull = code.NewLabel();
  755. code.Inst(Op.dup);
  756. code.Inst(Op.ldnull);
  757. code.Branch(BranchOp.bne_un_s, nonnull);
  758. if (parent.NullErrMakeCoerce == null)
  759. parent.NullErrMakeCoerce = emitter.findMethod("Fan.Sys.NullErr", "makeCoerce",
  760. new string[0], "Fan.Sys.Err/Val");
  761. code.MethInst(MethodOp.call, parent.NullErrMakeCoerce );
  762. code.Inst(Op.throwOp);
  763. code.CodeLabel(nonnull);
  764. }
  765. // don't bother casting to obj
  766. if (to.isObj()) return;
  767. code.TypeInst(TypeOp.castclass, emitter.findType(to.nname()));
  768. }
  769. //////////////////////////////////////////////////////////////////////////
  770. // Misc
  771. //////////////////////////////////////////////////////////////////////////
  772. private void doThrow()
  773. {
  774. if (parent.ErrVal == null)
  775. parent.ErrVal = emitter.findField("Fan.Sys.Err", "val", "Fan.Sys.Err/Val");
  776. code.FieldInst(FieldOp.ldfld, parent.ErrVal);
  777. code.Inst(Op.throwOp);
  778. }
  779. private void doLeave()
  780. {
  781. code.Branch(BranchOp.leave, jumps.add(u2()));
  782. }
  783. private void catchAllStart()
  784. {
  785. exType = "System.Object";
  786. errBlocks.Push(getLastTryBlock());
  787. code.StartBlock();
  788. code.Inst(Op.pop);
  789. }
  790. private void catchErrStart()
  791. {
  792. exType = "System.Exception";
  793. for (int i=0; i<tryJump.Length; i++)
  794. if (startPos == tryJump[i])
  795. {
  796. FTypeRef typeRef = pod.typeRef(tryErr[i]);
  797. dotnetErr = Fan.Sys.Err.fanToDotnet(typeRef.nname());
  798. if (!typeRef.isErr()) exType = typeRef.nname() + "/Val";
  799. break;
  800. }
  801. // close try block
  802. errBlocks.Push(getLastTryBlock());
  803. // use a filter if we need to "dual-check" for native exception
  804. if (dotnetErr != null)
  805. {
  806. code.CodeLabel(filterStart = code.NewLabel());
  807. CILLabel match = code.NewLabel();
  808. CILLabel endfilter = code.NewLabel();
  809. // check native type first
  810. code.Inst(Op.dup);
  811. code.TypeInst(TypeOp.isinst, emitter.findType(dotnetErr));
  812. code.Inst(Op.ldnull);
  813. code.Branch(BranchOp.bne_un_s, match);
  814. // then check Fantom type
  815. code.Inst(Op.dup);
  816. code.TypeInst(TypeOp.isinst, emitter.findType(exType));
  817. code.Inst(Op.ldnull);
  818. code.Branch(BranchOp.bne_un_s, match);
  819. // no match
  820. code.Inst(Op.pop); // pop exception off stack
  821. code.IntInst(IntOp.ldc_i4, 0);
  822. code.Branch(BranchOp.br_s, endfilter);
  823. // match
  824. code.CodeLabel(match);
  825. code.Inst(Op.pop); // pop exception off stack
  826. code.IntInst(IntOp.ldc_i4, 1);
  827. // endfilter
  828. code.CodeLabel(endfilter);
  829. code.Inst(Op.endfilter);
  830. }
  831. // start handler block
  832. code.StartBlock();
  833. // there is already a System.Exception on the stack, but
  834. // we need to map into a sys::Err type
  835. if (parent.ErrMake == null)
  836. parent.ErrMake = emitter.findMethod("Fan.Sys.Err", "make",
  837. new string[] { "System.Exception" }, "Fan.Sys.Err");
  838. code.MethInst(MethodOp.call, parent.ErrMake);
  839. cast();
  840. }
  841. private void catchEnd()
  842. {
  843. PERWAPI.TryBlock lastTry = (PERWAPI.TryBlock)errBlocks.Pop();
  844. if (dotnetErr != null)
  845. {
  846. // use a filter if we need to "dual-check" for native exception
  847. code.EndFilterBlock(filterStart, lastTry);
  848. dotnetErr = null;
  849. filterStart = null;
  850. }
  851. else
  852. {
  853. // this is the normal catch block
  854. code.EndCatchBlock(emitter.findType(exType) as PERWAPI.Class, lastTry);
  855. }
  856. }
  857. private void finallyStart()
  858. {
  859. code.EndTryBlock();
  860. errBlocks.Push(code.EndTryBlock());
  861. code.StartBlock();
  862. }
  863. private void finallyEnd()
  864. {
  865. code.Inst(Op.endfinally);
  866. code.EndFinallyBlock((PERWAPI.TryBlock)errBlocks.Pop());
  867. }
  868. private PERWAPI.TryBlock getLastTryBlock()
  869. {
  870. for (int i=0; i<tryJump.Length; i++)
  871. if (startPos == tryJump[i])
  872. {
  873. int start = tryStart[i];
  874. int end = tryEnd[i];
  875. string key = start + "/" + end;
  876. PERWAPI.TryBlock block = (PERWAPI.TryBlock)tryBlocks[key];
  877. if (block == null)
  878. {
  879. block = code.EndTryBlock();
  880. tryBlocks[key] = block;
  881. }
  882. return block;
  883. }
  884. throw new System.Exception("This is not good");
  885. }
  886. //////////////////////////////////////////////////////////////////////////
  887. // Utils
  888. //////////////////////////////////////////////////////////////////////////
  889. private int varStackType(int index)
  890. {
  891. if (vars == null) throw new Exception("Use of variable outside of method");
  892. if (!isStatic)
  893. {
  894. if (index == 0) return FTypeRef.OBJ; // assume this pointer
  895. else --index;
  896. }
  897. return pod.typeRef(vars[index].type).stackType;
  898. }
  899. private void boolBox()
  900. {
  901. PERWAPI.Method m = emitter.findMethod("Fan.Sys.Boolean", "valueOf",
  902. new string[] { "System.Boolean" }, "Fan.Sys.Boolean");
  903. code.MethInst(MethodOp.call, m);
  904. }
  905. private void boolUnbox(bool cast)
  906. {
  907. PERWAPI.Method m = emitter.findMethod("Fan.Sys.Boolean", "booleanValue",
  908. new string[0], "System.Boolean");
  909. m.AddCallConv(CallConv.Instance);
  910. code.MethInst(MethodOp.call, m);
  911. }
  912. private void intBox()
  913. {
  914. PERWAPI.Method m = emitter.findMethod("Fan.Sys.Long", "valueOf",
  915. new string[] { "System.Int64" }, "Fan.Sys.Long");
  916. code.MethInst(MethodOp.call, m);
  917. }
  918. private void intUnbox(bool cast)
  919. {
  920. PERWAPI.Method m = emitter.findMethod("Fan.Sys.Long", "longValue",
  921. new string[0], "System.Int64");
  922. m.AddCallConv(CallConv.Instance);
  923. code.MethInst(MethodOp.call, m);
  924. }
  925. private void floatBox()
  926. {
  927. PERWAPI.Method m = emitter.findMethod("Fan.Sys.Double", "valueOf",
  928. new string[] { "System.Double" }, "Fan.Sys.Double");
  929. code.MethInst(MethodOp.call, m);
  930. }
  931. private void floatUnbox(bool cast)
  932. {
  933. PERWAPI.Method m = emitter.findMethod("Fan.Sys.Double", "doubleValue",
  934. new string[0], "System.Double");
  935. m.AddCallConv(CallConv.Instance);
  936. code.MethInst(MethodOp.call, m);
  937. }
  938. private void loadIntVal()
  939. {
  940. if (parent.IntVal == null)
  941. {
  942. parent.IntVal = emitter.findMethod("Fan.Sys.Long", "longValue",
  943. new string[0], "System.Int64");
  944. parent.IntVal.AddCallConv(CallConv.Instance);
  945. }
  946. code.MethInst(MethodOp.call, parent.IntVal);
  947. }
  948. private void typeToNullable()
  949. {
  950. if (parent.TypeToNullable == null)
  951. {
  952. parent.TypeToNullable = emitter.findMethod("Fan.Sys.Type", "toNullable",
  953. new string[] {}, "Fan.Sys.Type");
  954. parent.TypeToNullable.AddCallConv(CallConv.Instance);
  955. }
  956. code.MethInst(MethodOp.callvirt, parent.TypeToNullable);
  957. }
  958. //////////////////////////////////////////////////////////////////////////
  959. // Buf
  960. //////////////////////////////////////////////////////////////////////////
  961. private int consumeOp()
  962. {
  963. // check for line numbers
  964. object line = lineNums[pos];
  965. if (line != null) code.Line((uint)((int)line), 1);
  966. // add labels
  967. code.CodeLabel(jumps.add(pos));
  968. // check for try blocks
  969. int peek = peekOp();
  970. int endPos = -1;
  971. for (int i=0; i<tryStart.Length; i++)
  972. //if (pos == tryStart[i])
  973. if (pos == tryStart[i] && peek != FConst.CatchErrStart && peek != FConst.CatchAllStart)
  974. {
  975. int jump = Peek(tryJump[i]);
  976. if (jump == FConst.CatchErrStart || jump == FConst.CatchAllStart)
  977. {
  978. // target is a catch, make sure we don't already
  979. // have a try block emitted for this region
  980. if (tryEnd[i] != endPos)
  981. {
  982. endPos = tryEnd[i];
  983. code.StartBlock();
  984. }
  985. }
  986. else
  987. {
  988. // target is a finally
  989. code.StartBlock();
  990. code.StartBlock();
  991. }
  992. }
  993. // get next opcode
  994. return u1();
  995. }
  996. private int peekOp()
  997. {
  998. if (pos < len) return buf[pos];
  999. return -1;
  1000. }
  1001. private int Peek(int index)
  1002. {
  1003. if (index < len) return buf[index];
  1004. return -1;
  1005. }
  1006. private int u1() { return buf[pos++]; }
  1007. private int u2() { return (buf[pos++] & 0xFF) << 8 | (buf[pos++] & 0xFF); }
  1008. private int u4() { return (buf[pos++] & 0xFF) << 24 | (buf[pos++] & 0xFF) << 16 | (buf[pos++] & 0xFF) << 8 | (buf[pos++] & 0xFF); }
  1009. //////////////////////////////////////////////////////////////////////////
  1010. // Jumps
  1011. //////////////////////////////////////////////////////////////////////////
  1012. internal class Jumps
  1013. {
  1014. public Jumps(CILInstructions code)
  1015. {
  1016. this.code = code;
  1017. }
  1018. public PERWAPI.CILLabel add(int loc)
  1019. {
  1020. if (map[loc] == null)
  1021. map[loc] = code.NewLabel();
  1022. return (PERWAPI.CILLabel)map[loc];
  1023. }
  1024. public PERWAPI.CILLabel get(int loc)
  1025. {
  1026. return (PERWAPI.CILLabel)map[loc];
  1027. }
  1028. Hashtable map = new Hashtable();
  1029. CILInstructions code;
  1030. }
  1031. //////////////////////////////////////////////////////////////////////////
  1032. // Reg
  1033. //////////////////////////////////////////////////////////////////////////
  1034. /// <summary>
  1035. /// Given a list of registers compute the max locals.
  1036. /// </summary>
  1037. internal static int maxLocals(Reg[] regs)
  1038. {
  1039. if (regs.Length == 0) return 0;
  1040. Reg last = regs[regs.Length-1];
  1041. return last.nindex + 1; //(last.isWide() ? 2 : 1);
  1042. }
  1043. /// <summary>
  1044. /// Map to .NET register info for the given Fantom local variables.
  1045. /// </summary>
  1046. internal static Reg[] initRegs(FPod pod, bool isStatic, FMethodVar[] vars)
  1047. {
  1048. Reg[] regs = new Reg[isStatic ? vars.Length : vars.Length+1];
  1049. int nindex = 0;
  1050. for (int i=0; i<regs.Length; ++i)
  1051. {
  1052. Reg r = new Reg();
  1053. if (i == 0 && !isStatic)
  1054. {
  1055. // this pointer
  1056. r.stackType = FTypeRef.OBJ;
  1057. r.nindex = nindex;
  1058. ++nindex;
  1059. }
  1060. else
  1061. {
  1062. FTypeRef typeRef = pod.typeRef(vars[isStatic ? i : i - 1].type);
  1063. r.stackType = typeRef.stackType;
  1064. r.nindex = nindex;
  1065. nindex += 1; //nindex += typeRef.isWide() ? 2 : 1;
  1066. }
  1067. regs[i] = r;
  1068. }
  1069. return regs;
  1070. }
  1071. private Reg reg(int fanIndex)
  1072. {
  1073. if (regs == null) throw new Exception("Use of variable with undefined regs");
  1074. return regs[fanIndex];
  1075. }
  1076. public class Reg
  1077. {
  1078. public string toString() { return "Reg " + nindex + " " + (char)stackType; }
  1079. public bool isWide() { return FTypeRef.isWide(stackType); }
  1080. internal int stackType; // FTypeRef.OBJ, LONG, INT, etc
  1081. internal int nindex; // .NET register number to use (might shift for longs/doubles)
  1082. }
  1083. //////////////////////////////////////////////////////////////////////////
  1084. // Fields
  1085. //////////////////////////////////////////////////////////////////////////
  1086. internal FPod pod;
  1087. internal Emitter emitter;
  1088. internal FTypeEmit parent;
  1089. internal FMethod fmethod; // maybe null
  1090. internal Reg[] regs = null; // register mappnig must be set for loadVar/storeVar
  1091. internal FMethodVar[] vars; // method variables must be set for loadVar/storeVar
  1092. internal bool isStatic; // used to determine how to index vars
  1093. internal FTypeRef ret; // return type
  1094. internal int paramCount = -1;
  1095. internal byte[] buf;
  1096. internal int len;
  1097. internal int pos;
  1098. internal int startPos; // start pos when processing a new opcode
  1099. internal CILInstructions code;
  1100. internal String podClass;
  1101. internal Jumps jumps; // map of jump locs to CodeLabels
  1102. internal int[] tryStart = new int[0]; // start offsets of try blocks
  1103. internal int[] tryEnd = new int[0]; // end offsets of try blocks
  1104. internal int[] tryJump = new int[0]; // jump offset of try blocks
  1105. internal int[] tryErr = new int[0]; // err types for catch blocks
  1106. internal string exType; // the exception type for the next catch block
  1107. internal Hashtable tryBlocks = new Hashtable(); // hash of opened try blocks
  1108. internal Hashtable lineNums = new Hashtable(); // map of fcode to line numbers
  1109. internal Stack errBlocks = new Stack(); // stack of try-catch-finally block offsets
  1110. internal string dotnetErr; // used for mapping .NET exceptions -> Fan
  1111. internal CILLabel filterStart; // used for mapping .NET exceptions -> Fan
  1112. }
  1113. }