4343import java .security .PrivilegedAction ;
4444import java .security .ProtectionDomain ;
4545import java .util .ArrayList ;
46+ import java .util .Arrays ;
4647import java .util .Collection ;
4748import java .util .Collections ;
4849import java .util .HashMap ;
@@ -158,20 +159,126 @@ public Loader run() {
158159
159160 compile (contextsToCompilers .get (context ), loader , scriptName , scriptSource , params );
160161
161- return generateFactory (loader , context );
162+ if (context .statefulFactoryClazz != null ) {
163+ return generateFactory (loader , context , generateStatefulFactory (loader , context ));
164+ } else {
165+ return generateFactory (loader , context , WriterConstants .CLASS_TYPE );
166+ }
167+ }
168+ }
169+
170+ /**
171+ * Generates a stateful factory class that will return script instances. Acts as a middle man between
172+ * the {@link ScriptContext#factoryClazz} and the {@link ScriptContext#instanceClazz} when used so that
173+ * the stateless factory can be used for caching and the stateful factory can act as a cache for new
174+ * script instances. Uses the newInstance method from a {@link ScriptContext#statefulFactoryClazz} to
175+ * define the factory method to create new instances of the {@link ScriptContext#instanceClazz}.
176+ * @param loader The {@link ClassLoader} that is used to define the factory class and script class.
177+ * @param context The {@link ScriptContext}'s semantics are used to define the factory class.
178+ * @param <T> The factory class.
179+ * @return A factory class that will return script instances.
180+ */
181+ private <T > Type generateStatefulFactory (Loader loader , ScriptContext <T > context ) {
182+ int classFrames = ClassWriter .COMPUTE_FRAMES | ClassWriter .COMPUTE_MAXS ;
183+ int classAccess = Opcodes .ACC_PUBLIC | Opcodes .ACC_SUPER | Opcodes .ACC_FINAL ;
184+ String interfaceBase = Type .getType (context .statefulFactoryClazz ).getInternalName ();
185+ String className = interfaceBase + "$StatefulFactory" ;
186+ String classInterfaces [] = new String [] { interfaceBase };
187+
188+ ClassWriter writer = new ClassWriter (classFrames );
189+ writer .visit (WriterConstants .CLASS_VERSION , classAccess , className , null , OBJECT_TYPE .getInternalName (), classInterfaces );
190+
191+ Method newFactory = null ;
192+
193+ for (Method method : context .factoryClazz .getMethods ()) {
194+ if ("newFactory" .equals (method .getName ())) {
195+ newFactory = method ;
196+
197+ break ;
198+ }
199+ }
200+
201+ for (int count = 0 ; count < newFactory .getParameterTypes ().length ; ++count ) {
202+ writer .visitField (Opcodes .ACC_PRIVATE | Opcodes .ACC_FINAL , "$arg" + count ,
203+ Type .getType (newFactory .getParameterTypes ()[count ]).getDescriptor (), null , null ).visitEnd ();
204+ }
205+
206+ org .objectweb .asm .commons .Method base =
207+ new org .objectweb .asm .commons .Method ("<init>" , MethodType .methodType (void .class ).toMethodDescriptorString ());
208+ org .objectweb .asm .commons .Method init = new org .objectweb .asm .commons .Method ("<init>" ,
209+ MethodType .methodType (void .class , newFactory .getParameterTypes ()).toMethodDescriptorString ());
210+
211+ GeneratorAdapter constructor = new GeneratorAdapter (Opcodes .ASM5 , init ,
212+ writer .visitMethod (Opcodes .ACC_PUBLIC , init .getName (), init .getDescriptor (), null , null ));
213+ constructor .visitCode ();
214+ constructor .loadThis ();
215+ constructor .invokeConstructor (OBJECT_TYPE , base );
216+
217+ for (int count = 0 ; count < newFactory .getParameterTypes ().length ; ++count ) {
218+ constructor .loadThis ();
219+ constructor .loadArg (count );
220+ constructor .putField (Type .getType (className ), "$arg" + count , Type .getType (newFactory .getParameterTypes ()[count ]));
221+ }
222+
223+ constructor .returnValue ();
224+ constructor .endMethod ();
225+
226+ Method newInstance = null ;
227+
228+ for (Method method : context .statefulFactoryClazz .getMethods ()) {
229+ if ("newInstance" .equals (method .getName ())) {
230+ newInstance = method ;
231+
232+ break ;
233+ }
234+ }
235+
236+ org .objectweb .asm .commons .Method instance = new org .objectweb .asm .commons .Method (newInstance .getName (),
237+ MethodType .methodType (newInstance .getReturnType (), newInstance .getParameterTypes ()).toMethodDescriptorString ());
238+
239+ List <Class <?>> parameters = new ArrayList <>(Arrays .asList (newFactory .getParameterTypes ()));
240+ parameters .addAll (Arrays .asList (newInstance .getParameterTypes ()));
241+
242+ org .objectweb .asm .commons .Method constru = new org .objectweb .asm .commons .Method ("<init>" ,
243+ MethodType .methodType (void .class , parameters .toArray (new Class <?>[] {})).toMethodDescriptorString ());
244+
245+ GeneratorAdapter adapter = new GeneratorAdapter (Opcodes .ASM5 , instance ,
246+ writer .visitMethod (Opcodes .ACC_PUBLIC | Opcodes .ACC_FINAL ,
247+ instance .getName (), instance .getDescriptor (), null , null ));
248+ adapter .visitCode ();
249+ adapter .newInstance (WriterConstants .CLASS_TYPE );
250+ adapter .dup ();
251+
252+ for (int count = 0 ; count < newFactory .getParameterTypes ().length ; ++count ) {
253+ adapter .loadThis ();
254+ adapter .getField (Type .getType (className ), "$arg" + count , Type .getType (newFactory .getParameterTypes ()[count ]));
162255 }
256+
257+ adapter .loadArgs ();
258+ adapter .invokeConstructor (WriterConstants .CLASS_TYPE , constru );
259+ adapter .returnValue ();
260+ adapter .endMethod ();
261+
262+ writer .visitEnd ();
263+
264+ loader .defineFactory (className .replace ('/' , '.' ), writer .toByteArray ());
265+
266+ return Type .getType (className );
163267 }
164268
165269 /**
166- * Generates a factory class that will return script instances.
270+ * Generates a factory class that will return script instances or stateful factories .
167271 * Uses the newInstance method from a {@link ScriptContext#factoryClazz} to define the factory method
168- * to create new instances of the {@link ScriptContext#instanceClazz}.
272+ * to create new instances of the {@link ScriptContext#instanceClazz} or uses the newFactory method
273+ * to create new factories of the {@link ScriptContext#statefulFactoryClazz}.
169274 * @param loader The {@link ClassLoader} that is used to define the factory class and script class.
170275 * @param context The {@link ScriptContext}'s semantics are used to define the factory class.
276+ * @param classType The type to be instaniated in the newFactory or newInstance method. Depends
277+ * on whether a {@link ScriptContext#statefulFactoryClazz} is specified.
171278 * @param <T> The factory class.
172279 * @return A factory class that will return script instances.
173280 */
174- private <T > T generateFactory (Loader loader , ScriptContext <T > context ) {
281+ private <T > T generateFactory (Loader loader , ScriptContext <T > context , Type classType ) {
175282 int classFrames = ClassWriter .COMPUTE_FRAMES | ClassWriter .COMPUTE_MAXS ;
176283 int classAccess = Opcodes .ACC_PUBLIC | Opcodes .ACC_SUPER | Opcodes .ACC_FINAL ;
177284 String interfaceBase = Type .getType (context .factoryClazz ).getInternalName ();
@@ -188,25 +295,37 @@ private <T> T generateFactory(Loader loader, ScriptContext<T> context) {
188295 writer .visitMethod (Opcodes .ACC_PUBLIC , init .getName (), init .getDescriptor (), null , null ));
189296 constructor .visitCode ();
190297 constructor .loadThis ();
191- constructor .loadArgs ();
192298 constructor .invokeConstructor (OBJECT_TYPE , init );
193299 constructor .returnValue ();
194300 constructor .endMethod ();
195301
196- Method reflect = context .factoryClazz .getMethods ()[0 ];
302+ Method reflect = null ;
303+
304+ for (Method method : context .factoryClazz .getMethods ()) {
305+ if ("newInstance" .equals (method .getName ())) {
306+ reflect = method ;
307+
308+ break ;
309+ } else if ("newFactory" .equals (method .getName ())) {
310+ reflect = method ;
311+
312+ break ;
313+ }
314+ }
315+
197316 org .objectweb .asm .commons .Method instance = new org .objectweb .asm .commons .Method (reflect .getName (),
198317 MethodType .methodType (reflect .getReturnType (), reflect .getParameterTypes ()).toMethodDescriptorString ());
199318 org .objectweb .asm .commons .Method constru = new org .objectweb .asm .commons .Method ("<init>" ,
200319 MethodType .methodType (void .class , reflect .getParameterTypes ()).toMethodDescriptorString ());
201320
202321 GeneratorAdapter adapter = new GeneratorAdapter (Opcodes .ASM5 , instance ,
203- writer .visitMethod (Opcodes .ACC_PUBLIC | Opcodes .ACC_SUPER | Opcodes . ACC_FINAL ,
322+ writer .visitMethod (Opcodes .ACC_PUBLIC | Opcodes .ACC_FINAL ,
204323 instance .getName (), instance .getDescriptor (), null , null ));
205324 adapter .visitCode ();
206- adapter .newInstance (WriterConstants . CLASS_TYPE );
325+ adapter .newInstance (classType );
207326 adapter .dup ();
208327 adapter .loadArgs ();
209- adapter .invokeConstructor (WriterConstants . CLASS_TYPE , constru );
328+ adapter .invokeConstructor (classType , constru );
210329 adapter .returnValue ();
211330 adapter .endMethod ();
212331
0 commit comments