Giter VIP home page Giter VIP logo

refl's Introduction

refl

An example project and script to clean up reflection configurations produced by the GraalVM native-image-agent for Clojure projects.

Problem description

The reflection configs produced by the GraalVM native-image-agent contain many false positives for Clojure programs. This can be alleviated using a caller-based filter, like described here. But excluding all calls from clojure.lang.RT may be too coarse for some programs. This repo offers a finer-grained solution.

How it works

This project invokes GRAALVM_HOME/bin/java on an AOT-ed Clojure program that does runtime reflection, twice. In both runs the native-image-agent is used. The first time it is invoked a trace-file.json will be produced. The second time a reflect-config.json will be produced. Unfortunately the reflect-config.json isn't very usable for GraalVM native-image yet, since it contains a lot of false positives. Using the script script/gen-reflect-config.clj the information from both JSON files is combined to create a cleaned up version of the reflect config, called reflect-config-cleaned.json. This config is then used for native compilation.

How to use

This project is intended as a reference example on how you could clean up your generated reflection config. Feel free to copy the code and change it to your needs. See the tasks section to see what you can do in this project.

Requirements

Download GraalVM and set GRAALVM_HOME. You will also need clojure for Clojure compilation. Scripts are executed with bb (babashka).

The example

This is the example Clojure program that performs reflection at runtime:

(ns refl.main
  (:require [clojure.java.io :as io])
  (:gen-class))

(defn refl-str [s]
  s)

(defn file [f]
  (io/file f))

(defn -main [& _]
  (let [res (refl-str "foo")]
    (println (.length res)) ;; reflect on string
    (prn (type (into-array [res res]))) ;; make array of unknown type
    (println (.getPath (file "."))))) ;; reflect on file

This is the generated reflection config, after executing bb gen-reflect-config:

[ {
  "name" : "java.io.File",
  "allPublicMethods" : true
}, {
  "name" : "java.lang.Object[]"
}, {
  "name" : "java.lang.String",
  "allPublicMethods" : true
}, {
  "name" : "java.lang.String[]"
}, {
  "name" : "java.lang.reflect.AccessibleObject",
  "methods" : [ {
    "name" : "canAccess",
    "parameterTypes" : [ "java.lang.Object" ]
  } ]
} ]

Note that the raw reflect-config.json is 527 lines long and contains many false positives, mainly due to calls to Class/forName in clojure.lang.RT and some other places. Unfortunately ignoring all calls from clojure.lang.RT is too coarse, since it also does reflection to create arrays.

The java.lang.reflect.AccessibleObject is needed because the clojure.lang.Reflector reflectively looks up the canAccess method on Method here. How meta.

Tasks

See bb tasks:

The following tasks are available:

compile-clj
classpath
gen-reflect-config
compile-native

License

Unlicense.

refl's People

Contributors

borkdude avatar

Stargazers

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

Watchers

 avatar  avatar  avatar

refl's Issues

Refl should ignore entries when caller is either clojure.lang.Compiler$ObjExpr or clojure.lang.Compiler$NewInstanceExpr, clojure.lang.Compiler$StaticFieldExpr etc.

Some of the entries like:

{"caller_class":"clojure.lang.Compiler$ObjExpr", "result":true, "args":[[]], "function":"getDeclaredConstructor", "tracer":"reflect", "class":"clojure.core.specs.alpha$fn__52"},
{"caller_class":"clojure.lang.Compiler$ObjExpr", "result":true, "args":[[]], "function":"getDeclaredConstructor", "tracer":"reflect", "class":"clojure.core.specs.alpha$fn__54"},
{"caller_class":"clojure.lang.Compiler$ObjExpr", "result":true, "args":[[]], "function":"getDeclaredConstructor", "tracer":"reflect", "class":"clojure.core.specs.alpha$fn__56"},
{"caller_class":"clojure.lang.Compiler$ObjExpr", "result":true, "args":[[]], "function":"getDeclaredConstructor", "tracer":"reflect", "class":"clojure.core.specs.alpha$fn__69"},
{"caller_class":"clojure.lang.Compiler$ObjExpr", "result":true, "args":[[]], "function":"getDeclaredConstructor", "tracer":"reflect", "class":"clojure.core.specs.alpha$fn__71"},
{"caller_class":"clojure.lang.Compiler$ObjExpr", "result":true, "args":[[]], "function":"getDeclaredConstructor", "tracer":"reflect", "class":"clojure.core.specs.alpha$fn__83"},
{"caller_class":"clojure.lang.Compiler$ObjExpr", "result":true, "args":[[]], "function":"getDeclaredConstructor", "tracer":"reflect", "class":"clojure.core.specs.alpha$fn__96"},
{"caller_class":"clojure.lang.Compiler$ObjExpr", "result":true, "args":[[]], "function":"getDeclaredConstructor", "tracer":"reflect", "class":"clojure.core.specs.alpha$fn__99"},

end up in reflect-config.json.

To mitigate this issue I'm ignoring those classes in which caller_class is one of

(defn ignore-by-class!
  [{:keys [tracer caller_class class] :as _m}]
  (when (= "reflect" tracer)
    (when (or (contains? #{"clojure.lang.Compiler$StaticFieldExpr"
                           "clojure.lang.Compiler$CompilerException"
                           "clojure.lang.Compiler$ObjExpr"
                           "clojure.lang.Compiler$NewInstanceExpr"}
                         caller_class)
              (and class (str/starts-with? class "clojure.lang")))
      (swap! ignored-by-class conj class))))

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.