Uploaded image for project: 'Clojure'
  1. CLJ-979

Clojure resolves to wrong deftype classes when AOT compiling or reloading

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Critical
    • Resolution: Completed
    • Affects versions: Release 1.7, Release 1.6, Release 1.5, Release 1.4, Release 1.3
    • Fix versions: Release 1.7
    • Approval:
      Ok
    • Patch:
      Code and Test

      Description

      Compiling a class via `deftype` during AOT compilation gives different results for the different constructors. These hashes should be identical.

      user=> (binding [*compile-files* true] (eval '(deftype Abc [])))
      user.Abc
      user=> (hash Abc)
      16446700
      user=> (hash (class (->Abc)))
      31966239 ;; should be 16446700
      

      This also means that whenever there's a stale AOT compiled deftype class in the classpath, that class will be used rather then the JIT compiled one, breaking repl interaction.

      Another demonstration of this classloader issue (from CLJ-1495 Closed ) when reloading deftypes (no AOT) :

      user> (defrecord Foo [bar])
      user.Foo
      user> (= (->Foo 42) #user.Foo{:bar 42}) ;;expect this to evaluate to true
      true
      user> (defrecord Foo [bar])
      user.Foo
      user> (= (->Foo 42) #user.Foo{:bar 42}) ;;expect this to evaluate to true also -- but it doesn't!
      false
      user>
      

      This bug also affects AOT compilation of multimethods that dispatch on a class, this affected core.match for years see http://dev.clojure.org/jira/browse/MATCH-86, http://dev.clojure.org/jira/browse/MATCH-98. David had to work-around this issue by using a bunch of protocols instead of multimethods.

      Cause of the bug: currently clojure uses Class.forName to resolve a class from a class name, which ignores the class cache from DynamicClassLoader thus reloading deftypes or mixing AOT compilation at the repl with deftypes breaks, resolving to the wrong class.

      Approach: the current patch (CLJ-979-v7.patch) addresses this issue in multiple ways:

      • it makes RT.classForName/classForNameNonLoading look in the class cache before delegating to Class/forName if the current classloader is not a DynamicClassLoader (this incidentally addresses also CLJ-1457 Closed )
      • it makes clojure use RT.classForName/classForNameNonLoading instead of Class/forName
      • it overrides Classloader/loadClass so that it's class cache aware – this method is used by the jvm to load classes
      • it changes gen-interface to always emit an in-memory interface along with the [optional] on-disk interface so that the in-memory class is always updated.

      Patch: CLJ-979-v7.patch

      Screened by: Alex Miller

        Attachments

        1. CLJ-979.patch
          8 kB
        2. clj-979-symptoms.patch
          2 kB
        3. CLJ-979-v2.patch
          9 kB
        4. CLJ-979-v3.patch
          9 kB
        5. CLJ-979-v4.patch
          9 kB
        6. CLJ-979-v5.patch
          10 kB
        7. CLJ-979-v6.patch
          10 kB
        8. CLJ-979-v7.patch
          10 kB

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              alex+import import
            • Votes:
              10 Vote for this issue
              Watchers:
              6 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: