Skip to content

Commit 658693f

Browse files
frenchy64puredanger
authored andcommitted
CLJ-1973: sort proxy methods for reproducibility
Comparator sorts methods by arity then name/param-types/return-types on clashes. Signed-off-by: Alex Miller <alex.miller@cognitect.com>
1 parent 8957a93 commit 658693f

File tree

4 files changed

+77
-7
lines changed

4 files changed

+77
-7
lines changed

build.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
<!--<sysproperty key="clojure.compiler.disable-locals-clearing" value="true"/>-->
106106
<sysproperty key="clojure.compiler.direct-linking" value="${directlinking}"/>
107107
<arg value="clojure.test-clojure.protocols.examples"/>
108+
<arg value="clojure.test-clojure.proxy.examples"/>
108109
<arg value="clojure.test-clojure.genclass.examples"/>
109110
<arg value="clojure.test-clojure.compilation.load-ns"/>
110111
<arg value="clojure.test-clojure.annotations"/>

src/clj/clojure/core_proxy.clj

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -241,12 +241,17 @@
241241
mb (map #(vector (%1 %2) (vals (dissoc %1 %2))) mgroups rtypes)
242242
bridge? (reduce1 into1 #{} (map second mb))
243243
ifaces-meths (remove bridge? (vals ifaces-meths))
244-
mm (remove bridge? (vals mm))]
244+
mm (remove bridge? (vals mm))
245+
reflect-Method-keyfn (fn [meth]
246+
(let [[name param-types ^Class return-type] (method-sig meth)]
247+
(-> [name]
248+
(into1 (map #(.getName ^Class %) param-types))
249+
(conj (.getName return-type)))))]
245250
;add methods matching supers', if no mapping -> call super
246-
(doseq [[^java.lang.reflect.Method dest bridges] mb
247-
^java.lang.reflect.Method meth bridges]
251+
(doseq [[^java.lang.reflect.Method dest bridges] (sort-by (comp reflect-Method-keyfn first) mb)
252+
^java.lang.reflect.Method meth (sort-by reflect-Method-keyfn bridges)]
248253
(gen-bridge meth dest))
249-
(doseq [^java.lang.reflect.Method meth mm]
254+
(doseq [^java.lang.reflect.Method meth (sort-by reflect-Method-keyfn mm)]
250255
(gen-method meth
251256
(fn [^GeneratorAdapter gen ^Method m]
252257
(. gen (loadThis))
@@ -259,7 +264,7 @@
259264
(. m (getDescriptor)))))))
260265

261266
;add methods matching interfaces', if no mapping -> throw
262-
(doseq [^java.lang.reflect.Method meth ifaces-meths]
267+
(doseq [^java.lang.reflect.Method meth (sort-by reflect-Method-keyfn ifaces-meths)]
263268
(gen-method meth
264269
(fn [^GeneratorAdapter gen ^Method m]
265270
(. gen (throwException ex-type (. m (getName))))))))

test/clojure/test_clojure/java_interop.clj

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111

1212
(ns clojure.test-clojure.java-interop
1313
(:use clojure.test)
14-
(:require [clojure.inspector]
15-
[clojure.set :as set])
14+
(:require [clojure.data :as data]
15+
[clojure.inspector]
16+
[clojure.pprint :as pp]
17+
[clojure.set :as set]
18+
[clojure.test-clojure.proxy.examples :as proxy-examples])
1619
(:import java.util.Base64
1720
(java.util.concurrent.atomic AtomicLong AtomicInteger)))
1821

@@ -176,6 +179,37 @@
176179
str)
177180
"chain chain chain")))
178181

182+
;https://clojure.atlassian.net/browse/CLJ-1973
183+
(deftest test-proxy-method-order
184+
(let [class-reader (clojure.asm.ClassReader. proxy-examples/proxy1-class-name)
185+
method-order (atom [])
186+
method-visitor (proxy [clojure.asm.ClassVisitor] [clojure.asm.Opcodes/ASM4 nil]
187+
(visitMethod [access name descriptor signature exceptions]
188+
(swap! method-order conj {:name name :descriptor descriptor})
189+
nil))
190+
_ (.accept class-reader method-visitor 0)
191+
expected [{:name "<init>", :descriptor "()V"}
192+
{:name "__initClojureFnMappings", :descriptor "(Lclojure/lang/IPersistentMap;)V"}
193+
{:name "__updateClojureFnMappings", :descriptor "(Lclojure/lang/IPersistentMap;)V"}
194+
{:name "__getClojureFnMappings", :descriptor "()Lclojure/lang/IPersistentMap;"}
195+
{:name "clone", :descriptor "()Ljava/lang/Object;"}
196+
{:name "hashCode", :descriptor "()I"}
197+
{:name "toString", :descriptor "()Ljava/lang/String;"}
198+
{:name "equals", :descriptor "(Ljava/lang/Object;)Z"}
199+
{:name "a", :descriptor "(Ljava/io/File;)Z"}
200+
{:name "a", :descriptor "(Ljava/lang/Boolean;)Ljava/lang/Object;"}
201+
{:name "a", :descriptor "(Ljava/lang/Runnable;)Z"}
202+
{:name "a", :descriptor "(Ljava/lang/String;)I"}
203+
{:name "b", :descriptor "(Ljava/lang/String;)Ljava/lang/Object;"}
204+
{:name "c", :descriptor "(Ljava/lang/String;)Ljava/lang/Object;"}
205+
{:name "d", :descriptor "(Ljava/lang/String;)Ljava/lang/Object;"}
206+
{:name "a", :descriptor "(Ljava/lang/Boolean;Ljava/lang/String;)I"}
207+
{:name "a", :descriptor "(Ljava/lang/String;Ljava/io/File;)Z"}
208+
{:name "a", :descriptor "(Ljava/lang/String;Ljava/lang/Runnable;)Z"}
209+
{:name "a", :descriptor "(Ljava/lang/String;Ljava/lang/String;)I"}]
210+
actual @method-order]
211+
(is (= expected actual)
212+
(with-out-str (pp/pprint (data/diff expected actual))))))
179213

180214
;; serialized-proxy can be regenerated using a modified version of
181215
;; Clojure with the proxy serialization prohibition disabled and the
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
; Copyright (c) Rich Hickey. All rights reserved.
2+
; The use and distribution terms for this software are covered by the
3+
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4+
; which can be found in the file epl-v10.html at the root of this distribution.
5+
; By using this software in any fashion, you are agreeing to be bound by
6+
; the terms of this license.
7+
; You must not remove this notice, or any other, from this software.
8+
9+
(ns ^{:doc "Test proxy classes that are AOT-compiled for the tests in
10+
clojure.test-clojure.java-interop."
11+
:author "Ambrose Bonnaire-Sergeant"}
12+
clojure.test-clojure.proxy.examples)
13+
14+
(definterface A
15+
(^int a [^String x])
16+
(^boolean a [^java.io.File x])
17+
(^boolean a [^Runnable x])
18+
(a [^Boolean x])
19+
(^int a [^Boolean x ^String y])
20+
(^int a [^String x ^String y])
21+
(^boolean a [^String x ^java.io.File y])
22+
(^boolean a [^String x ^Runnable y])
23+
(b [^String x])
24+
(c [^String x])
25+
(d [^String x]))
26+
27+
(def ^String proxy1-class-name
28+
(-> (proxy [A] [])
29+
class
30+
.getName))

0 commit comments

Comments
 (0)