1 /*
2  * Function.java
3  *
4  * Copyright (C) 2002-2005 Peter Graves
5  * $Id$
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * As a special exception, the copyright holders of this library give you
22  * permission to link this library with independent modules to produce an
23  * executable, regardless of the license terms of these independent
24  * modules, and to copy and distribute the resulting executable under
25  * terms of your choice, provided that you also meet, for each linked
26  * independent module, the terms and conditions of the license of that
27  * module.  An independent module is a module which is not derived from
28  * or based on this library.  If you modify this library, you may extend
29  * this exception to your version of the library, but you are not
30  * obligated to do so.  If you do not wish to do so, delete this
31  * exception statement from your version.
32  */
33 
34 package org.armedbear.lisp;
35 
36 import java.io.*;
37 import java.io.ByteArrayInputStream;
38 import java.io.ByteArrayOutputStream;
39 
40 import static org.armedbear.lisp.Lisp.*;
41 
42 public abstract class Function extends Operator implements Serializable {
43     private LispObject propertyList = NIL;
44     private int callCount;
45     private int hotCount;
46     /**
47      * The value of *load-truename* which was current when this function
48      * was loaded, used for fetching the class bytes in case of disassembly.
49      */
50     public final LispObject loadedFrom;
51 
Function()52     protected Function() {
53 	LispObject loadTruename = Symbol.LOAD_TRUENAME.symbolValueNoThrow();
54 	LispObject loadTruenameFasl = Symbol.LOAD_TRUENAME_FASL.symbolValueNoThrow();
55 	loadedFrom = loadTruenameFasl != null ? loadTruenameFasl : (loadTruename != null ? loadTruename : NIL);
56     }
57 
Function(String name)58     public Function(String name)
59     {
60         this(name, (String)null);
61     }
62 
Function(String name, String arglist)63     public Function(String name, String arglist)
64     {
65 	this();
66         if(arglist != null)
67             setLambdaList(new SimpleString(arglist));
68         if (name != null) {
69             Symbol symbol = Symbol.addFunction(name.toUpperCase(), this);
70             if (cold)
71                 symbol.setBuiltInFunction(true);
72             setLambdaName(symbol);
73         }
74     }
75 
Function(Symbol symbol)76     public Function(Symbol symbol)
77     {
78 	this(symbol, null, null);
79     }
80 
Function(Symbol symbol, String arglist)81     public Function(Symbol symbol, String arglist)
82     {
83 	this(symbol, arglist, null);
84     }
85 
Function(Symbol symbol, String arglist, String docstring)86     public Function(Symbol symbol, String arglist, String docstring)
87     {
88 	this();
89         symbol.setSymbolFunction(this);
90         if (cold)
91             symbol.setBuiltInFunction(true);
92         setLambdaName(symbol);
93         if(arglist != null)
94             setLambdaList(new SimpleString(arglist));
95         if (docstring != null)
96             symbol.setDocumentation(Symbol.FUNCTION,
97                                     new SimpleString(docstring));
98     }
99 
Function(String name, Package pkg)100     public Function(String name, Package pkg)
101     {
102         this(name, pkg, false);
103     }
104 
Function(String name, Package pkg, boolean exported)105     public Function(String name, Package pkg, boolean exported)
106     {
107         this(name, pkg, exported, null, null);
108     }
109 
Function(String name, Package pkg, boolean exported, String arglist)110     public Function(String name, Package pkg, boolean exported,
111                     String arglist)
112     {
113         this(name, pkg, exported, arglist, null);
114     }
115 
Function(String name, Package pkg, boolean exported, String arglist, String docstring)116     public Function(String name, Package pkg, boolean exported,
117                     String arglist, String docstring)
118     {
119 	this();
120         if (arglist instanceof String)
121             setLambdaList(new SimpleString(arglist));
122         if (name != null) {
123             Symbol symbol;
124             if (exported)
125                 symbol = pkg.internAndExport(name.toUpperCase());
126             else
127                 symbol = pkg.intern(name.toUpperCase());
128             symbol.setSymbolFunction(this);
129             if (cold)
130                 symbol.setBuiltInFunction(true);
131             setLambdaName(symbol);
132             if (docstring != null)
133                 symbol.setDocumentation(Symbol.FUNCTION,
134                                         new SimpleString(docstring));
135         }
136     }
137 
Function(LispObject name)138     public Function(LispObject name)
139     {
140 	this();
141         setLambdaName(name);
142     }
143 
Function(LispObject name, LispObject lambdaList)144     public Function(LispObject name, LispObject lambdaList)
145     {
146 	this();
147         setLambdaName(name);
148         setLambdaList(lambdaList);
149     }
150 
151     @Override
typeOf()152     public LispObject typeOf()
153     {
154         return Symbol.FUNCTION;
155     }
156 
157     @Override
classOf()158     public LispObject classOf()
159     {
160         return BuiltInClass.FUNCTION;
161     }
162 
163     @Override
typep(LispObject typeSpecifier)164     public LispObject typep(LispObject typeSpecifier)
165     {
166         if (typeSpecifier == Symbol.FUNCTION)
167             return T;
168         if (typeSpecifier == Symbol.COMPILED_FUNCTION)
169             return T;
170         if (typeSpecifier == BuiltInClass.FUNCTION)
171             return T;
172         return super.typep(typeSpecifier);
173     }
174 
175     @Override
getPropertyList()176     public final LispObject getPropertyList()
177     {
178         if (propertyList == null)
179             propertyList = NIL;
180         return propertyList;
181     }
182 
183     @Override
setPropertyList(LispObject obj)184     public final void setPropertyList(LispObject obj)
185     {
186         if (obj == null)
187             throw new NullPointerException();
188         propertyList = obj;
189     }
190 
setClassBytes(byte[] bytes)191     public final void setClassBytes(byte[] bytes)
192     {
193         propertyList = putf(propertyList, Symbol.CLASS_BYTES,
194                             new JavaObject(bytes));
195     }
196 
getClassBytes()197     public final LispObject getClassBytes() {
198         LispObject o = getf(propertyList, Symbol.CLASS_BYTES, NIL);
199         if(o != NIL) {
200             return o;
201         } else {
202             ClassLoader c = getClass().getClassLoader();
203             if(c instanceof JavaClassLoader) {
204                 final LispThread thread = LispThread.currentThread();
205                 SpecialBindingsMark mark = thread.markSpecialBindings();
206                 try {
207                     thread.bindSpecial(Symbol.LOAD_TRUENAME, loadedFrom);
208                     return new JavaObject(((JavaClassLoader) c).getFunctionClassBytes(this));
209                 } catch(Throwable t) {
210                     //This is because unfortunately getFunctionClassBytes uses
211                     //Debug.assertTrue(false) to signal errors
212                     if(t instanceof ControlTransfer) {
213                         throw (ControlTransfer) t;
214                     } else {
215                         return NIL;
216                     }
217                 } finally {
218                     thread.resetSpecialBindings(mark);
219                 }
220             } else {
221                 return NIL;
222             }
223         }
224     }
225 
226     public static final Primitive FUNCTION_CLASS_BYTES = new pf_function_class_bytes();
227     public static final class pf_function_class_bytes extends Primitive {
pf_function_class_bytes()228 	public pf_function_class_bytes() {
229 	    super("function-class-bytes", PACKAGE_SYS, false, "function");
230         }
231         @Override
execute(LispObject arg)232         public LispObject execute(LispObject arg) {
233             if (arg instanceof Function) {
234                 return ((Function) arg).getClassBytes();
235 	    }
236             return type_error(arg, Symbol.FUNCTION);
237         }
238     }
239 
240     @Override
execute()241     public LispObject execute()
242     {
243         return error(new WrongNumberOfArgumentsException(this, 0));
244     }
245 
246     @Override
execute(LispObject arg)247     public LispObject execute(LispObject arg)
248     {
249         return error(new WrongNumberOfArgumentsException(this, 1));
250     }
251 
252     @Override
execute(LispObject first, LispObject second)253     public LispObject execute(LispObject first, LispObject second)
254 
255     {
256         return error(new WrongNumberOfArgumentsException(this, 2));
257     }
258 
259     @Override
execute(LispObject first, LispObject second, LispObject third)260     public LispObject execute(LispObject first, LispObject second,
261                               LispObject third)
262 
263     {
264         return error(new WrongNumberOfArgumentsException(this, 3));
265     }
266 
267     @Override
execute(LispObject first, LispObject second, LispObject third, LispObject fourth)268     public LispObject execute(LispObject first, LispObject second,
269                               LispObject third, LispObject fourth)
270 
271     {
272         return error(new WrongNumberOfArgumentsException(this, 4));
273     }
274 
275     @Override
execute(LispObject first, LispObject second, LispObject third, LispObject fourth, LispObject fifth)276     public LispObject execute(LispObject first, LispObject second,
277                               LispObject third, LispObject fourth,
278                               LispObject fifth)
279 
280     {
281         return error(new WrongNumberOfArgumentsException(this, 5));
282     }
283 
284     @Override
execute(LispObject first, LispObject second, LispObject third, LispObject fourth, LispObject fifth, LispObject sixth)285     public LispObject execute(LispObject first, LispObject second,
286                               LispObject third, LispObject fourth,
287                               LispObject fifth, LispObject sixth)
288 
289     {
290         return error(new WrongNumberOfArgumentsException(this, 6));
291     }
292 
293     @Override
execute(LispObject first, LispObject second, LispObject third, LispObject fourth, LispObject fifth, LispObject sixth, LispObject seventh)294     public LispObject execute(LispObject first, LispObject second,
295                               LispObject third, LispObject fourth,
296                               LispObject fifth, LispObject sixth,
297                               LispObject seventh)
298 
299     {
300         return error(new WrongNumberOfArgumentsException(this, 7));
301     }
302 
303     @Override
execute(LispObject first, LispObject second, LispObject third, LispObject fourth, LispObject fifth, LispObject sixth, LispObject seventh, LispObject eighth)304     public LispObject execute(LispObject first, LispObject second,
305                               LispObject third, LispObject fourth,
306                               LispObject fifth, LispObject sixth,
307                               LispObject seventh, LispObject eighth)
308 
309     {
310         return error(new WrongNumberOfArgumentsException(this, 8));
311     }
312 
313     @Override
execute(LispObject[] args)314     public LispObject execute(LispObject[] args)
315     {
316         return error(new WrongNumberOfArgumentsException(this));
317     }
318 
319     @Override
printObject()320     public String printObject()
321     {
322         LispObject name = getLambdaName();
323         if (name != null && name != NIL) {
324             return unreadableString(name.princToString());
325         }
326         // No name.
327         LispObject lambdaList = getLambdaList();
328         if (lambdaList != null) {
329             StringBuilder sb = new StringBuilder("FUNCTION ");
330             sb.append("(LAMBDA ");
331             if (lambdaList == NIL) {
332                 sb.append("()");
333             } else {
334                 final LispThread thread = LispThread.currentThread();
335                 final SpecialBindingsMark mark = thread.markSpecialBindings();
336                 thread.bindSpecial(Symbol.PRINT_LENGTH, Fixnum.THREE);
337                 try {
338                     sb.append(lambdaList.printObject());
339                 }
340                 finally {
341                     thread.resetSpecialBindings(mark);
342                 }
343             }
344             sb.append(")");
345             return unreadableString(sb.toString());
346         }
347         return unreadableString("FUNCTION");
348     }
349 
350     // Used by the JVM compiler.
argCountError()351     public final void argCountError()
352     {
353         error(new WrongNumberOfArgumentsException(this));
354     }
355 
356     // Profiling.
357     @Override
getCallCount()358     public final int getCallCount()
359     {
360         return callCount;
361     }
362 
363     @Override
setCallCount(int n)364     public void setCallCount(int n)
365     {
366         callCount = n;
367     }
368 
369     @Override
incrementCallCount()370     public final void incrementCallCount()
371     {
372         ++callCount;
373     }
374 
375     @Override
getHotCount()376     public final int getHotCount()
377     {
378         return hotCount;
379     }
380 
381     @Override
setHotCount(int n)382     public void setHotCount(int n)
383     {
384         hotCount = n;
385     }
386 
387     @Override
incrementHotCount()388     public final void incrementHotCount()
389     {
390         ++hotCount;
391     }
392 
393     //Serialization
394     public static class SerializedNamedFunction implements Serializable {
395         private final Symbol name;
SerializedNamedFunction(Symbol name)396         public SerializedNamedFunction(Symbol name) {
397             this.name = name;
398         }
399 
readResolve()400         public Object readResolve() {
401             return name.getSymbolFunctionOrDie();
402         }
403     }
404 
405     public static class ObjectInputStreamWithClassLoader extends ObjectInputStream {
406         private final ClassLoader classLoader;
ObjectInputStreamWithClassLoader(InputStream in, ClassLoader classLoader)407         public ObjectInputStreamWithClassLoader(InputStream in, ClassLoader classLoader) throws IOException {
408             super(in);
409             this.classLoader = classLoader;
410         }
411 
412         @Override
resolveClass(ObjectStreamClass desc)413         protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
414             return Class.forName(desc.getName(), false, classLoader);
415         }
416     }
417 
418     public static class SerializedLocalFunction implements Serializable {
419         final LispObject className;
420         final LispObject classBytes;
421         final byte[] serializedFunction;
422 
SerializedLocalFunction(Function function)423         public SerializedLocalFunction(Function function) {
424             this.className = new SimpleString(function.getClass().getName());
425             this.classBytes = function.getClassBytes();
426             serializingClosure.set(true);
427             try {
428                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
429                 new ObjectOutputStream(baos).writeObject(function);
430                 serializedFunction = baos.toByteArray();
431             } catch (IOException e) {
432                 throw new RuntimeException(e);
433             } finally {
434                 serializingClosure.remove();
435             }
436         }
437 
readResolve()438         public Object readResolve() throws InvalidObjectException {
439             MemoryClassLoader loader = new MemoryClassLoader();
440             MemoryClassLoader.PUT_MEMORY_FUNCTION.execute(JavaObject.getInstance(loader), className, classBytes);
441             try {
442                 ByteArrayInputStream in = new ByteArrayInputStream(serializedFunction);
443                 return new ObjectInputStreamWithClassLoader(in, loader).readObject();
444             } catch (Exception e) {
445                 InvalidObjectException ex = new InvalidObjectException("Could not read the serialized function back");
446                 ex.initCause(e);
447                 throw ex;
448             }
449         }
450     }
451 
452     private static final ThreadLocal<Boolean> serializingClosure = new ThreadLocal<Boolean>();
453 
writeReplace()454     public Object writeReplace() throws ObjectStreamException {
455         if(shouldSerializeByName()) {
456             return new SerializedNamedFunction((Symbol) getLambdaName());
457         } else if(getClassBytes() == NIL || serializingClosure.get() != null) {
458             return this;
459         } else {
460             return new SerializedLocalFunction(this);
461         }
462     }
463 
shouldSerializeByName()464     protected boolean shouldSerializeByName() {
465         LispObject lambdaName = getLambdaName();
466         return lambdaName instanceof Symbol && lambdaName.getSymbolFunction() == this;
467     }
468 }
469