Fork me on GitHub
#clojure
<
2020-12-14
>
dpsutton05:12:17

anyone ever seen > Syntax error (VerifyError) compiling new at (/task/sync_databases_test.clj:143:1). > Operand stack underflow

1
dpsutton05:12:44

Syntax error (VerifyError) compiling new at (********/task/sync_databases_test.clj:143:1).
Operand stack underflow
Exception Details:
  Location:
    ********/task/sync_databases_test/MockJobExecutionContext.put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; @8: areturn
  Reason:
    Attempt to pop empty stack.
  Current Frame:
    bci: @8
    flags: { }
    locals: { '********/task/sync_databases_test/MockJobExecutionContext', 'java/lang/Object', 'java/lang/Object' }
    stack: { }
  Bytecode:
    0000000: 2a2b 2cb9 017e 0300 b0                 


Full report at:
/tmp/clojure-18430136797529807953.edn
Tests failed.

dpsutton05:12:22

a PR removed the jvm option "-Xverify:none" and this was the result

dpsutton06:12:39

tests passed on java 8 image but broke with this error on java 11

dpsutton06:12:00

> Running with these options enables execution of Java programs that violate the Java Virtual Machine Specification. hmm. i'm hoping we're not purposefully emitting programs that violate the jvm spec

hiredman07:12:56

Hard to say without more of the stack trace to see where the method is being called but my guess is one of the interfaces you are pulling into the mockjobexecutioncontext is also defining a put method (JobExecutionContext) and defrecord implicitly defines put method (as a map) and something is getting confused about which method is being called

hiredman08:12:13

You might try using deftype instead of defrecord there

hiredman08:12:13

method resolution is slightly different between java8 and post java9, which could explain the put method confusion passing on java 8 but failing on java 11

ghadi13:12:00

I would really like to see the clojure code that produced a verifier error. (Shame on lein for disabling verification)

dpsutton14:12:19

The verification was an explicit Arg we added here. This is another time I’ve been bitten by the stacktrace thrown into a random temp file on a machine in the cloud. Not a huge fan of that default

alexmiller14:12:06

Disabling the verifier is a bad idea, there is no good reason to do that (which is why you can’t in newer Java’s)

dpsutton14:12:45

yeah. someone added this PR for exactly that reason. it doesn't work going forward and is deprecated in 13+ i think

alexmiller14:12:04

If you can boil this into a small repro, would definitely like to see an ask question or jira ticket

dpsutton14:12:08

i just added the option to print the stack trace to stderr so maybe that will give a bit more info

dpsutton14:12:18

if i can get it there i absolutely will

dpsutton14:12:39

#error {
 :cause "Operand stack underflow\nException Details:\n  Location:\n    metabase/task/sync_databases_test/MockJobExecutionContext.put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; @8: areturn\n  Reason:\n    Attempt to pop empty stack.\n  Current Frame:\n    bci: @8\n    flags: { }\n    locals: { 'metabase/task/sync_databases_test/MockJobExecutionContext', 'java/lang/Object', 'java/lang/Object' }\n    stack: { }\n  Bytecode:\n    0000000: 2a2b 2cb9 017e 0300 b0                 \n"
 :via
 [{:type clojure.lang.Compiler$CompilerException
   :message "Syntax error compiling new at (/Users/dan/projects/clojure/metabase/test/metabase/task/sync_databases_test.clj:143:1)."
   :data #:clojure.error{:phase :compile-syntax-check, :line 143, :column 1, :source "/Users/dan/projects/clojure/metabase/test/metabase/task/sync_databases_test.clj", :symbol new}
   :at [clojure.lang.Compiler analyzeSeq "Compiler.java" 7115]}
  {:type java.lang.VerifyError
   :message "Operand stack underflow\nException Details:\n  Location:\n    metabase/task/sync_databases_test/MockJobExecutionContext.put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; @8: areturn\n  Reason:\n    Attempt to pop empty stack.\n  Current Frame:\n    bci: @8\n    flags: { }\n    locals: { 'metabase/task/sync_databases_test/MockJobExecutionContext', 'java/lang/Object', 'java/lang/Object' }\n    stack: { }\n  Bytecode:\n    0000000: 2a2b 2cb9 017e 0300 b0                 \n"
   :at [java.lang.Class getDeclaredConstructors0 "Class.java" -2]}]
 :trace
 [[java.lang.Class getDeclaredConstructors0 "Class.java" -2]
  [java.lang.Class privateGetDeclaredConstructors "Class.java" 3138]
  [java.lang.Class getConstructors "Class.java" 1944]
  [clojure.lang.Compiler$NewExpr <init> "Compiler.java" 2575]
  [clojure.lang.Compiler$NewExpr$Parser parse "Compiler.java" 2667]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7107]
  [clojure.lang.Compiler analyze "Compiler.java" 6789]
  [clojure.lang.Compiler analyze "Compiler.java" 6745]
  [clojure.lang.Compiler$BodyExpr$Parser parse "Compiler.java" 6120]
  [clojure.lang.Compiler$FnMethod parse "Compiler.java" 5467]
  [clojure.lang.Compiler$FnExpr parse "Compiler.java" 4029]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7105]
  [clojure.lang.Compiler analyze "Compiler.java" 6789]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7095]
  [clojure.lang.Compiler analyze "Compiler.java" 6789]
  [clojure.lang.Compiler access$300 "Compiler.java" 38]
  [clojure.lang.Compiler$DefExpr$Parser parse "Compiler.java" 596]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7107]
  [clojure.lang.Compiler analyze "Compiler.java" 6789]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7095]
  [clojure.lang.Compiler analyze "Compiler.java" 6789]
  [clojure.lang.Compiler analyze "Compiler.java" 6745]
  [clojure.lang.Compiler$BodyExpr$Parser parse "Compiler.java" 6118]
  [clojure.lang.Compiler$LetExpr$Parser parse "Compiler.java" 6436]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7107]
  [clojure.lang.Compiler analyze "Compiler.java" 6789]
  [clojure.lang.Compiler analyze "Compiler.java" 6745]
  [clojure.lang.Compiler$BodyExpr$Parser parse "Compiler.java" 6120]

