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