This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-02-21
Channels
- # announcements (9)
- # babashka (45)
- # beginners (45)
- # calva (1)
- # clojure (40)
- # clojure-austin (16)
- # clojure-europe (16)
- # clojure-nl (1)
- # clojure-norway (35)
- # clojure-uk (4)
- # clojurescript (11)
- # conjure (3)
- # cursive (4)
- # datalevin (14)
- # datavis (1)
- # datomic (8)
- # emacs (6)
- # hyperfiddle (7)
- # introduce-yourself (3)
- # joyride (17)
- # missionary (16)
- # off-topic (2)
- # pedestal (9)
- # polylith (27)
- # re-frame (7)
- # reitit (1)
- # releases (1)
- # shadow-cljs (17)
- # sql (17)
- # tools-build (19)
- # tools-deps (15)
- # xtdb (15)
Hello everyone, Does anyone already played with https://github.com/jgpc42/insn and java bytecode generation in general? I’m stuck with a problem and would like some help on this. Thanks in advance
hi Chrisn, how is doing? Thanks for reply. Basically I am trying to implement a basic recursive function, here is the code I’m playing right now:
(def test-data
{:name "my.pkg.Test.Test123",
:flags [:public],
:fields [{:flags [:public :static], :name "y", :type "my.pkg.Test.Println"}],
:methods
[{:name :clinit
:emit [[:new "my.pkg.Test.Println"]
[:dup]
[:invokespecial "my.pkg.Test.Println" :init [:void]]
[:putstatic :this "y" "my.pkg.Test.Println"]
[:return]]}
{:flags #{:public :static},
:name "invoke",
:desc [:long :void],
:emit[
[:lload 0]
[:ldc2 10]
[:lcmp]
[:ifge "LABEL1"]
[:getstatic :this "y" "my.pkg.Test.Println"]
[:astore 2]
[:aload 2]
[:lload 0]
[:invokestatic java.lang.Long "valueOf" [:long Long]]
[:invokevirtual "my.pkg.Test.Println" "invoke" [java.lang.Object Object]]
[:lload 0]
[:lconst-1]
[:ladd]
[:invokestatic java.lang.Long "valueOf" [:long Long]]
[:invokestatic "my.pkg.Test.Test123" "invoke" ]
[:mark "LABEL1"]
[:return]]}]})
But It gives me this:
Inconsistent stackmap frames at branch target 12 Exception Details: Location:
my/pkg/Test/Test123.invoke(J)V @8: goto Reason: Current frame's stack size
doesn't match stackmap. Current Frame: bci: @8 flags: { } locals: { long,
long_2nd } stack: { } Stackmap Frame: bci: @12 flags: { } locals: { long,
long_2nd } stack: { null } Bytecode: 0000000: 1e14 0007 949c 0006 a700 0401
b1 Stackmap Table: same_frame(@11) same_locals_1_stack_item_frame(@12,Null)
I successfully implemented it without use my ‘custom/external’ classes (like my.pkg.Test.Println
). But when I try to introduce it I cannot make it work.https://github.com/athos/power-dot this maybe? @U0508956F is this still ok?
but thanks anyway 🙂
I will check
I tend to just use the stripped down vendored copy of asm that clojure ships with (annoying everyone because that is "private") https://git.sr.ht/~hiredman/tagbody/tree/master/item/src/com/manigfeald/tagbody.clj
the error you are getting is because when control flow meets (jump targets) all the execution paths that lead to that target need to result in the same stack state (same number of elements on the stack and same types of elements)
the error message has stack: { }
and stack: { null }
which means on one path the stack is empty and on the other path the stack has a single element which is null
I have caused the same error messing around with the Clojure compiler - specifically attempting to get try/catch to compiler directly into the parent method when not in a tail position - I never figured out exactly how to deal with it sanely but that description is superb - @U0NCTKEV8 you have mentioned a few things like this that really show a great understanding of this part of the JVM. This specific error can happen with the vendored ASM just as well although I thought ASM has some verification check you could turn on at generation time - enabling this check would be helpful if it exists. Especially since in my case it happened in a eval'ed for
macro making seeing what is happening from the code impossible or nearly so.
Specifically one thing I would like but hadn't figured out was how to map those instruction indicators bci @8
, etc. to the specific instruction to look at which according to your explanation the branches come together at the mark at the bottom and there is nothing on the stack after the comparison just before and null on the stack just before the mark
. Do you know a good way to go from the exception message all the way to some symbolic asm representation with the indicators highlighted or even just labeled?
I don't, I really don't do stuff with bytecode that often, and when I have hit that error I usually don't even read it, just automatically "oh the verifier complained, better put a pop somewhere"
Thanks guys, I’m trying to compile a minor version, but still with issues (even trying to ‘pop’ in different places) . (I make it works using ifeq
instruction). Here the failed example:
(def test-data
{:name "my.pkg.Test.Test",
:flags [:public],
:fields [],
:methods
[{:flags #{:public},
:name "invoke",
:desc [java.lang.Long :void],
:emit
[[:ldc2 10]
[:invokestatic java.lang.Long "valueOf" [:long Long]]
[:ldc2 100]
[:invokestatic java.lang.Long "valueOf" [:long Long]]
[:invokestatic "clojure.lang.Numbers" "gt" [Object Object boolean]]
[:getstatic "java.lang.Boolean" "FALSE"]
[:if-acmpeq "label1"]
[:ldc "Hello"]
[:goto "end"]
[:mark "label1"]
[:ldc "Bye"]
[:mark "end"]
[:return]]}]})
I am not sure if the unconditional [:goto "end"]
changes the verifiers requirements for the stack state at [:mark "label1"]
needing to be the same for both paths through [:if-acmpeq "label1"]
what is the error from the verifier?
here is the error:
1. Unhandled java.lang.VerifyError
Bad type on operand stack Exception Details: Location:
my/pkg/Test/Test.invoke(Ljava/lang/Long;)V @18: if_acmpeq Reason: Type
integer (current frame, stack[0]) is not assignable to reference type Current
Frame: bci: @18 flags: { } locals: { 'my/pkg/Test/Test', 'java/lang/Long' }
stack: { integer, 'java/lang/Boolean' } Bytecode: 0000000: 1400 07b8 000e
1400 0fb8 000e b800 16b2 0000010: 001c a500 0812 1ea7 0005 1220 b1 Stackmap
I would try again with just a different class name if you are loading these classes live, I don't see that condition arising from the above definition, but if you tried a different definition previously you can get caching behaviors from classloaders
hum, interesting, good point, let me play with different names so
no success, I will continue to play with it, lets see.
@U0NCTKEV8 when you said
> [:mark "label1"]
needing to be the same for both paths through [:if-acmpeq "label1"]
You mean, something like it ?
(def test-data
{:name "my.pkg.Test.Test2",
:flags [:public],
:fields []
:methods
[{:flags #{:public},
:name "invoke",
:desc [java.lang.Long :void],
:emit
[[:aload 1]
[:ldc2 100]
[:invokestatic java.lang.Long "valueOf" [:long Long]]
[:invokestatic "clojure.lang.Numbers" "gt" [Object Object boolean]]
[:getstatic "java.lang.Boolean" "FALSE"]
[:if-acmpeq "label1"]
[:ldc "Hello"]
[:goto "label1"]
[:ldc "Hello"]
[:mark "label1"]
[:return]]}]})
I meant more like if the verifier just ignores the unconditional nature of the goto so you would need a pop after it, but I don't see how that would lead to the verifier complaining about an int and a Boolean on the stack
yes, I see, weird. But Thanks for your time, I will continue exploring, if it works, I will post here 🙂
My silly contrib for today, I found surprisingly few results for this handy pattern https://grep.app/search?q=%40%28def%20&filter[lang][0]=Clojure
It's useful for comment
forms or other REPLing.

well most repl-ing is not checked in, so maybe not that surprising
What is it doing? What kind of magic is it? 😂
def
returns the var that was defined, and @
deref's the var to get the value that was just defined. Does that make sense?
so in the core.typed
example
#_(deftest def-syntax-test
(is (= @(def a 1)
@(t/def a 1)
is testing that when t/def
and def
are used they both return something that can be derefed to 1
Makes sense! I’ve forgotten vars can be derefed .
This is great, I was desiring something like this last night. Had never considered deref'ing a var in this fashion. Thank you for this.
What's the reason def
returns a var instead of the value anyway? REPL experience would probably be nicer with value, but I guess there's ... reasons?
returning a lazy value does nothing. evaluating a lazy value should be handled well by REPL tooling
def
is the usual way to create a var. It seems consistent to me. (atom 1)
returns an atom, not 1
. I realise that you've just created a known name for the var, but idk, maybe it's useful to have the container returned by the call you've created it with? Maybe it helps with debugging code loading sometimes?