Skip to content
Browse files

Compiler+genclass: emit jdk8 classfiles

Enabling JDK7+ bytecode requires stack map frames to be
emitted. ASM generates these automatically when given
a flag.

To enable faster single pass verification, java uses
stack map frames, which record stack contents at
just before the first instruction at a target branch.
The verifier easily tracks stack content within a basic

ASM requires that you override getCommonSuperClass(t1, t2)
as its default implementation does classloading and does not
work for Clojure.

If you have code like so:
 (or (seq coll) ())

immediately before :foo control flow merges with two different
types on the stack depending on the result of the or branch.
ASM calls getCommonSuperClass() to record into the stack map frame.

Luckily, because Clojure emits checkcasts conservatively before subsequent
calls, we do not have to provide maximally specific answers to
getCommonSuperClass(), instead we can return Object every time.

We may want to revisit this when LWorld value types happen, or when
generic specialization is available.

Signed-off-by: Stuart Halloway <>
  • Loading branch information...
ghadishayban authored and stuarthalloway committed Oct 11, 2016
1 parent 3d053b1 commit 38705b49fd3dbae11e94c576ef49ff3eb1c47395
Showing with 24 additions and 11 deletions.
  1. +4 −4 src/clj/clojure/genclass.clj
  2. +20 −7 src/jvm/clojure/lang/
@@ -133,7 +133,7 @@
interfaces (map the-class implements)
supers (cons super interfaces)
ctor-sig-map (or constructors (zipmap (ctor-sigs super) (ctor-sigs super)))
cv (new ClassWriter (. ClassWriter COMPUTE_MAXS))
cv (clojure.lang.Compiler/classWriter)
cname (. name (replace "." "/"))
pkg-name name
impl-pkg-name (str impl-ns)
@@ -254,7 +254,7 @@
(. gen (endMethod))))
;start class definition
(. cv (visit (. Opcodes V1_5) (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_SUPER))
(. cv (visit (. Opcodes V1_8) (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_SUPER))
cname nil (iname super)
(when-let [ifc (seq interfaces)]
(into-array (map iname ifc)))))
@@ -661,8 +661,8 @@
(IllegalArgumentException. "Interface methods must not contain '-'")))
(let [iname (.replace (str name) "." "/")
cv (ClassWriter. ClassWriter/COMPUTE_MAXS)]
(. cv visit Opcodes/V1_5 (+ Opcodes/ACC_PUBLIC
cv (clojure.lang.Compiler/classWriter)]
(. cv visit Opcodes/V1_8 (+ Opcodes/ACC_PUBLIC
iname nil "java/lang/Object"
@@ -4295,12 +4295,12 @@ void compile(String superName, String[] interfaceNames, boolean oneTimeUse) thro
//with name current_ns.defname[$letname]+
//anonymous fns get names fn__id
//derived from AFn/RestFn
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassWriter cw = classWriter();
// ClassWriter cw = new ClassWriter(0);
ClassVisitor cv = cw;
// ClassVisitor cv = new TraceClassVisitor(new CheckClassAdapter(cw), new PrintWriter(System.out));
//ClassVisitor cv = new TraceClassVisitor(cw, new PrintWriter(System.out));
cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, internalName, null,superName,interfaceNames);
cv.visit(V1_8, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, internalName, null,superName,interfaceNames);
// superName != null ? superName :
// (isVariadic() ? "clojure/lang/RestFn" : "clojure/lang/AFunction"), null);
String source = (String) SOURCE.deref();
@@ -7655,9 +7655,9 @@ public static Object compile(Reader rdr, String sourcePath, String sourceName) t

objx.objtype = Type.getObjectType(objx.internalName);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassWriter cw = classWriter();
ClassVisitor cv = cw;
cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER, objx.internalName, null, "java/lang/Object", null);
cv.visit(V1_8, ACC_PUBLIC + ACC_SUPER, objx.internalName, null, "java/lang/Object", null);

//static load method
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC,
@@ -7975,9 +7975,9 @@ static ObjExpr build(IPersistentVector interfaceSyms, IPersistentVector fieldSym
* Unmunge the name (using a magic prefix) on any code gen for classes
static Class compileStub(String superName, NewInstanceExpr ret, String[] interfaceNames, Object frm){
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = cw;
cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER, COMPILE_STUB_PREFIX + "/" + ret.internalName,
ClassWriter cw = classWriter();
ClassVisitor cv = cw;
cv.visit(V1_8, ACC_PUBLIC + ACC_SUPER, COMPILE_STUB_PREFIX + "/" + ret.internalName,

//instance fields for closed-overs
@@ -8950,4 +8950,17 @@ public Expr parse(C context, Object frm) {

static IPersistentCollection emptyVarCallSites(){return PersistentHashSet.EMPTY;}

static public ClassWriter classWriter() {
return new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES) {
protected String getCommonSuperClass (final String type1, final String type2) {
return "java/lang/Object";
// if (!(type1.equals("java/lang/Object") || type2.equals("java/lang/Object"))) {
// RT.errPrintWriter()
// .format("stack map frame \"%s\" and \"%s\" on %s:%d:%d \n",
// type1, type2,
// SOURCE_PATH.deref(), LINE.deref(), COLUMN.deref());
// }

0 comments on commit 38705b4

Please sign in to comment.
You can’t perform that action at this time.