dpsutton14:12:51

it seems @U0NCTKEV8’s guess about deftype versus defrecord was correct. that's some amazing bytecode intuition

dpsutton05:12:29

> Attempt to pop empty stack.

zerusski12:12:12

Hi. Is there an interface lower level than ILookup? I want to "proxy" lookups to a record field but can't figure out how to get record fields once I overwrite the ILookup interface for the record. So something like this:

(defrecord foo [id map]
  ILookup
  (valAt [o key] 
    (or (get o key) 
        (get-in o [map key])))
I'm guessing get and get-in here would create an infinite lookup loop. Is there a way to do that? Thanks

zerusski13:12:43

Was trying to figure out what defrecord does in the impl of ILookup it generates and I absolutely do not understand that magic. Essentially it amounts to (get __extmap key) and I can't even figure out how this magical __extmap gets into scope (a global or some special runtime thing?) and this isn't even used. Confused

valerauko13:12:42

I think you just call the java .get on the map your record closes on? Potemkin has plenty of magic like this https://github.com/clj-commons/potemkin/blob/master/src/potemkin/collections.clj I also saw pohjavirta do something similar too. https://github.com/metosin/pohjavirta/blob/master/src/pohjavirta/request.clj

zerusski13:12:52

problem isn't in delegation - it'll work out of the box since record field value would probably implement its own ILookup. Problem is obtaining record field value in the first place since we are overriding this record's interface

zerusski13:12:41

I'll have a look in those libs. Thank you fro pointers!

zerusski13:12:22

Potemkin is cheating by rolling out its own type abstraction. I'd rather not go there

alexmiller14:12:00

If you are changing lookup semantics you’re already there

alexmiller14:12:08

This is what deftype is for

zerusski14:12:52

yeah that's what I figured. IIUC I'll have to extend all the usual interfaces the moment I switch to deftype. Fair enough

zerusski14:12:14

I ended up solving the problem I had differently without proxying, cause in this case it lended itself to this other solution (typical - 9 out of 10 times you don't need special non Clojure sematics). But I still hoped that there was a simpler way to do such delegation.

valerauko15:12:03

The extmap is just fallback (when you assoc in extra stuff), defined fields go the mapcat's way which I think closes over the values https://github.com/clojure/clojure/blob/38bafca9e76cd6625d8dce5fb6d16b87845c8b9d/src/clj/clojure/core_deftype.clj#L194-L195

user=> (defrecord Bar [a])
user.Bar
user=> (.__extmap (assoc (->Bar 12) :c 42))
{:c 42}

Frankline Apiyo13:12:56

are there any advantages/disadvantages of using clojure.test over midje. please feel free to share articles that can help me answer this question

p-himik13:12:10

By Sean Corfield, on this Slack some time ago: > I personally would also recommend not using Midje. It's considered non-idiomatic and drags in a lot of transitive dependencies that can be problematic. > Just use plain old clojure.test > Midje isn't compatible with any standard test runners or test tooling.

borkdude13:12:31

<3 clojure.test despite its shortcomings

borkdude13:12:23

my projects run with both the cognitest test runner and leiningen, so you get the benefit of using more standard tools as well

Frankline Apiyo11:12:03

I feel like when it comes to mocking/stubbing, clojure.test is falling short. is midje better at this?

borkdude11:12:53

@USWTQB9RU You can consider 1) dynamic vars to swap out existing functions or make tests more flexible, 2) with-redefs (watch out, not thread-safe)

