66
77package org .mozilla .javascript ;
88
9+ import static org .mozilla .javascript .ScriptableObject .DONTENUM ;
10+
911import java .io .IOException ;
1012import java .io .ObjectInputStream ;
1113import java .io .ObjectOutputStream ;
2729import org .mozilla .classfile .ClassFileWriter ;
2830import org .mozilla .javascript .lc .type .TypeInfo ;
2931
30- public final class JavaAdapter implements IdFunctionCall {
32+ public final class JavaAdapter {
3133 /**
3234 * Provides a key with which to distinguish previously generated adapter classes stored in a
3335 * hash table.
36+ *
37+ * <p>JavaAdapter should cache the adapter class when input classes are the same, and
38+ * implementation object are of "same shape", which means having same number of function
39+ * properties, and same length for each function
3440 */
3541 static class JavaAdapterSignature {
3642 Class <?> superClass ;
3743 Class <?>[] interfaces ;
44+ // name -> function arg length
3845 Map <String , Integer > names ;
3946
40- JavaAdapterSignature (
41- Class <?> superClass , Class <?>[] interfaces , Map <String , Integer > names ) {
42- this .superClass = superClass ;
43- this .interfaces = interfaces ;
44- this .names = names ;
45- }
46-
4747 @ Override
4848 public boolean equals (Object obj ) {
4949 if (!(obj instanceof JavaAdapterSignature )) return false ;
5050 JavaAdapterSignature sig = (JavaAdapterSignature ) obj ;
51- if (superClass != sig .superClass ) return false ;
52- if (interfaces != sig .interfaces ) {
53- if (interfaces .length != sig .interfaces .length ) return false ;
54- for (int i = 0 ; i < interfaces .length ; i ++)
55- if (interfaces [i ] != sig .interfaces [i ]) return false ;
56- }
57- if (names .size () != sig .names .size ()) return false ;
58- for (Map .Entry <String , Integer > e : names .entrySet ()) {
59- String name = e .getKey ();
60- int arity = e .getValue ();
61- if (arity != sig .names .getOrDefault (name , arity + 1 )) return false ;
62- }
63- return true ;
51+ return superClass == sig .superClass
52+ && Arrays .equals (interfaces , sig .interfaces )
53+ && names .equals (sig .names );
6454 }
6555
6656 @ Override
6757 public int hashCode () {
68- return (superClass .hashCode () + Arrays .hashCode (interfaces )) ^ names .size ();
58+ return (superClass .hashCode () * 31 + Arrays .hashCode (interfaces )) ^ names .size ();
6959 }
7060 }
7161
7262 public static void init (Context cx , Scriptable scope , boolean sealed ) {
73- JavaAdapter obj = new JavaAdapter ();
74- IdFunctionObject ctor =
75- new IdFunctionObject (obj , FTAG , Id_JavaAdapter , "JavaAdapter" , 1 , scope );
76- ctor .markAsConstructor (null );
63+ var ctor = new LambdaConstructor (scope , "JavaAdapter" , 1 , JavaAdapter ::js_createAdapter );
64+
7765 if (sealed ) {
7866 ctor .sealObject ();
7967 }
80- ctor .exportAsScopeProperty ();
81- }
8268
83- @ Override
84- public Object execIdCall (
85- IdFunctionObject f , Context cx , Scriptable scope , Scriptable thisObj , Object [] args ) {
86- if (f .hasTag (FTAG )) {
87- if (f .methodId () == Id_JavaAdapter ) {
88- return js_createAdapter (cx , scope , args );
89- }
90- }
91- throw f .unknown ();
69+ ScriptableObject .defineProperty (scope , "JavaAdapter" , ctor , DONTENUM );
9270 }
9371
9472 public static Object convertResult (Object result , Class <?> c ) {
@@ -113,7 +91,7 @@ public static Object getAdapterSelf(Class<?> adapterClass, Object adapter)
11391 return self .get (adapter );
11492 }
11593
116- static Object js_createAdapter (Context cx , Scriptable scope , Object [] args ) {
94+ static Scriptable js_createAdapter (Context cx , Scriptable scope , Object [] args ) {
11795 int N = args .length ;
11896 if (N == 0 ) {
11997 throw ScriptRuntime .typeErrorById ("msg.adapter.zero.args" );
@@ -123,51 +101,21 @@ static Object js_createAdapter(Context cx, Scriptable scope, Object[] args) {
123101 // Any number of NativeJavaClass objects representing the super-class
124102 // and/or interfaces to implement, followed by one NativeObject providing
125103 // the implementation, followed by any number of arguments to pass on
126- // to the (super-class) constructor.
127-
128- int classCount ;
129- for (classCount = 0 ; classCount < N - 1 ; classCount ++) {
130- Object arg = args [classCount ];
131- // We explicitly test for NativeObject here since checking for
132- // instanceof ScriptableObject or !(instanceof NativeJavaClass)
133- // would fail for a Java class that isn't found in the class path
134- // as NativeJavaPackage extends ScriptableObject.
135- if (arg instanceof NativeObject ) {
136- break ;
137- }
138- if (!(arg instanceof NativeJavaClass )) {
139- throw ScriptRuntime .typeErrorById (
140- "msg.not.java.class.arg" ,
141- String .valueOf (classCount ),
142- ScriptRuntime .toString (arg ));
143- }
144- }
145- Class <?> superClass = null ;
146- Class <?>[] intfs = new Class [classCount ];
147- int interfaceCount = 0 ;
148- for (int i = 0 ; i < classCount ; ++i ) {
149- Class <?> c = ((NativeJavaClass ) args [i ]).getClassObject ();
150- if (!c .isInterface ()) {
151- if (superClass != null ) {
152- throw ScriptRuntime .typeErrorById (
153- "msg.only.one.super" , superClass .getName (), c .getName ());
154- }
155- superClass = c ;
156- } else {
157- intfs [interfaceCount ++] = c ;
158- }
159- }
104+ // to the (super-class) constructor:
105+ // new JavaAdapter(
106+ // AbstractClazz, Interface1, Interface2,
107+ // { someMethod: function(args) {}, someOtherMethod: function(args) {} },
108+ // ["args for AbstractClazz ctor"]
109+ // )
160110
161- if (superClass == null ) {
162- superClass = ScriptRuntime .ObjectClass ;
163- }
111+ var sig = new JavaAdapterSignature ();
112+ var classCount = fillAdapterInheritanceData (args , sig );
164113
165- Class <?>[] interfaces = new Class [interfaceCount ];
166- System .arraycopy (intfs , 0 , interfaces , 0 , interfaceCount );
167114 // next argument is implementation, must be scriptable
168115 Scriptable obj = ScriptableObject .ensureScriptable (args [classCount ]);
116+ sig .names = getObjectFunctionNames (obj );
169117
170- Class <?> adapterClass = getAdapterClass (scope , superClass , interfaces , obj );
118+ Class <?> adapterClass = getAdapterClass (scope , sig );
171119 Object adapter ;
172120
173121 int argsCount = N - classCount - 1 ;
@@ -185,9 +133,10 @@ static Object js_createAdapter(Context cx, Scriptable scope, Object[] args) {
185133 NativeJavaMethod ctors = classWrapper .members .ctors ;
186134 int index = ctors .findCachedFunction (cx , ctorArgs );
187135 if (index < 0 ) {
188- String sig = NativeJavaMethod .scriptSignature (args );
189136 throw Context .reportRuntimeErrorById (
190- "msg.no.java.ctor" , adapterClass .getName (), sig );
137+ "msg.no.java.ctor" ,
138+ adapterClass .getName (),
139+ NativeJavaMethod .scriptSignature (args ));
191140 }
192141
193142 // Found the constructor, so try invoking it.
@@ -200,15 +149,15 @@ static Object js_createAdapter(Context cx, Scriptable scope, Object[] args) {
200149 adapter = adapterClass .getConstructor (ctorParms ).newInstance (ctorArgs );
201150 }
202151
203- Object self = getAdapterSelf (adapterClass , adapter );
152+ var self = ( Scriptable ) getAdapterSelf (adapterClass , adapter );
204153 // Return unwrapped JavaAdapter if it implements Scriptable
205154 if (self instanceof Wrapper ) {
206155 Object unwrapped = ((Wrapper ) self ).unwrap ();
207156 if (unwrapped instanceof Scriptable ) {
208157 if (unwrapped instanceof ScriptableObject ) {
209158 ScriptRuntime .setObjectProtoAndParent ((ScriptableObject ) unwrapped , scope );
210159 }
211- return unwrapped ;
160+ return ( Scriptable ) unwrapped ;
212161 }
213162 }
214163 return self ;
@@ -217,6 +166,56 @@ static Object js_createAdapter(Context cx, Scriptable scope, Object[] args) {
217166 }
218167 }
219168
169+ /**
170+ * @param args JavaAdapter args to scan
171+ * @param signature holder of collected information, {@link JavaAdapterSignature#superClass} and
172+ * {@link JavaAdapterSignature#interfaces} will be overwritten
173+ * @return the index of JS implementation object
174+ */
175+ private static int fillAdapterInheritanceData (Object [] args , JavaAdapterSignature signature ) {
176+ var len = args .length ;
177+
178+ int classCount ;
179+ for (classCount = 0 ; classCount < len - 1 ; classCount ++) {
180+ Object arg = args [classCount ];
181+ // We explicitly test for NativeObject here since checking for
182+ // instanceof ScriptableObject or !(instanceof NativeJavaClass)
183+ // would fail for a Java class that isn't found in the class path
184+ // as NativeJavaPackage extends ScriptableObject.
185+ if (arg instanceof NativeObject ) {
186+ break ;
187+ }
188+ if (!(arg instanceof NativeJavaClass )) {
189+ throw ScriptRuntime .typeErrorById (
190+ "msg.not.java.class.arg" ,
191+ String .valueOf (classCount ),
192+ ScriptRuntime .toString (arg ));
193+ }
194+ }
195+ Class <?> superClass = null ;
196+ Class <?>[] interfaces = new Class [classCount ];
197+ int interfaceCount = 0 ;
198+ for (int i = 0 ; i < classCount ; ++i ) {
199+ Class <?> c = ((NativeJavaClass ) args [i ]).getClassObject ();
200+ if (c .isInterface ()) {
201+ interfaces [interfaceCount ++] = c ;
202+ } else {
203+ if (superClass != null ) {
204+ throw ScriptRuntime .typeErrorById (
205+ "msg.only.one.super" , superClass .getName (), c .getName ());
206+ }
207+ superClass = c ;
208+ }
209+ }
210+
211+ signature .superClass = superClass == null ? Object .class : superClass ;
212+ signature .interfaces =
213+ interfaceCount == interfaces .length
214+ ? interfaces
215+ : Arrays .copyOf (interfaces , interfaceCount );
216+ return classCount ;
217+ }
218+
220219 // Needed by NativeJavaObject serializer
221220 public static void writeAdapterObject (Object javaObject , ObjectOutputStream out )
222221 throws IOException {
@@ -250,17 +249,19 @@ public static Object readAdapterObject(Scriptable self, ObjectInputStream in)
250249 factory = null ;
251250 }
252251
253- Class <?> superClass = Class .forName ((String ) in .readObject ());
252+ var sig = new JavaAdapterSignature ();
253+ sig .superClass = Class .forName ((String ) in .readObject ());
254254
255255 String [] interfaceNames = (String []) in .readObject ();
256- Class <?>[] interfaces = new Class [interfaceNames .length ];
256+ sig . interfaces = new Class [interfaceNames .length ];
257257
258258 for (int i = 0 ; i < interfaceNames .length ; i ++)
259- interfaces [i ] = Class .forName (interfaceNames [i ]);
259+ sig . interfaces [i ] = Class .forName (interfaceNames [i ]);
260260
261261 Scriptable delegee = (Scriptable ) in .readObject ();
262+ sig .names = getObjectFunctionNames (delegee );
262263
263- Class <?> adapterClass = getAdapterClass (self , superClass , interfaces , delegee );
264+ Class <?> adapterClass = getAdapterClass (self , sig );
264265
265266 Class <?>[] ctorParms = {
266267 ScriptRuntime .ContextFactoryClass ,
@@ -298,18 +299,15 @@ private static Map<String, Integer> getObjectFunctionNames(Scriptable obj) {
298299 return map ;
299300 }
300301
301- private static Class <?> getAdapterClass (
302- Scriptable scope , Class <?> superClass , Class <?>[] interfaces , Scriptable obj ) {
302+ private static Class <?> getAdapterClass (Scriptable scope , JavaAdapterSignature sig ) {
303303 ClassCache cache = ClassCache .get (scope );
304304 Map <JavaAdapterSignature , Class <?>> generated = cache .getInterfaceAdapterCacheMap ();
305305
306- Map <String , Integer > names = getObjectFunctionNames (obj );
307- JavaAdapterSignature sig ;
308- sig = new JavaAdapterSignature (superClass , interfaces , names );
309306 Class <?> adapterClass = generated .get (sig );
310307 if (adapterClass == null ) {
311308 String adapterName = "adapter" + cache .newClassSerialNumber ();
312- byte [] code = createAdapterCode (names , adapterName , superClass , interfaces , null );
309+ byte [] code =
310+ createAdapterCode (sig .names , adapterName , sig .superClass , sig .interfaces , null );
313311
314312 adapterClass = loadAdapterClass (adapterName , code );
315313 if (cache .isCachingEnabled ()) {
@@ -1130,7 +1128,4 @@ static int[] getArgsToConvert(Class<?>[] argTypes) {
11301128 }
11311129 return array ;
11321130 }
1133-
1134- private static final Object FTAG = "JavaAdapter" ;
1135- private static final int Id_JavaAdapter = 1 ;
11361131}
0 commit comments