/src/sys/java/fanx/emit/FMethodEmit.java
Java | 492 lines | 270 code | 69 blank | 153 comment | 36 complexity | 51257356d191442eb7a61ea73dc4c662 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
- //
- // Copyright (c) 2006, Brian Frank and Andy Frank
- // Licensed under the Academic Free License version 3.0
- //
- // History:
- // 25 Mar 06 Brian Frank Creation
- //
- package fanx.emit;
- import java.util.*;
- import fan.sys.*;
- import fan.sys.List;
- import fanx.fcode.*;
- import fanx.util.*;
- /**
- * FMethodEmit is used to emit Java bytecode methods from fcode methods.
- * It encapsulates lot of nitty details like when to include an implicit
- * self paramater, etc.
- */
- public class FMethodEmit
- implements EmitConst
- {
- //////////////////////////////////////////////////////////////////////////
- // Constructor
- //////////////////////////////////////////////////////////////////////////
- /**
- * Constructor
- */
- public FMethodEmit(FTypeEmit emit, FMethod method)
- {
- this.emit = emit;
- this.method = method;
- this.code = method.code;
- this.name = method.name;
- this.jflags = FTypeEmit.jflags(method.flags);
- this.paramLen = method.paramCount;
- this.isStatic = (method.flags & FConst.Static) != 0;
- this.isCtor = (method.flags & FConst.Ctor) != 0;
- this.isNative = (method.flags & FConst.Native) != 0;
- this.ret = emit.pod.typeRef(method.inheritedRet); // we don't actually use Java covariance
- this.selfName = emit.selfName;
- this.lineNum = method.attrs.lineNum;
- }
- /**
- * Constructor
- */
- public FMethodEmit(FTypeEmit emit)
- {
- this.emit = emit;
- }
- //////////////////////////////////////////////////////////////////////////
- // Emit
- //////////////////////////////////////////////////////////////////////////
- /**
- * Emit a standard instance/static class method.
- */
- public MethodEmit emitStandard()
- {
- // emit method
- MethodEmit main = doEmit();
- // emit param default wrappers
- emitWrappers(main);
- return main;
- }
- /**
- * Emit a constructor - constructors get created as a static
- * factory methods, so that that CallNew can just push args
- * and invoke them
- * fan:
- * class Foo { new make(Int? a) { ... } }
- * java:
- * static Foo make(Long a) { return make$(new Foo(), a) }
- * static Foo make$(Foo self, Long a) { ... return self }
- *
- * We call the first method "make" the "factory" and the
- * second method "make$" the "body". CallNew opcodes are
- * routed to the ctor factory, and CallCtor opcodes are routed
- * to the ctor body.
- */
- public MethodEmit emitCtor()
- {
- String ctorName = this.name;
- // both factory and body are static from Java's perspective
- this.jflags |= STATIC;
- this.isStatic = true;
- // first emit the body with implicit self
- this.name = ctorName + "$";
- this.self = true;
- MethodEmit body = doEmit();
- // emit body default parameter wrappers
- MethodEmit[] wrappers = emitWrappers(body);
- // then emit the factory
- this.name = ctorName;
- this.self = false;
- this.ret = emit.pod.typeRef(emit.type.self);
- this.code = null;
- int init = emit.method(selfName+ ".<init>()V");
- MethodEmit factory = emitCtorFactory(init, body, method.paramCount);
- // emit separate factory for each method with default
- // parameters: note each factory allocs the object and
- // passes it to the make$ wrapper; we rely on the make$
- // wrappers to do default params because the factory's
- // signature doesn't match up to what the Fantom compiler
- // generated (ctor assumes local_0 is this pointer)
- for (int i=0; i<method.paramCount; ++i)
- if (method.vars[i].def != null)
- emitCtorFactory(init, wrappers[i], i);
- // return static factory as our primary Java method
- return factory;
- }
- /**
- * Emit the factory part of the constructor:
- * fan:
- * new make(Int a, Obj? b := null) {...}
- * java:
- * static Foo make(long a) { return make$(new Foo(), a) }
- * static Foo make(long a, Object b) { return make$(new Foo(), a, b) }
- */
- private MethodEmit emitCtorFactory(int init, MethodEmit body, int paramLen)
- {
- this.paramLen = paramLen;
- MethodEmit factory = doEmit();
- CodeEmit code = factory.emitCode();
- code.op2(NEW, emit.cls(selfName));
- code.op(DUP);
- code.op2(INVOKESPECIAL, init);
- code.op(DUP);
- code.maxLocals = pushArgs(code, false, paramLen);
- code.maxStack = code.maxLocals + 2;
- code.op2(INVOKESTATIC, body.ref());
- code.op(ARETURN);
- code.emitLineNumber(lineNum);
- return factory;
- }
- /**
- * Emit a Fantom constructor for a class which subclasses from
- * a normal Java class brought into the Fantom type system via FFI.
- * In this case the superclass constructor is a real constructor
- * so we need to emit the code differently:
- * fan:
- * class Foo { new make(String a) { ... } }
- * java:
- * static Foo make(String a) { return new Foo(a) }
- * Foo(String a) { ... }
- */
- public MethodEmit emitCtorWithJavaSuper()
- {
- String ctorName = this.name;
- // first emit the body as a true Java constructor
- this.name = "<init>";
- MethodEmit body = doEmit();
- // emit body default parameter wrappers
- emitWrappers(body);
- // then emit the factory
- this.name = ctorName;
- this.self = false;
- this.ret = emit.pod.typeRef(emit.type.self);
- this.code = null;
- this.jflags |= STATIC;
- this.isStatic = true;
- MethodEmit factory = doEmit();
- CodeEmit code = factory.emitCode();
- code.op2(NEW, emit.cls(selfName));
- code.op(DUP);
- code.maxLocals = pushArgs(code, false, method.paramCount) + 1;
- code.maxStack = code.maxLocals + 2;
- code.op2(INVOKESPECIAL, body.ref());
- code.op(ARETURN);
- code.emitLineNumber(lineNum);
- // emit factory default parameter wrappers
- emitWrappers(factory);
- return factory;
- }
- /**
- * Emit a native method
- */
- public MethodEmit emitNative()
- {
- // emit an empty method
- this.code = null;
- MethodEmit main = doEmit();
- // emit code which calls the peer
- CodeEmit code = main.emitCode();
- if (isStatic)
- {
- int peerMethod = emit.method(selfName + "Peer." + name + sig);
- code.maxLocals = pushArgs(code, false, paramLen);
- code.maxStack = Math.max(code.maxLocals, 2);
- code.op2(INVOKESTATIC, peerMethod);
- }
- else
- {
- // generate peer's signature with self
- this.self = true;
- String sig = signature();
- this.self = false;
- int peerMethod = emit.method(selfName + "Peer." + name + sig);
- code.op(ALOAD_0);
- code.op2(GETFIELD, emit.peerField.ref());
- code.maxLocals = pushArgs(code, true, paramLen) + 1;
- code.maxStack = Math.max(code.maxLocals, 2);
- code.op2(INVOKEVIRTUAL, peerMethod);
- }
- code.op(FCodeEmit.returnOp(ret));
- // emit default parameter wrappers
- emitWrappers(main);
- return main;
- }
- /**
- * Emit the method as a mixin interface
- */
- public void emitMixinInterface()
- {
- // we only emit instance methods in the interface
- if (isStatic || isCtor) return;
- // set abstract/public flags and clear code
- this.jflags |= (ABSTRACT | PUBLIC);
- this.code = null;
- // emit main
- doEmit();
- // emit a signature for each overload based on param defaults
- for (int i=0; i<method.paramCount; ++i)
- {
- if (method.vars[i].def != null)
- {
- paramLen = i;
- doEmit();
- }
- }
- }
- /**
- * Emit the method as a mixin body class which ends with $.
- */
- public void emitMixinBody()
- {
- // skip abstract methods without code
- if (method.code == null) return;
- // instance methods have an implicit self
- if (!isStatic) this.self = true;
- // bodies are always public static
- this.jflags |= (STATIC | PUBLIC);
- // emit main body
- MethodEmit main = doEmit();
- // emit param default wrappers
- emitWrappers(main);
- }
- /**
- * Emit a mixin router from a class to the mixin body methods.
- */
- public void emitMixinRouter(Method m)
- {
- String parent = "fan/" + m.parent().pod().name() + "/" + m.parent().name();
- String name = m.name();
- int jflags = emit.jflags(m.flags()) | PUBLIC | SYNTHETIC;
- List params = m.params();
- int paramCount = params.sz();
- // find first param with default value
- int firstDefault = paramCount;
- for (int i=0; i<paramCount; ++i)
- if (((Param)params.get(i)).hasDefault())
- { firstDefault = i; break; }
- // generate routers
- for (int i=firstDefault; i<=paramCount; ++i)
- {
- String mySig = signature(m, null, i);
- String implSig = signature(m, parent, i);
- MethodEmit me = emit.emitMethod(name, mySig, jflags);
- CodeEmit code = me.emitCode();
- code.op(ALOAD_0); // push this
- int jindex = 1;
- for (int p=0; p<i; ++p)
- {
- // push args
- Param param = (Param)m.params().get(p);
- jindex = FCodeEmit.loadVar(code, FanUtil.toJavaStackType(param.type()), jindex);
- }
- code.op2(INVOKESTATIC, emit.method(parent + "$." + name + implSig));
- code.op(FCodeEmit.returnOp(FanUtil.toJavaStackType(m.returns())));
- code.maxLocals = jindex;
- code.maxStack = jindex+1; // leave room for wide return
- // use line number of class header
- code.emitLineNumber(emit.lineNum);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // Param Default Wrappers
- //////////////////////////////////////////////////////////////////////////
- /**
- * Emit wrappers, return wrapper methods indexed by param length or null.
- */
- private MethodEmit[] emitWrappers(MethodEmit main)
- {
- // change flags so that defaults aren't abstract
- int oldFlags = this.jflags;
- this.jflags = jflags & ~ABSTRACT;
- // handle generating default param wrappers
- MethodEmit[] wrappers = null;
- for (int i=0; i<method.paramCount; ++i)
- if (method.vars[i].def != null)
- {
- if (wrappers == null) wrappers = new MethodEmit[method.paramCount];
- wrappers[i] = emitWrapper(main, i);
- }
- this.paramLen = method.paramCount;
- this.jflags = oldFlags;
- return wrappers;
- }
- /**
- * Emit wrapper.
- */
- private MethodEmit emitWrapper(MethodEmit main, int paramLen)
- {
- // use explicit param count, and clear code
- this.paramLen = paramLen;
- this.code = null;
- // emit code
- CodeEmit code = doEmit().emitCode();
- // push arguments passed thru
- pushArgs(code, !(isStatic && !self), paramLen);
- // emit default arguments
- FCodeEmit.Reg[] regs = FCodeEmit.initRegs(emit, isStatic & !isCtor, method.vars);
- for (int i=paramLen; i<method.paramCount; ++i)
- {
- FCodeEmit e = new FCodeEmit(emit, method.vars[i].def, code, regs, emit.pod.typeRef(method.ret));
- e.emit();
- }
- code.maxStack = code.maxLocals + 4; // leave room for wide return or type literal loading and null coercion
- // call master implementation
- code.op2((main.flags & STATIC) != 0 ? INVOKESTATIC : INVOKEVIRTUAL, main.ref());
- // return
- code.op(FCodeEmit.returnOp(ret));
- return me;
- }
- //////////////////////////////////////////////////////////////////////////
- // Emit
- //////////////////////////////////////////////////////////////////////////
- /**
- * This is the method that all the public emitX methods
- * route to once everything is setup correctly.
- */
- protected MethodEmit doEmit()
- {
- this.sig = signature();
- this.me = emit.emitMethod(name, sig, jflags);
- if (code != null)
- {
- new FCodeEmit(emit, method, me.emitCode()).emit();
- }
- return this.me;
- }
- //////////////////////////////////////////////////////////////////////////
- // Signature Utils
- //////////////////////////////////////////////////////////////////////////
- /**
- * Generate the java method signature base on our current setup.
- */
- private String signature()
- {
- StringBuilder sig = new StringBuilder();
- // params (with optional implicit self)
- sig.append('(');
- if (self) sig.append('L').append(selfName).append(';');
- for (int i=0; i<paramLen; ++i)
- emit.pod.typeRef(method.vars[i].type).jsig(sig);
- sig.append(')');
- // return
- ret.jsig(sig);
- return sig.toString();
- }
- /**
- * Generate a method signature from a reflection sys::Method.
- */
- private String signature(Method m, String self, int paramLen)
- {
- StringBuilder sig = new StringBuilder();
- // params
- sig.append('(');
- if (self != null) sig.append('L').append(self).append(';');
- for (int i=0; i<paramLen; ++i)
- {
- Param param = (Param)m.params().get(i);
- sig.append(FanUtil.toJavaMemberSig(param.type()));
- }
- sig.append(')');
- // return
- sig.append(FanUtil.toJavaMemberSig(m.inheritedReturns()));
- return sig.toString();
- }
- //////////////////////////////////////////////////////////////////////////
- // Code Utils
- //////////////////////////////////////////////////////////////////////////
- /**
- * Push the specified number of arguments onto the stack.
- */
- private int pushArgs(CodeEmit code, boolean self, int count)
- {
- int jindex = 0;
- if (self) { code.op(ALOAD_0); ++jindex; }
- for (int i=0; i<count; ++i)
- {
- FTypeRef var = emit.pod.typeRef(method.vars[i].type);
- jindex = FCodeEmit.loadVar(code, var.stackType, jindex);
- }
- return jindex;
- }
- //////////////////////////////////////////////////////////////////////////
- // Fields
- //////////////////////////////////////////////////////////////////////////
- FTypeEmit emit; // parent type class emitter
- FMethod method; // fan method info
- FBuf code; // code to emit
- String name; // method name
- int jflags; // java flags
- boolean isStatic; // are we emitting a static method
- boolean isCtor; // are we emitting a constructor
- boolean isNative; // are we emitting a native method
- FTypeRef ret; // java return sig
- boolean self; // add implicit self as first parameter
- String selfName; // class name for self if self is true
- int paramLen; // number of parameters to use
- String sig; // last java signature emitted
- MethodEmit me; // last java method emitted
- int lineNum; // line number of method (or zero)
- }