PageRenderTime 41ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/sys/java/fanx/emit/FMethodEmit.java

https://bitbucket.org/bedlaczech/fan-1.0
Java | 492 lines | 270 code | 69 blank | 153 comment | 36 complexity | 51257356d191442eb7a61ea73dc4c662 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. // 25 Mar 06 Brian Frank Creation
  7. //
  8. package fanx.emit;
  9. import java.util.*;
  10. import fan.sys.*;
  11. import fan.sys.List;
  12. import fanx.fcode.*;
  13. import fanx.util.*;
  14. /**
  15. * FMethodEmit is used to emit Java bytecode methods from fcode methods.
  16. * It encapsulates lot of nitty details like when to include an implicit
  17. * self paramater, etc.
  18. */
  19. public class FMethodEmit
  20. implements EmitConst
  21. {
  22. //////////////////////////////////////////////////////////////////////////
  23. // Constructor
  24. //////////////////////////////////////////////////////////////////////////
  25. /**
  26. * Constructor
  27. */
  28. public FMethodEmit(FTypeEmit emit, FMethod method)
  29. {
  30. this.emit = emit;
  31. this.method = method;
  32. this.code = method.code;
  33. this.name = method.name;
  34. this.jflags = FTypeEmit.jflags(method.flags);
  35. this.paramLen = method.paramCount;
  36. this.isStatic = (method.flags & FConst.Static) != 0;
  37. this.isCtor = (method.flags & FConst.Ctor) != 0;
  38. this.isNative = (method.flags & FConst.Native) != 0;
  39. this.ret = emit.pod.typeRef(method.inheritedRet); // we don't actually use Java covariance
  40. this.selfName = emit.selfName;
  41. this.lineNum = method.attrs.lineNum;
  42. }
  43. /**
  44. * Constructor
  45. */
  46. public FMethodEmit(FTypeEmit emit)
  47. {
  48. this.emit = emit;
  49. }
  50. //////////////////////////////////////////////////////////////////////////
  51. // Emit
  52. //////////////////////////////////////////////////////////////////////////
  53. /**
  54. * Emit a standard instance/static class method.
  55. */
  56. public MethodEmit emitStandard()
  57. {
  58. // emit method
  59. MethodEmit main = doEmit();
  60. // emit param default wrappers
  61. emitWrappers(main);
  62. return main;
  63. }
  64. /**
  65. * Emit a constructor - constructors get created as a static
  66. * factory methods, so that that CallNew can just push args
  67. * and invoke them
  68. * fan:
  69. * class Foo { new make(Int? a) { ... } }
  70. * java:
  71. * static Foo make(Long a) { return make$(new Foo(), a) }
  72. * static Foo make$(Foo self, Long a) { ... return self }
  73. *
  74. * We call the first method "make" the "factory" and the
  75. * second method "make$" the "body". CallNew opcodes are
  76. * routed to the ctor factory, and CallCtor opcodes are routed
  77. * to the ctor body.
  78. */
  79. public MethodEmit emitCtor()
  80. {
  81. String ctorName = this.name;
  82. // both factory and body are static from Java's perspective
  83. this.jflags |= STATIC;
  84. this.isStatic = true;
  85. // first emit the body with implicit self
  86. this.name = ctorName + "$";
  87. this.self = true;
  88. MethodEmit body = doEmit();
  89. // emit body default parameter wrappers
  90. MethodEmit[] wrappers = emitWrappers(body);
  91. // then emit the factory
  92. this.name = ctorName;
  93. this.self = false;
  94. this.ret = emit.pod.typeRef(emit.type.self);
  95. this.code = null;
  96. int init = emit.method(selfName+ ".<init>()V");
  97. MethodEmit factory = emitCtorFactory(init, body, method.paramCount);
  98. // emit separate factory for each method with default
  99. // parameters: note each factory allocs the object and
  100. // passes it to the make$ wrapper; we rely on the make$
  101. // wrappers to do default params because the factory's
  102. // signature doesn't match up to what the Fantom compiler
  103. // generated (ctor assumes local_0 is this pointer)
  104. for (int i=0; i<method.paramCount; ++i)
  105. if (method.vars[i].def != null)
  106. emitCtorFactory(init, wrappers[i], i);
  107. // return static factory as our primary Java method
  108. return factory;
  109. }
  110. /**
  111. * Emit the factory part of the constructor:
  112. * fan:
  113. * new make(Int a, Obj? b := null) {...}
  114. * java:
  115. * static Foo make(long a) { return make$(new Foo(), a) }
  116. * static Foo make(long a, Object b) { return make$(new Foo(), a, b) }
  117. */
  118. private MethodEmit emitCtorFactory(int init, MethodEmit body, int paramLen)
  119. {
  120. this.paramLen = paramLen;
  121. MethodEmit factory = doEmit();
  122. CodeEmit code = factory.emitCode();
  123. code.op2(NEW, emit.cls(selfName));
  124. code.op(DUP);
  125. code.op2(INVOKESPECIAL, init);
  126. code.op(DUP);
  127. code.maxLocals = pushArgs(code, false, paramLen);
  128. code.maxStack = code.maxLocals + 2;
  129. code.op2(INVOKESTATIC, body.ref());
  130. code.op(ARETURN);
  131. code.emitLineNumber(lineNum);
  132. return factory;
  133. }
  134. /**
  135. * Emit a Fantom constructor for a class which subclasses from
  136. * a normal Java class brought into the Fantom type system via FFI.
  137. * In this case the superclass constructor is a real constructor
  138. * so we need to emit the code differently:
  139. * fan:
  140. * class Foo { new make(String a) { ... } }
  141. * java:
  142. * static Foo make(String a) { return new Foo(a) }
  143. * Foo(String a) { ... }
  144. */
  145. public MethodEmit emitCtorWithJavaSuper()
  146. {
  147. String ctorName = this.name;
  148. // first emit the body as a true Java constructor
  149. this.name = "<init>";
  150. MethodEmit body = doEmit();
  151. // emit body default parameter wrappers
  152. emitWrappers(body);
  153. // then emit the factory
  154. this.name = ctorName;
  155. this.self = false;
  156. this.ret = emit.pod.typeRef(emit.type.self);
  157. this.code = null;
  158. this.jflags |= STATIC;
  159. this.isStatic = true;
  160. MethodEmit factory = doEmit();
  161. CodeEmit code = factory.emitCode();
  162. code.op2(NEW, emit.cls(selfName));
  163. code.op(DUP);
  164. code.maxLocals = pushArgs(code, false, method.paramCount) + 1;
  165. code.maxStack = code.maxLocals + 2;
  166. code.op2(INVOKESPECIAL, body.ref());
  167. code.op(ARETURN);
  168. code.emitLineNumber(lineNum);
  169. // emit factory default parameter wrappers
  170. emitWrappers(factory);
  171. return factory;
  172. }
  173. /**
  174. * Emit a native method
  175. */
  176. public MethodEmit emitNative()
  177. {
  178. // emit an empty method
  179. this.code = null;
  180. MethodEmit main = doEmit();
  181. // emit code which calls the peer
  182. CodeEmit code = main.emitCode();
  183. if (isStatic)
  184. {
  185. int peerMethod = emit.method(selfName + "Peer." + name + sig);
  186. code.maxLocals = pushArgs(code, false, paramLen);
  187. code.maxStack = Math.max(code.maxLocals, 2);
  188. code.op2(INVOKESTATIC, peerMethod);
  189. }
  190. else
  191. {
  192. // generate peer's signature with self
  193. this.self = true;
  194. String sig = signature();
  195. this.self = false;
  196. int peerMethod = emit.method(selfName + "Peer." + name + sig);
  197. code.op(ALOAD_0);
  198. code.op2(GETFIELD, emit.peerField.ref());
  199. code.maxLocals = pushArgs(code, true, paramLen) + 1;
  200. code.maxStack = Math.max(code.maxLocals, 2);
  201. code.op2(INVOKEVIRTUAL, peerMethod);
  202. }
  203. code.op(FCodeEmit.returnOp(ret));
  204. // emit default parameter wrappers
  205. emitWrappers(main);
  206. return main;
  207. }
  208. /**
  209. * Emit the method as a mixin interface
  210. */
  211. public void emitMixinInterface()
  212. {
  213. // we only emit instance methods in the interface
  214. if (isStatic || isCtor) return;
  215. // set abstract/public flags and clear code
  216. this.jflags |= (ABSTRACT | PUBLIC);
  217. this.code = null;
  218. // emit main
  219. doEmit();
  220. // emit a signature for each overload based on param defaults
  221. for (int i=0; i<method.paramCount; ++i)
  222. {
  223. if (method.vars[i].def != null)
  224. {
  225. paramLen = i;
  226. doEmit();
  227. }
  228. }
  229. }
  230. /**
  231. * Emit the method as a mixin body class which ends with $.
  232. */
  233. public void emitMixinBody()
  234. {
  235. // skip abstract methods without code
  236. if (method.code == null) return;
  237. // instance methods have an implicit self
  238. if (!isStatic) this.self = true;
  239. // bodies are always public static
  240. this.jflags |= (STATIC | PUBLIC);
  241. // emit main body
  242. MethodEmit main = doEmit();
  243. // emit param default wrappers
  244. emitWrappers(main);
  245. }
  246. /**
  247. * Emit a mixin router from a class to the mixin body methods.
  248. */
  249. public void emitMixinRouter(Method m)
  250. {
  251. String parent = "fan/" + m.parent().pod().name() + "/" + m.parent().name();
  252. String name = m.name();
  253. int jflags = emit.jflags(m.flags()) | PUBLIC | SYNTHETIC;
  254. List params = m.params();
  255. int paramCount = params.sz();
  256. // find first param with default value
  257. int firstDefault = paramCount;
  258. for (int i=0; i<paramCount; ++i)
  259. if (((Param)params.get(i)).hasDefault())
  260. { firstDefault = i; break; }
  261. // generate routers
  262. for (int i=firstDefault; i<=paramCount; ++i)
  263. {
  264. String mySig = signature(m, null, i);
  265. String implSig = signature(m, parent, i);
  266. MethodEmit me = emit.emitMethod(name, mySig, jflags);
  267. CodeEmit code = me.emitCode();
  268. code.op(ALOAD_0); // push this
  269. int jindex = 1;
  270. for (int p=0; p<i; ++p)
  271. {
  272. // push args
  273. Param param = (Param)m.params().get(p);
  274. jindex = FCodeEmit.loadVar(code, FanUtil.toJavaStackType(param.type()), jindex);
  275. }
  276. code.op2(INVOKESTATIC, emit.method(parent + "$." + name + implSig));
  277. code.op(FCodeEmit.returnOp(FanUtil.toJavaStackType(m.returns())));
  278. code.maxLocals = jindex;
  279. code.maxStack = jindex+1; // leave room for wide return
  280. // use line number of class header
  281. code.emitLineNumber(emit.lineNum);
  282. }
  283. }
  284. //////////////////////////////////////////////////////////////////////////
  285. // Param Default Wrappers
  286. //////////////////////////////////////////////////////////////////////////
  287. /**
  288. * Emit wrappers, return wrapper methods indexed by param length or null.
  289. */
  290. private MethodEmit[] emitWrappers(MethodEmit main)
  291. {
  292. // change flags so that defaults aren't abstract
  293. int oldFlags = this.jflags;
  294. this.jflags = jflags & ~ABSTRACT;
  295. // handle generating default param wrappers
  296. MethodEmit[] wrappers = null;
  297. for (int i=0; i<method.paramCount; ++i)
  298. if (method.vars[i].def != null)
  299. {
  300. if (wrappers == null) wrappers = new MethodEmit[method.paramCount];
  301. wrappers[i] = emitWrapper(main, i);
  302. }
  303. this.paramLen = method.paramCount;
  304. this.jflags = oldFlags;
  305. return wrappers;
  306. }
  307. /**
  308. * Emit wrapper.
  309. */
  310. private MethodEmit emitWrapper(MethodEmit main, int paramLen)
  311. {
  312. // use explicit param count, and clear code
  313. this.paramLen = paramLen;
  314. this.code = null;
  315. // emit code
  316. CodeEmit code = doEmit().emitCode();
  317. // push arguments passed thru
  318. pushArgs(code, !(isStatic && !self), paramLen);
  319. // emit default arguments
  320. FCodeEmit.Reg[] regs = FCodeEmit.initRegs(emit, isStatic & !isCtor, method.vars);
  321. for (int i=paramLen; i<method.paramCount; ++i)
  322. {
  323. FCodeEmit e = new FCodeEmit(emit, method.vars[i].def, code, regs, emit.pod.typeRef(method.ret));
  324. e.emit();
  325. }
  326. code.maxStack = code.maxLocals + 4; // leave room for wide return or type literal loading and null coercion
  327. // call master implementation
  328. code.op2((main.flags & STATIC) != 0 ? INVOKESTATIC : INVOKEVIRTUAL, main.ref());
  329. // return
  330. code.op(FCodeEmit.returnOp(ret));
  331. return me;
  332. }
  333. //////////////////////////////////////////////////////////////////////////
  334. // Emit
  335. //////////////////////////////////////////////////////////////////////////
  336. /**
  337. * This is the method that all the public emitX methods
  338. * route to once everything is setup correctly.
  339. */
  340. protected MethodEmit doEmit()
  341. {
  342. this.sig = signature();
  343. this.me = emit.emitMethod(name, sig, jflags);
  344. if (code != null)
  345. {
  346. new FCodeEmit(emit, method, me.emitCode()).emit();
  347. }
  348. return this.me;
  349. }
  350. //////////////////////////////////////////////////////////////////////////
  351. // Signature Utils
  352. //////////////////////////////////////////////////////////////////////////
  353. /**
  354. * Generate the java method signature base on our current setup.
  355. */
  356. private String signature()
  357. {
  358. StringBuilder sig = new StringBuilder();
  359. // params (with optional implicit self)
  360. sig.append('(');
  361. if (self) sig.append('L').append(selfName).append(';');
  362. for (int i=0; i<paramLen; ++i)
  363. emit.pod.typeRef(method.vars[i].type).jsig(sig);
  364. sig.append(')');
  365. // return
  366. ret.jsig(sig);
  367. return sig.toString();
  368. }
  369. /**
  370. * Generate a method signature from a reflection sys::Method.
  371. */
  372. private String signature(Method m, String self, int paramLen)
  373. {
  374. StringBuilder sig = new StringBuilder();
  375. // params
  376. sig.append('(');
  377. if (self != null) sig.append('L').append(self).append(';');
  378. for (int i=0; i<paramLen; ++i)
  379. {
  380. Param param = (Param)m.params().get(i);
  381. sig.append(FanUtil.toJavaMemberSig(param.type()));
  382. }
  383. sig.append(')');
  384. // return
  385. sig.append(FanUtil.toJavaMemberSig(m.inheritedReturns()));
  386. return sig.toString();
  387. }
  388. //////////////////////////////////////////////////////////////////////////
  389. // Code Utils
  390. //////////////////////////////////////////////////////////////////////////
  391. /**
  392. * Push the specified number of arguments onto the stack.
  393. */
  394. private int pushArgs(CodeEmit code, boolean self, int count)
  395. {
  396. int jindex = 0;
  397. if (self) { code.op(ALOAD_0); ++jindex; }
  398. for (int i=0; i<count; ++i)
  399. {
  400. FTypeRef var = emit.pod.typeRef(method.vars[i].type);
  401. jindex = FCodeEmit.loadVar(code, var.stackType, jindex);
  402. }
  403. return jindex;
  404. }
  405. //////////////////////////////////////////////////////////////////////////
  406. // Fields
  407. //////////////////////////////////////////////////////////////////////////
  408. FTypeEmit emit; // parent type class emitter
  409. FMethod method; // fan method info
  410. FBuf code; // code to emit
  411. String name; // method name
  412. int jflags; // java flags
  413. boolean isStatic; // are we emitting a static method
  414. boolean isCtor; // are we emitting a constructor
  415. boolean isNative; // are we emitting a native method
  416. FTypeRef ret; // java return sig
  417. boolean self; // add implicit self as first parameter
  418. String selfName; // class name for self if self is true
  419. int paramLen; // number of parameters to use
  420. String sig; // last java signature emitted
  421. MethodEmit me; // last java method emitted
  422. int lineNum; // line number of method (or zero)
  423. }