Frankline Apiyo11:12:36

With option 1) wouldn't I have to re-write the functions I would like to test to use dynamic vars to reference the functions I would like to stub?

Frankline Apiyo11:12:56

oh boy! that doesn't sound very nice 😄

borkdude11:12:34

sometimes it's worth it. E.g. if you're testing a function that does #(System/exit x) that's pretty much your only option. Or use with-redefs.

borkdude12:12:07

Every other mocking technique probably uses this behind the covers anyway

Frankline Apiyo12:12:55

the problem I've found with using with-redefs is that the tests will not tell you when the stubbed function was not called with the correct/expected arguments; unless of course, you write these instructions into your stub. so, you may have to add this logic to every stub or write some kind of macro to do this. But midje does it out of the box!

Frankline Apiyo12:12:58

I'm starting to want to use midje. I just want you guys to check the math on why I'm about to use midje 😆

borkdude12:12:57

you have to write the expectations somewhere > unless of course, you write these instructions into your stub that would be the logical place to me.

borkdude12:12:14

it's a choice between code that's easy to reason about or some black box testing lib you will depend on

borkdude12:12:37

> clojure.test is simpler and less magical, with a larger ecosystem of tools and plugins. this is why I use clojure.test

borkdude12:12:47

feel free to make your own choices :)

Frankline Apiyo12:12:20

here's a GIF from a show I enjoy ☝️ . Thank you very much.

borkdude12:12:14

haha. which show is this?

Felix M15:12:36

Hi, what my options for sprinkling a few reactive components in a server side rendered Clojure app without going full SPA, is this doable with Rum? I'm looking to have form inputs filter the the db and return the results client side without incurring a full page refresh. #beginners #programming-beginners

noisesmith16:12:05

you might get better answers in #clojurescript but I haven't seen anyone talk about using rum in years

👍 1
erwinrooijakkers18:12:38

@UG9U7TPDZ had started building a server side rendered reactive application using Reagent at https://github.com/kwrooijen/yggdrasil. But this is not yet production ready I think, but the idea of server side rendering but having Reagent at your disposal (and perhaps even connection with the backend) seems interesting and useful to me. Not sure if there are any other solutions for this

kwrooijen18:12:32

And no documentation 😄

Felix M21:12:15

@U2PGHFU5U Thanks, I'll check it out

msolli06:12:43

@U01GD44GX2P This is doable in Rum, and exactly why I use it. I build a SaaS-type web app, where all the pages are HTML pages served by Rum components in .cljc files. Some are completely static, while others have quite complex behavior.

Felix M16:12:28

@U06BEJGKD thanks, that's exactly the type of set-up I had in mind. Glad to hear its being used successfully.

erwinrooijakkers18:12:10

I am trying to create a Clojure Google Cloud Function and I have to specify the entrypoint of a leiningen console app (via lein create new app hello-world):

$ gcloud functions deploy jar-example --entry-point=hello_world_core$_main --runtime=java11 --trigger-http --source=target/uberjar
This entry-point is not correct. / is not allowed. Any suggestions on how to specify the entry-point correctly?

mozinator217:12:11

There are messages in the #google-cloud channel on how to do this. You need to create a Java file as a wrapper

noisesmith18:12:17

it should be hello_world.core$_main

🙏 1
hiredman19:12:51

hello_world.core$main or elloworld_core$_main or anything like that is almost certainly not correct

hiredman19:12:19

those are all "jvm" names for clojure functions, and the gcloud api will have no idea what to do with a clojure function

erwinrooijakkers19:12:51

Thanks! I forgot the logic, this helps 🙂. Still get an error though with --entry-point=hello_world.core$_main

Build failed: build succeeded but did not produce the class "hello_world.core" specified as the function target: Error: class not found: hello_world.core; Error ID: fb35dca7

erwinrooijakkers19:12:30

maybe bash substitution

erwinrooijakkers19:12:58

Ah and with ‘’ it’s:

Invalid value 'hello_world.core$_main': Entry point name must contain only Latin letters (lower- or upper-case), digits, dot (.) and underscore (_), and must be at most 128 characters long. It can neither begin nor end with a dot (.), nor contain two consecutive dots (..).

hiredman19:12:24

you need to look at the documentation for gcloud functions and see what kind of thing it expects as an entry-point, instead of just putting the names of random things in

hiredman19:12:24

if entry-point takes a class name, you'll need to ensure that the named class exists in the jar file, and is not a class that is generated while loading clojure code, because gcloud almost certainly doesn't know about loading clojure code

hiredman19:12:02

and if it is a class name, that class almost certainly will need at least a specific method, but likely to implement some interface