PageRenderTime 57ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/bedlaczech/fan-1.0
C# | 587 lines | 339 code | 78 blank | 170 comment | 77 complexity | e365ef5316791022e2f5f9bc35106eed 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. // 2 Oct 06 Andy Frank Creation
  7. //
  8. using System.Collections;
  9. using System.IO;
  10. using System.Reflection;
  11. using ICSharpCode.SharpZipLib.Zip;
  12. using Fan.Sys;
  13. using Fanx.Fcode;
  14. using Fanx.Util;
  15. namespace Fanx.Emit
  16. {
  17. /// <summary>
  18. /// FTypeEmit translates FType fcode to IL.
  19. /// </summary>
  20. public abstract class FTypeEmit
  21. {
  22. //////////////////////////////////////////////////////////////////////////
  23. // Factory
  24. //////////////////////////////////////////////////////////////////////////
  25. public static System.Type[] emitAndLoad(FType ftype)
  26. {
  27. string className = ftype.m_pod.nname(ftype.m_self);
  28. Assembly assembly = emitPod(ftype.m_pod, true, null);
  29. FTypeEmit[] emitted = (FTypeEmit[])ftypes[ftype];
  30. System.Type[] types = new System.Type[emitted.Length];
  31. for (int i=0; i<emitted.Length; i++)
  32. {
  33. FTypeEmit e = emitted[i];
  34. types[i] = assembly.GetType(e.className);
  35. }
  36. return types;
  37. }
  38. public static Assembly emitPod(FPod pod, bool load, string path)
  39. {
  40. string podName = pod.m_podName;
  41. Assembly assembly = (Assembly)assemblies[podName];
  42. if (assembly == null)
  43. {
  44. Emitter emitter = new Emitter(podName, path);
  45. // unzip the native.dll if one exists
  46. unzipToTemp(pod, podName + "Native_.dll");
  47. unzipToTemp(pod, podName + "Native_.pdb");
  48. // emit the pod class itself (which declares all constants)
  49. //FPodEmit.EmitAndLoad(emitter, pod);
  50. FPodEmit.emit(emitter, pod);
  51. // the Emitter needs base types to be defined before
  52. // descendant types, so make sure everything gets stubbed
  53. // out in the correct order ahead of time
  54. for (int i=0; i<pod.m_types.Length; i++)
  55. emitter.findType(pod.nname(pod.m_types[i].m_self));
  56. // emit all the rest of the types in this pod
  57. for (int i=0; i<pod.m_types.Length; i++)
  58. {
  59. FType ftype = pod.m_types[i];
  60. FTypeRef tref = ftype.m_pod.typeRef(ftype.m_self);
  61. Type parent = Type.find(tref.podName+"::"+tref.typeName, true);
  62. // make sure we have reflected to setup slots
  63. parent.reflect();
  64. // route based on type
  65. if ((ftype.m_flags & FConst.Mixin) != 0)
  66. {
  67. // interface
  68. FMixinInterfaceEmit iemit = new FMixinInterfaceEmit(emitter, parent, ftype);
  69. iemit.emit();
  70. // body class
  71. FMixinBodyEmit bemit = new FMixinBodyEmit(emitter, parent, ftype);
  72. bemit.emit();
  73. ftypes[ftype] = new FTypeEmit[] { iemit, bemit };
  74. }
  75. else if (parent.@is(Sys.ErrType))
  76. {
  77. // error
  78. FErrEmit emitErr = new FErrEmit(emitter, parent, ftype);
  79. emitErr.emit();
  80. FErrValEmit emitErrVal = new FErrValEmit(emitter, parent, ftype);
  81. emitErrVal.emit();
  82. ftypes[ftype] = new FTypeEmit[] { emitErr, emitErrVal };
  83. }
  84. else
  85. {
  86. // class
  87. FClassEmit emit = new FClassEmit(emitter, parent, ftype);
  88. emit.emit();
  89. ftypes[ftype] = new FTypeEmit[] { emit };
  90. }
  91. }
  92. // commit assembly
  93. byte[] buf = emitter.commit();
  94. if (load)
  95. {
  96. //long start = System.Environment.TickCount;
  97. // load assembly
  98. assembly = (buf == null)
  99. ? Assembly.LoadFile(emitter.fileName)
  100. : Assembly.Load(buf);
  101. assemblies[podName] = assembly;
  102. //long end = System.Environment.TickCount;
  103. //System.Console.WriteLine("load " + podName + " in " + (end-start) + " ms");
  104. // load $Pod type
  105. FPodEmit.load(assembly, pod);
  106. }
  107. }
  108. return assembly;
  109. }
  110. /// <summary>
  111. /// Unzip the file if it exists into the lib/tmp dir
  112. /// </summary>
  113. static void unzipToTemp(FPod pod, string filename)
  114. {
  115. if (pod.m_store == null) return; // compiled from script
  116. ZipEntry entry = pod.m_store.zipFile.GetEntry(filename);
  117. if (entry == null) return;
  118. BufferedStream fin = new BufferedStream(pod.m_store.zipFile.GetInputStream(entry));
  119. FileStream fout = System.IO.File.Create(
  120. FileUtil.combine(Fan.Sys.Sys.m_homeDir, "lib", "tmp", filename));
  121. byte[] b = new byte[4096];
  122. while (true)
  123. {
  124. int r = fin.Read(b, 0, b.Length);
  125. if (r <= 0) break;
  126. fout.Write(b, 0, r);
  127. }
  128. fout.Flush();
  129. fin.Close();
  130. fout.Close();
  131. }
  132. //////////////////////////////////////////////////////////////////////////
  133. // Constructor
  134. //////////////////////////////////////////////////////////////////////////
  135. protected FTypeEmit(Emitter emitter, Type parent, FType type)
  136. {
  137. this.emitter = emitter;
  138. this.parent = parent;
  139. this.pod = type.m_pod;
  140. this.type = type;
  141. }
  142. /// <summary>
  143. /// Initialize an Emitter to generate a class with given name,
  144. /// super class, interfaces, and class level access flags.
  145. /// <summary>
  146. public void init(string thisClass, string baseClass, string[] interfaces, int flags)
  147. {
  148. this.className = thisClass;
  149. this.baseClassName = baseClass;
  150. this.interfaces = interfaces;
  151. //this.cp.add(new CpDummy()); // dummy entry since constant pool starts at 1
  152. //this.thisClassIndex = cls(thisClass);
  153. //this.superClassIndex = cls(superClass);
  154. //this.interfaces = new int[interfaces.length];
  155. //for (int i=0; i<interfaces.length; ++i)
  156. // this.interfaces[i] = cls(interfaces[i]);
  157. //this.flags = flags;
  158. this.isAbstract = (flags & FConst.Abstract) != 0;
  159. }
  160. //////////////////////////////////////////////////////////////////////////
  161. // Emit
  162. //////////////////////////////////////////////////////////////////////////
  163. /// <summary>
  164. /// Return true if this pod has already been emitted.
  165. /// </summary>
  166. public static bool isEmitted(string podName)
  167. {
  168. return assemblies[podName] != null;
  169. }
  170. /// <summary>
  171. /// Emit to IL assembly.
  172. /// </summary>
  173. public virtual void emit()
  174. {
  175. init(nname(type.m_self), @base(), mixins(), type.m_flags);
  176. this.selfName = className;
  177. // make sure type has been read
  178. if (type.m_hollow) type.read();
  179. // emit
  180. preview();
  181. emitType();
  182. for (int i=0; i<type.m_fields.Length; i++) emit(type.m_fields[i]);
  183. for (int i=0; i<type.m_methods.Length; i++) emit(type.m_methods[i]);
  184. emitAttributes(type.m_attrs);
  185. emitMixinRouters();
  186. if (!hasInstanceInit) emitInstanceInit(null);
  187. if (!hasStaticInit) emitStaticInit(null);
  188. emitTypeConstFields();
  189. }
  190. /// <summary>
  191. /// Return the base type for this type.
  192. /// </summary>
  193. protected abstract string @base();
  194. protected virtual string[] mixins()
  195. {
  196. string[] mixins = new string[type.m_mixins.Length];
  197. for (int i=0; i<mixins.Length; i++)
  198. mixins[i] = nname(type.m_mixins[i]);
  199. return mixins;
  200. }
  201. private void preview()
  202. {
  203. this.isNative = (type.m_flags & FConst.Native) != 0;
  204. if (!this.isNative)
  205. {
  206. for (int i=0; i<type.m_methods.Length; ++i)
  207. if ((type.m_methods[i].m_flags & FConst.Native) != 0)
  208. { this.isNative = true; break; }
  209. }
  210. }
  211. /// <summary>
  212. /// Emit the type information for this type.
  213. /// </summary>
  214. protected abstract void emitType();
  215. /// <summary>
  216. /// Emit a attribute.
  217. /// </summary>
  218. private void emitAttributes(FAttrs attrs)
  219. {
  220. //if (attrs.sourceFile != null)
  221. //{
  222. // AttrEmit attr = emitAttr("SourceFile");
  223. // attr.info.u2(utf(attrs.sourceFile));
  224. //}
  225. }
  226. /// <summary>
  227. /// Emit a field.
  228. /// </summary>
  229. protected virtual void emit(FField f)
  230. {
  231. if ((f.m_flags & FConst.Storage) != 0)
  232. emitter.emitField("m_" + f.m_name, nname(f.m_type), fieldFlags(f.m_flags));
  233. }
  234. /// <summary>
  235. /// Emit a method.
  236. /// </summary>
  237. protected virtual void emit(FMethod m)
  238. {
  239. string n = m.m_name;
  240. bool isNative = (m.m_flags & FConst.Native) != 0;
  241. bool isCtor = (m.m_flags & FConst.Ctor) != 0;
  242. // static$init -> .cctor
  243. // instance$init -> .ctor
  244. if (n == "static$init") { emitStaticInit(m); return; }
  245. if (n == "instance$init") { emitInstanceInit(m); return; }
  246. // handle native/constructor/normal method
  247. if (isNative)
  248. {
  249. new FMethodEmit(this, m).emitNative();
  250. }
  251. else if (isCtor)
  252. {
  253. new FMethodEmit(this, m).emitCtor();
  254. }
  255. else
  256. {
  257. new FMethodEmit(this, m).emitStandard();
  258. }
  259. }
  260. protected virtual void emitInstanceInit(FMethod m)
  261. {
  262. hasInstanceInit = true;
  263. PERWAPI.CILInstructions code = ctor.CreateCodeBuffer();
  264. // initalize code to call super
  265. code.Inst(PERWAPI.Op.ldarg_0);
  266. // if closure, push FuncType static field
  267. if (funcType != null)
  268. {
  269. code.FieldInst(PERWAPI.FieldOp.ldsfld, typeField);
  270. PERWAPI.Method baseCtor = emitter.findMethod(baseClassName, ".ctor",
  271. new string[] { "Fan.Sys.FuncType" }, "System.Void");
  272. baseCtor.AddCallConv(PERWAPI.CallConv.Instance); // if stub, make sure instance callconv
  273. code.MethInst(PERWAPI.MethodOp.call, baseCtor);
  274. }
  275. else
  276. {
  277. PERWAPI.Method baseCtor = emitter.findMethod(baseClassName, ".ctor",
  278. new string[0], "System.Void");
  279. baseCtor.AddCallConv(PERWAPI.CallConv.Instance); // if stub, make sure instance callconv
  280. code.MethInst(PERWAPI.MethodOp.call, baseCtor);
  281. }
  282. // make peer
  283. if (isNative)
  284. {
  285. //code.op(ALOAD_0); // for putfield
  286. //code.op(DUP); // for arg to make
  287. //code.op2(INVOKESTATIC, method(selfName + "Peer.make(L" + className + ";)L" + className + "Peer;"));
  288. //code.op2(PUTFIELD, peerField.ref());
  289. code.Inst(PERWAPI.Op.ldarg_0);
  290. code.Inst(PERWAPI.Op.dup);
  291. PERWAPI.Method peerMake = emitter.findMethod(className + "Peer", "make",
  292. new string[] { className }, className + "Peer");
  293. code.MethInst(PERWAPI.MethodOp.call, peerMake);
  294. code.FieldInst(PERWAPI.FieldOp.stfld, peerField);
  295. }
  296. if (m == null)
  297. code.Inst(PERWAPI.Op.ret);
  298. else
  299. new FCodeEmit(this, m, code).emit();
  300. }
  301. internal void emitStaticInit(FMethod m)
  302. {
  303. // make sure we add local defs
  304. if (m != null && m.m_localCount > 0)
  305. {
  306. PERWAPI.Local[] locals = new PERWAPI.Local[m.m_vars.Length];
  307. for (int i=0; i<locals.Length; i++)
  308. {
  309. string name = m.m_vars[i].name;
  310. string type = nname(m.m_vars[i].type);
  311. locals[i] = new PERWAPI.Local(name, emitter.findType(type));
  312. }
  313. cctor.AddLocals(locals, true);
  314. }
  315. hasStaticInit = true;
  316. PERWAPI.CILInstructions code = cctor.CreateCodeBuffer();
  317. // set $Type field with type (if we this is a closure,
  318. // then the FuncType will be the type exposed)
  319. if (!parent.isMixin())
  320. {
  321. Type t = parent;
  322. if (parent.@base() is FuncType) t = parent.@base();
  323. code.ldstr(t.signature());
  324. PERWAPI.Method findType = emitter.findMethod("Fan.Sys.Type", "find",
  325. new string[] { "System.String" }, "Fan.Sys.Type");
  326. code.MethInst(PERWAPI.MethodOp.call, findType);
  327. code.FieldInst(PERWAPI.FieldOp.stsfld, typeField);
  328. }
  329. if (m == null)
  330. code.Inst(PERWAPI.Op.ret);
  331. else
  332. new FCodeEmit(this, m, code).emit();
  333. }
  334. internal void emitTypeConstFields()
  335. {
  336. // if during the emitting of all the methods we ran across a non-sys
  337. // LoadType opcode, then we need to generate a static field called
  338. // type${pod}${name} we can use to cache the type once it is looked up
  339. // if (typeLiteralFields == null) return;
  340. // Iterator it = typeLiteralFields.values().iterator();
  341. // while (it.hasNext())
  342. // {
  343. // String fieldName = (String)it.next();
  344. // emitField(fieldName, "Lfan/sys/Type;", EmitConst.PRIVATE|EmitConst.STATIC);
  345. // }
  346. }
  347. //////////////////////////////////////////////////////////////////////////
  348. // Mixin Routers
  349. //////////////////////////////////////////////////////////////////////////
  350. private void emitMixinRouters()
  351. {
  352. // short circuit if no direct mixins implemented
  353. if (parent.mixins().isEmpty()) return;
  354. // first we have to find all the mixins I inherit thru my
  355. // direct mixin inheritances (but not my class extension) - these
  356. // are the ones I need routers for (but I can skip generating
  357. // routers for any mixins implemented by my super class)
  358. Hashtable acc = new Hashtable();
  359. findMixins(parent, acc);
  360. // emit routers for concrete instance methods
  361. IEnumerator en = acc.Values.GetEnumerator();
  362. while (en.MoveNext())
  363. {
  364. Type mixin = (Type)en.Current;
  365. emitMixinRouters(mixin);
  366. }
  367. }
  368. private void findMixins(Type t, Hashtable acc)
  369. {
  370. // if mixin I haven't seen add to accumulator
  371. string qname = t.qname();
  372. if (t.isMixin() && acc[qname] == null)
  373. acc[qname] = t;
  374. // recurse
  375. for (int i=0; i<t.mixins().sz(); ++i)
  376. findMixins((Type)t.mixins().get(i), acc);
  377. }
  378. private void emitMixinRouters(Type type)
  379. {
  380. // generate router method for each concrete instance method
  381. List methods = type.methods();
  382. for (int i=0; i<methods.sz(); i++)
  383. {
  384. Method m = (Method)methods.get(i);
  385. string name = m.name();
  386. // only emit router for non-abstract instance methods
  387. if (m.isStatic()) continue;
  388. if (m.isAbstract())
  389. {
  390. // however if abstract, check if a base class
  391. // has already implemented this method
  392. if (m.parent() == type && parent.slot(name, true).parent() == type)
  393. {
  394. Type b = parent.@base();
  395. while (b != null)
  396. {
  397. Slot s = b.slot(name, false);
  398. if (s != null && s.parent() == b)
  399. {
  400. new FMethodEmit(this).emitInterfaceRouter(b, m);
  401. break;
  402. }
  403. b = b.@base();
  404. }
  405. }
  406. continue;
  407. }
  408. // only emit the router unless this is the exact one I inherit
  409. if (parent.slot(name, true).parent() != type) continue;
  410. // do it
  411. new FMethodEmit(this).emitMixinRouter(m);
  412. }
  413. }
  414. //////////////////////////////////////////////////////////////////////////
  415. // Utils
  416. //////////////////////////////////////////////////////////////////////////
  417. /// <summary>
  418. /// Map Fantom flags to .NET field flags. Note we emit protected as
  419. /// public and internal/private as package-private so that we don't
  420. /// need to deal with scope issues for accessors like closures and
  421. /// helper classes.
  422. /// </summary>
  423. internal static PERWAPI.FieldAttr fieldFlags(int fflags)
  424. {
  425. /*
  426. PERWAPI.FieldAttr nflags = 0;
  427. if ((fflags & FConst.Private) != 0) nflags |= PERWAPI.FieldAttr.Assembly;
  428. if ((fflags & FConst.Protected) != 0) nflags |= PERWAPI.FieldAttr.Public;
  429. if ((fflags & FConst.Public) != 0) nflags |= PERWAPI.FieldAttr.Public;
  430. //if ((fflags & FConst.Internal) != 0) nflags |= PERWAPI.FieldAttr.Assembly;
  431. if ((fflags & FConst.Static) != 0) nflags |= PERWAPI.FieldAttr.Static;
  432. return nflags;
  433. */
  434. PERWAPI.FieldAttr nflags = PERWAPI.FieldAttr.Public;
  435. if ((fflags & FConst.Static) != 0) nflags |= PERWAPI.FieldAttr.Static;
  436. return nflags;
  437. }
  438. /// <summary>
  439. /// Given a Fantom qname index, map to a .NET type name: sys/Bool
  440. /// </summary>
  441. internal string nname(int index)
  442. {
  443. return pod.nname(index);
  444. }
  445. /// <summary>
  446. /// Map a simple name index to it's string value
  447. /// </summary>
  448. internal string name(int index)
  449. {
  450. return pod.name(index);
  451. }
  452. /*
  453. /// <summary>
  454. /// Map a simple name index to it's string value
  455. /// </summary>
  456. internal string Name(int index)
  457. {
  458. return pod.Name(index);
  459. }
  460. /// <summary>
  461. /// Get method ref to sys::Sys.type(String, bool)
  462. /// </summary>
  463. internal int SysFindType()
  464. {
  465. if (sysFindType == 0)
  466. sysFindType = method("fan/sys/Sys.findType(Ljava/lang/String;Z)Lfan/sys/Type;");
  467. return sysFindType ;
  468. }
  469. private int sysFindType;
  470. */
  471. //////////////////////////////////////////////////////////////////////////
  472. // Cached CpInfo
  473. //////////////////////////////////////////////////////////////////////////
  474. internal PERWAPI.Method CompareSame;
  475. internal PERWAPI.Method CompareNotSame;
  476. internal PERWAPI.Method CompareNull;
  477. internal PERWAPI.Method CompareNotNull;
  478. internal PERWAPI.Method IsViaType;
  479. internal PERWAPI.Method IntVal;
  480. internal PERWAPI.Method ErrMake;
  481. internal PERWAPI.Field ErrVal;
  482. internal PERWAPI.Method TypeToNullable;
  483. internal PERWAPI.Method NullErrMakeCoerce;
  484. //////////////////////////////////////////////////////////////////////////
  485. // Fields
  486. //////////////////////////////////////////////////////////////////////////
  487. public string className;
  488. public string baseClassName;
  489. public string[] interfaces;
  490. internal PERWAPI.MethodDef ctor; // cached .ctor emit object
  491. internal PERWAPI.MethodDef cctor; // cahced .cctor emit object
  492. internal Emitter emitter; // The emitter for the assembly for this type
  493. internal Type parent;
  494. internal FPod pod;
  495. internal FType type;
  496. internal string selfName; // type name to use as self (for mixin body - this is interface)
  497. internal PERWAPI.Field typeField; // private static final Type $Type
  498. internal PERWAPI.Field peerField = null; // public static final TypePeer peer
  499. internal bool hasInstanceInit; // true if we already emitted <init>
  500. internal bool hasStaticInit; // true if we already emitted <clinit>
  501. internal FuncType funcType; // if type is a closure
  502. internal Hashtable typeLiteralFields; // signature Strings we need to turn into cached fields
  503. internal bool isNative = false; // do we have any native methods requiring a peer
  504. internal bool isAbstract; // are we emitting an abstract method
  505. private static Hashtable assemblies = new Hashtable(); // assembly cache
  506. private static Hashtable ftypes = new Hashtable(); // ftype[] lookup
  507. }
  508. }