Coder Social home page Coder Social logo

jise's Introduction

JiSE: Java in S-Expression

Clojars Project CircleCI

JiSE is a Clojure DSL library that compiles a Java-like language into JVM bytecode at macroexpansion time.

Features

Using JiSE, you can:

  • Write imperative code that is compiled to JVM bytecode as efficient as written in Java
    • You can use assignment, (nested) loops, (labeled) break/continue, etc.
  • Define your own Java class in a cleaner way than using gen-class or proxy
  • Combine JiSE code with Clojure in a function- or expression-level of granularity
  • Find more errors at compile time due to its static typing
  • Extend JiSE syntax with Clojure's ordinary macros
    • Existing Clojure macros can also be used seamlessly from JiSE, such as ->, .. and with-open

Installation

Add the following to your project :dependencies:

Clojars Project

If you would rather use an unstable version of the library via Clojure CLI tool, add the following to your deps.edn instead:

{...
 :deps {...
        athos/jise {:git/url "https://github.com/athos/JiSE.git"
                    :sha "<commit sha>"}
        ...}
 ...}

Usage

(require '[jise.core :refer [defclass]])


^:public
(defclass Counter
  ^:private
  (def ^int c)
  ^:public
  (defm Counter [^int c]
    (set! (.-c this) c))
  ^:public ^int
  (defm inc []
    (inc! (.-c this))))

;; You can use the defined class (`Counter`) as an ordinary Java class
(def c (Counter. 10))
(.inc c) ;=> 11
(.inc c) ;=> 12
(.inc c) ;=> 13

;; Also, you can even write quite imperative code easily as follows:

^:public
(defclass Qsort
  ^:public ^:static
  (defm qsort [^{:tag [int]} xs]
    (qsort xs 0 (- (alength xs) 1)))

  ^:private ^:static
  (defm qsort [^{:tag [int]} xs ^int left ^int right]
    (when (< left right)
      (let [p (aget xs (/ (+ left right) 2))
            l left
            r right]
        (while (<= l r)
          (while (< (aget xs l) p) (inc! l))
          (while (> (aget xs r) p) (dec! r))
          (when (<= l r)
            (let [tmp (aget xs l)]
              (aset xs l (aget xs r))
              (aset xs r tmp)
              (inc! l)
              (dec! r))))
        (qsort xs left r)
        (qsort xs l right)))))

(def arr (int-array [3 1 4 1 5 9 2]))
(Qsort/qsort arr)
(seq arr)
;=> (1 1 2 3 4 5 9)

For a more practical example, see also AOBench code written in JiSE.

Supported Java features

  • class definition
  • inheritance & interface implementation
  • constructor definition
  • field & method definition
    • non-static fields & methods
    • static fields & static methods
    • method overloading
    • variable arity methods
  • various modifiers
    • access control (public / protected / private)
    • abstract
    • final
    • transient
    • volatile
    • synchronized
  • initializer & static initializer
  • primitive arithmetics
  • logical expressions
  • assignments
  • increments & decrements
  • conditionals (if / switch)
  • loops (while / for / enhanced for / break / continue)
  • return
  • arrays (including multi-dimensional arrays)
  • casting
  • string concatenation
  • auto-boxing & auto-unboxing
  • constructor invocation (including explicit this() / super() invocation)
  • field access
  • method invocation (including variable arity method invocation)
  • exception handling (try / catch / finally / throw)

Not supported Java features

  • interface definition
  • enum definition
  • nested class definition
    • member classes
    • local classes
    • anonymous classes
  • synchronized blocks
  • annotations
  • generics
  • lambda expressions
  • method references

License

Copyright © 2019 Shogo Ohta

This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.

This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version, with the GNU Classpath Exception which is available at https://www.gnu.org/software/classpath/license.html.

jise's People

Contributors

athos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

crhough eoliphan

jise's Issues

Unexpected use of final in emitted class

Original java:

ThreadLocalRandom tlr = ThreadLocalRandom.current();
long l1 = tlr.nextLong(), l2 = tlr.nextLong();
char[] rt = new char[22];
 rt[21] = tbl[(int)l1 & 0x3f]; l1 = l1 >>> 6;
 rt[20] = tbl[(int)l1 & 0x3f]; l1 = l1 >>> 6;
 rt[19] = tbl[(int)l1 & 0x3f]; l1 = l1 >>> 6;

Corresponding JiSE:

(let [^ThreadLocalRandom tlr (ThreadLocalRandom/current)
      ^long  l1 (.nextLong tlr)
      ^long  l2 (.nextLong tlr)
      ^chars rt (new [char] 22)]
(aset rt 21 (tbl ^int (&  l1 0x3f)))(set! l1 (>>> l1 6))
(aset rt 20 (tbl ^int (&  l1 0x3f)))(set! l1 (>>> l1 6))
(aset rt 19 (tbl ^int (&  l1 0x3f)))(set! l1 (>>> l1 6))
...
)

Inspected with clj-decompiler, using (decompile ...), gives:

final char[] array = new char[22];
final ThreadLocalRandom current = ThreadLocalRandom.current();
final long nextLong = current.nextLong();
final long nextLong2 = current.nextLong();
array[21] = RandM.tbl[(int)(nextLong & 0x3FL)];
final long n = nextLong >>> 6;
array[20] = RandM.tbl[(int)(n & 0x3FL)];
final long n2 = n >>> 6;
array[19] = RandM.tbl[(int)(n2 & 0x3FL)];
...

long variable in let is set to final. Is there a way to allow it to mutate?

Why is it so un-java?

Why does it use def, square brackets, and other vestigal clojure syntax? I can't give this to my Java friends, they'd laugh. There is a JiSE out there waiting to be discovered that they wouldn't laugh it.

Annotations?

Curious how much additional effort would be required for annotations. I am helping work through porting an example from optaplanner which is "heavily" class-based and requires lots of annotations to communicate with the framework. Most of clojure's deftype (with annotation support) worked here, however we end up with problems since the field types are "always" Object, regardless of typehint. So optaplanner will complain that the field does not return a constrained type (List or array). Looking at custom bytecode generation with insn, but I figured I would see if JiSE could be closer...

Cannot find methods defined in Object class for interface types

user=> (require '[jise.utils :as jise])
nil
user=> (jise/do (let [^java.io.Serializable s "foo"] (.toString s)))
Syntax error macroexpanding jise.core/class at (form-init7183457418990174559.clj:64:4).
Error: cannot find symbol
  symbol: method toString(no arguments)
  location: class java.io.Serializable (/private/var/folders/mr/bf82sldd78g9822yxqgzd12h0000gn/T/form-init7183457418990174559.clj:1:47)
user=>

Whereas:

user=> (jise/do (let [^String s "foo"] (.toString s)))
"foo"
user=>

This is essentially due to the fact that the current implementation of t/get-methods does not consider the Object class as a supertype of any interface type:

user=> (t/get-methods nil nil (t/tag->type 'java.io.Serializable) "toString")
nil
user=> (t/get-methods nil nil (t/tag->type 'String) "toString")
({:class #object[clojure.asm.Type 0x3d7eb9c1 "Ljava/lang/String;"],
  :interface? false,
  :param-types [],
  :return-type #object[clojure.asm.Type 0xbc97527 "Ljava/lang/String;"],
  :access #{:public}})
user=>

Allow different types for the second and third operands of conditional operator

According to JLS 15.25, typing the conditional operator never fails unless it includes an invocation of a void method. However, the current implementation throws VerifyError when the second and third operands differs in type:

user=> (defclass C ^String (defm m [^int x] (str (if (== x 0) "foo" 1))))
Syntax error (VerifyError) compiling at (REPL:1:1).
Bad type on operand stack
Exception Details:
  Location:
    user/C.m(I)Ljava/lang/String; @17: invokevirtual
  Reason:
    Type top (current frame, stack[1]) is not assignable to 'java/lang/String'
  Current Frame:
    bci: @17
    flags: { }
    locals: { 'user/C', integer }
    stack: { 'java/lang/StringBuilder', top }
  Bytecode:
    0000000: bb00 0e59 b700 0f1b 9a00 0812 11a7 0004
    0000010: 04b6 0015 b600 19b0                    
  Stackmap Table:
    same_locals_1_stack_item_frame(@16,Object[#14])
    full_frame(@17,{Object[#2],Integer},{Object[#14],Top})

user=> 

would lambdas be supported?

It'd be great to have direct lambda support instead of using the clojure.lang.IFn interface.

Is that on the roadmap and how difficult would it be to get working?

Array set example?

Hello, this is a really nice library. There's a discussion over on zulip over porting a java implementation of a method that generates random strings, where most of the path is going through char arrays. We seem to be paying an unavoidable cost in uncheckedIntCast when doing any array access with literals (since the literal numbers are always longs), where javac gets away with this. I have a repo here with the corresponding java implementation and my current attempt at porting it to JiSE, cited below. I was using both aset and aget, per some of the examples, and it looks like we're as fast as the clojure example if unchecked-math is enabled (which will emit uncheckedIntCast s for us). That still tends to be about 8% slower on my machine, worse on others (if you believe in the ns accuracy of criterium). Is there a way to ensure that the indices for aset are actually being represented as ints in the resulting class definition (such as a different array set syntax, I was unable to figure out..)? Or is the expected path to use aset/aget as I have done? I found that the direct array access is possible in the benchmark example you provided. Thanks for any insight!

^:public
(defclass Rand
  (def ^:public ^:static ^chars tbl
    (new [char] [\- \0 \1 \2 \3 \4 \5 \6 \7 \8
                 \9 \A \B \C \D \E \F \G \H \I
                 \J \K \L \M \N \O \P \Q \R \S
                 \T \U \V \W \X \Y \Z \_ \a \b
                 \c \d \e \f \g \h \i \j \k \l
                 \m \n \o \p \q \r \s \t \u \v
                 \w \x \y \z]))
  ^:public ^:static  ^String
  (defm genId []
    (let [^ThreadLocalRandom tlr (ThreadLocalRandom/current)
          ^long  l1 (.nextLong tlr)
          ^long  l2 (.nextLong tlr)
          ^chars rt (new [char] 22)]
      (aset rt 21 (tbl ^int (&  l1 0x3f)))
      (aset rt 20 (tbl ^int (&  (>>> l1 6) 0x3f)))
      (aset rt 19 (tbl ^int (&  (>>> l1 12) 0x3f)))
      (aset rt 18 (tbl ^int (&  (>>> l1 18) 0x3f)))
      (aset rt 17 (tbl ^int (&  (>>> l1 24) 0x3f)))
      (aset rt 16 (tbl ^int (&  (>>> l1 30) 0x3f)))
      (aset rt 15 (tbl ^int (&  (>>> l1 36) 0x3f)))
      (aset rt 14 (tbl ^int (&  (>>> l1 42) 0x3f)))
      (aset rt 13 (tbl ^int (&  (>>> l1 48) 0x3f)))
      (aset rt 12 (tbl ^int (&  (>>> l1 54) 0x3f)))
      (aset rt 11 (tbl ^int (&  (>>> l1 60) 0x3f)))
      (aset rt 10 (tbl ^int (&  l2 0x3f)))
      (aset rt 9  (tbl ^int (&  (>>> l2 6) 0x3f)))
      (aset rt 8  (tbl ^int (&  (>>> l2 12) 0x3f)))
      (aset rt 7  (tbl ^int (&  (>>> l2 18) 0x3f)))
      (aset rt 6  (tbl ^int (&  (>>> l2 24) 0x3f)))
      (aset rt 5  (tbl ^int (&  (>>> l2 30) 0x3f)))
      (aset rt 4  (tbl ^int (&  (>>> l2 36) 0x3f)))
      (aset rt 3  (tbl ^int (&  (>>> l2 42) 0x3f)))
      (aset rt 2  (tbl ^int (&  (>>> l2 48) 0x3f)))
      (aset rt 1  (tbl ^int (&  (>>> l2 54) 0x3f)))
      (aset rt 0  (tbl ^int (&  (>>> l2 60) 0x3f)))
      (String. rt))))

Getting Example to Work

@athos: I'm looking to get jnr-ffi working with jise and am porting the Gettimeofday example here:

https://github.com/jnr/jnr-ffi-examples/blob/master/gettimeofday/src/main/java/gettimeofday/Gettimeofday.java

I'm having problems compiling the defclass form. Would you please take a look and let me know what I'm doing wrong.

https://gist.github.com/zcaudate/f05001f6cc38a8a02f9b0892bab02a82

I'm getting this error here, which I'm not sure is defclass or jnr related.

 Error: constructor jnr.ffi.Struct$time_t in class
   jnr.ffi.Struct$time_t cannot be applied to given types
   (/Users/chris/Development/caudata/statstrade/statstrade-server/src/statstrade/lib/curl.clj:55:26)
   {:column 26,
    :line 55,
    :alternatives
    ({:param-types
      [#object[clojure.asm.Type 0x29c9e0cb "Ljnr/ffi/Struct;"]],
      :access #{:public}}
     {:param-types
      [#object[clojure.asm.Type 0x23d3c400 "Ljnr/ffi/Struct;"]
       #object[clojure.asm.Type 0x7093025d "Ljnr/ffi/Struct$Offset;"]],
      :access #{:public}})}

Are there any comparison benchmarks for the examples?

I'm really curious about the typical speeds for the algorithms you implemented (like AOBench).

I ran it, it feels really fast but I'm not sure what a comparison might be.

Do you have any comparisons that you might have ran yourself?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.