From 059c70d1d7dcb488f97fbfc75b31c8fee8a7de77 Mon Sep 17 00:00:00 2001 From: nomennescio Date: Fri, 18 Oct 2019 15:04:30 +0200 Subject: [PATCH] Imported https://downloads.factorcode.org/releases/0.29/Factor-0.29.jar --- .cvskeywords | 45 + META-INF/MANIFEST.MF | 5 + factor/FactorArrayStack.java | 160 ++ factor/FactorCallFrame.java | 115 ++ factor/FactorCallStack.java | 65 + factor/FactorCompoundDefinition.java | 317 +++ factor/FactorDataStack.java | 71 + factor/FactorDictionary.java | 137 ++ factor/FactorDomainException.java | 38 + factor/FactorException.java | 54 + factor/FactorExternalizable.java | 39 + factor/FactorInterpreter.java | 357 ++++ factor/FactorJava.java | 522 +++++ factor/FactorLib.java | 255 +++ factor/FactorList.java | 347 ++++ factor/FactorMath.java | 363 ++++ factor/FactorMissingDefinition.java | 52 + factor/FactorNamespace.java | 336 +++ factor/FactorObject.java | 35 + factor/FactorParseException.java | 38 + factor/FactorParser.java | 540 +++++ factor/FactorPrimitive.java | 374 ++++ factor/FactorRatio.java | 286 +++ factor/FactorReflectionForm.java | 394 ++++ factor/FactorRuntimeException.java | 43 + factor/FactorShuffleDefinition.java | 490 +++++ factor/FactorStackException.java | 38 + factor/FactorUndefinedWordException.java | 38 + factor/FactorWord.java | 69 + factor/FactorWordDefinition.java | 191 ++ factor/PublicCloneable.java | 35 + factor/boot.factor | 102 + factor/combinators.factor | 228 ++ factor/continuations.factor | 73 + factor/dictionary.factor | 67 + factor/examples.factor | 38 + factor/examples/httpd.factor | 151 ++ factor/interpreter.factor | 175 ++ factor/lists.factor | 149 ++ factor/math.factor | 174 ++ factor/miscellaneous.factor | 93 + factor/namespaces.factor | 68 + factor/network.factor | 60 + factor/parser.factor | 42 + factor/random.factor | 95 + factor/stream.factor | 230 +++ factor/strings.factor | 137 ++ org/objectweb/asm/Attribute.java | 213 ++ org/objectweb/asm/ByteVector.java | 293 +++ org/objectweb/asm/ClassAdapter.java | 109 + org/objectweb/asm/ClassReader.java | 1011 +++++++++ org/objectweb/asm/ClassVisitor.java | 158 ++ org/objectweb/asm/ClassWriter.java | 1033 ++++++++++ org/objectweb/asm/CodeAdapter.java | 163 ++ org/objectweb/asm/CodeVisitor.java | 310 +++ org/objectweb/asm/CodeWriter.java | 1826 +++++++++++++++++ org/objectweb/asm/Constants.java | 288 +++ org/objectweb/asm/Edge.java | 70 + org/objectweb/asm/Item.java | 266 +++ org/objectweb/asm/Label.java | 327 +++ org/objectweb/asm/Type.java | 763 +++++++ org/objectweb/asm/attrs/Annotation.java | 335 +++ .../asm/attrs/AnnotationDefaultAttribute.java | 120 ++ .../asm/attrs/AnnotationMemberValue.java | 466 +++++ org/objectweb/asm/attrs/Dumpable.java | 61 + .../asm/attrs/EnclosingMethodAttribute.java | 115 ++ .../attrs/RuntimeInvisibleAnnotations.java | 133 ++ .../RuntimeInvisibleParameterAnnotations.java | 152 ++ .../asm/attrs/RuntimeVisibleAnnotations.java | 129 ++ .../RuntimeVisibleParameterAnnotations.java | 144 ++ .../asm/attrs/SignatureAttribute.java | 129 ++ .../attrs/SourceDebugExtensionAttribute.java | 189 ++ .../asm/attrs/StackMapAttribute.java | 232 +++ org/objectweb/asm/attrs/StackMapFrame.java | 227 ++ org/objectweb/asm/attrs/StackMapType.java | 124 ++ org/objectweb/asm/tree/AbstractInsnNode.java | 78 + org/objectweb/asm/tree/ClassNode.java | 186 ++ org/objectweb/asm/tree/FieldInsnNode.java | 103 + org/objectweb/asm/tree/FieldNode.java | 120 ++ org/objectweb/asm/tree/IincInsnNode.java | 74 + org/objectweb/asm/tree/InnerClassNode.java | 109 + org/objectweb/asm/tree/InsnNode.java | 126 ++ org/objectweb/asm/tree/IntInsnNode.java | 78 + org/objectweb/asm/tree/JumpInsnNode.java | 87 + org/objectweb/asm/tree/LdcInsnNode.java | 72 + org/objectweb/asm/tree/LineNumberNode.java | 81 + org/objectweb/asm/tree/LocalVariableNode.java | 113 + .../asm/tree/LookupSwitchInsnNode.java | 108 + org/objectweb/asm/tree/MethodInsnNode.java | 104 + org/objectweb/asm/tree/MethodNode.java | 210 ++ .../asm/tree/MultiANewArrayInsnNode.java | 76 + .../asm/tree/TableSwitchInsnNode.java | 107 + org/objectweb/asm/tree/TreeClassAdapter.java | 133 ++ org/objectweb/asm/tree/TreeCodeAdapter.java | 186 ++ org/objectweb/asm/tree/TryCatchBlockNode.java | 102 + org/objectweb/asm/tree/TypeInsnNode.java | 81 + org/objectweb/asm/tree/VarInsnNode.java | 84 + org/objectweb/asm/util/CheckClassAdapter.java | 230 +++ org/objectweb/asm/util/CheckCodeAdapter.java | 859 ++++++++ org/objectweb/asm/util/DumpClassVisitor.java | 532 +++++ org/objectweb/asm/util/DumpCodeVisitor.java | 306 +++ org/objectweb/asm/util/PrintClassVisitor.java | 132 ++ org/objectweb/asm/util/PrintCodeVisitor.java | 693 +++++++ org/objectweb/asm/util/TraceClassVisitor.java | 366 ++++ org/objectweb/asm/util/TraceCodeVisitor.java | 360 ++++ version.factor | 1 + 106 files changed, 23014 insertions(+) create mode 100644 META-INF/MANIFEST.MF create mode 100644 factor/FactorArrayStack.java create mode 100644 factor/FactorCallFrame.java create mode 100644 factor/FactorCallStack.java create mode 100644 factor/FactorCompoundDefinition.java create mode 100644 factor/FactorDataStack.java create mode 100644 factor/FactorDictionary.java create mode 100644 factor/FactorDomainException.java create mode 100644 factor/FactorException.java create mode 100644 factor/FactorExternalizable.java create mode 100644 factor/FactorInterpreter.java create mode 100644 factor/FactorJava.java create mode 100644 factor/FactorLib.java create mode 100644 factor/FactorList.java create mode 100644 factor/FactorMath.java create mode 100644 factor/FactorMissingDefinition.java create mode 100644 factor/FactorNamespace.java create mode 100644 factor/FactorObject.java create mode 100644 factor/FactorParseException.java create mode 100644 factor/FactorParser.java create mode 100644 factor/FactorPrimitive.java create mode 100644 factor/FactorRatio.java create mode 100644 factor/FactorReflectionForm.java create mode 100644 factor/FactorRuntimeException.java create mode 100644 factor/FactorShuffleDefinition.java create mode 100644 factor/FactorStackException.java create mode 100644 factor/FactorUndefinedWordException.java create mode 100644 factor/FactorWord.java create mode 100644 factor/FactorWordDefinition.java create mode 100644 factor/PublicCloneable.java create mode 100644 factor/boot.factor create mode 100644 factor/combinators.factor create mode 100644 factor/continuations.factor create mode 100644 factor/dictionary.factor create mode 100644 factor/examples.factor create mode 100644 factor/examples/httpd.factor create mode 100644 factor/interpreter.factor create mode 100644 factor/lists.factor create mode 100644 factor/math.factor create mode 100644 factor/miscellaneous.factor create mode 100644 factor/namespaces.factor create mode 100644 factor/network.factor create mode 100644 factor/parser.factor create mode 100644 factor/random.factor create mode 100644 factor/stream.factor create mode 100644 factor/strings.factor create mode 100644 org/objectweb/asm/Attribute.java create mode 100644 org/objectweb/asm/ByteVector.java create mode 100644 org/objectweb/asm/ClassAdapter.java create mode 100644 org/objectweb/asm/ClassReader.java create mode 100644 org/objectweb/asm/ClassVisitor.java create mode 100644 org/objectweb/asm/ClassWriter.java create mode 100644 org/objectweb/asm/CodeAdapter.java create mode 100644 org/objectweb/asm/CodeVisitor.java create mode 100644 org/objectweb/asm/CodeWriter.java create mode 100644 org/objectweb/asm/Constants.java create mode 100644 org/objectweb/asm/Edge.java create mode 100644 org/objectweb/asm/Item.java create mode 100644 org/objectweb/asm/Label.java create mode 100644 org/objectweb/asm/Type.java create mode 100644 org/objectweb/asm/attrs/Annotation.java create mode 100644 org/objectweb/asm/attrs/AnnotationDefaultAttribute.java create mode 100644 org/objectweb/asm/attrs/AnnotationMemberValue.java create mode 100644 org/objectweb/asm/attrs/Dumpable.java create mode 100644 org/objectweb/asm/attrs/EnclosingMethodAttribute.java create mode 100644 org/objectweb/asm/attrs/RuntimeInvisibleAnnotations.java create mode 100644 org/objectweb/asm/attrs/RuntimeInvisibleParameterAnnotations.java create mode 100644 org/objectweb/asm/attrs/RuntimeVisibleAnnotations.java create mode 100644 org/objectweb/asm/attrs/RuntimeVisibleParameterAnnotations.java create mode 100644 org/objectweb/asm/attrs/SignatureAttribute.java create mode 100644 org/objectweb/asm/attrs/SourceDebugExtensionAttribute.java create mode 100644 org/objectweb/asm/attrs/StackMapAttribute.java create mode 100644 org/objectweb/asm/attrs/StackMapFrame.java create mode 100644 org/objectweb/asm/attrs/StackMapType.java create mode 100644 org/objectweb/asm/tree/AbstractInsnNode.java create mode 100644 org/objectweb/asm/tree/ClassNode.java create mode 100644 org/objectweb/asm/tree/FieldInsnNode.java create mode 100644 org/objectweb/asm/tree/FieldNode.java create mode 100644 org/objectweb/asm/tree/IincInsnNode.java create mode 100644 org/objectweb/asm/tree/InnerClassNode.java create mode 100644 org/objectweb/asm/tree/InsnNode.java create mode 100644 org/objectweb/asm/tree/IntInsnNode.java create mode 100644 org/objectweb/asm/tree/JumpInsnNode.java create mode 100644 org/objectweb/asm/tree/LdcInsnNode.java create mode 100644 org/objectweb/asm/tree/LineNumberNode.java create mode 100644 org/objectweb/asm/tree/LocalVariableNode.java create mode 100644 org/objectweb/asm/tree/LookupSwitchInsnNode.java create mode 100644 org/objectweb/asm/tree/MethodInsnNode.java create mode 100644 org/objectweb/asm/tree/MethodNode.java create mode 100644 org/objectweb/asm/tree/MultiANewArrayInsnNode.java create mode 100644 org/objectweb/asm/tree/TableSwitchInsnNode.java create mode 100644 org/objectweb/asm/tree/TreeClassAdapter.java create mode 100644 org/objectweb/asm/tree/TreeCodeAdapter.java create mode 100644 org/objectweb/asm/tree/TryCatchBlockNode.java create mode 100644 org/objectweb/asm/tree/TypeInsnNode.java create mode 100644 org/objectweb/asm/tree/VarInsnNode.java create mode 100644 org/objectweb/asm/util/CheckClassAdapter.java create mode 100644 org/objectweb/asm/util/CheckCodeAdapter.java create mode 100644 org/objectweb/asm/util/DumpClassVisitor.java create mode 100644 org/objectweb/asm/util/DumpCodeVisitor.java create mode 100644 org/objectweb/asm/util/PrintClassVisitor.java create mode 100644 org/objectweb/asm/util/PrintCodeVisitor.java create mode 100644 org/objectweb/asm/util/TraceClassVisitor.java create mode 100644 org/objectweb/asm/util/TraceCodeVisitor.java create mode 100644 version.factor diff --git a/.cvskeywords b/.cvskeywords index e69de29bb2..014a1e06db 100644 --- a/.cvskeywords +++ b/.cvskeywords @@ -0,0 +1,45 @@ +./factor/random.factor:! $Id: random.factor,v 1.3 2004/01/09 01:51:30 slava Exp $ +./factor/FactorExternalizable.java: * $Id: FactorExternalizable.java,v 1.3 2003/12/09 09:40:18 slava Exp $ +./factor/FactorJava.java: * $Id: FactorJava.java,v 1.12 2004/01/06 06:59:38 slava Exp $ +./factor/FactorDictionary.java: * $Id: FactorDictionary.java,v 1.12 2004/01/14 01:58:37 slava Exp $ +./factor/combinators.factor:! $Id: combinators.factor,v 1.17 2004/01/11 23:00:34 slava Exp $ +./factor/FactorDataStack.java: * $Id: FactorCallStack.java,v 1.1 2003/12/20 02:13:09 slava Exp $ +./factor/continuations.factor:! $Id: continuations.factor,v 1.8 2003/12/20 05:31:01 slava Exp $ +./factor/network.factor:! $Id: io.factor,v 1.2 2003/12/20 05:31:01 slava Exp $ +./factor/FactorLib.java: * $Id: FactorLib.java,v 1.10 2004/01/03 20:58:09 slava Exp $ +./factor/FactorRuntimeException.java: * $Id: FactorRuntimeException.java,v 1.3 2003/12/09 09:40:18 slava Exp $ +./factor/FactorRatio.java: * $Id: FactorRatio.java,v 1.1 2003/12/16 08:03:08 slava Exp $ +./factor/FactorCallStack.java: * $Id: FactorCallStack.java,v 1.1 2003/12/20 02:13:09 slava Exp $ +./factor/FactorStackException.java: * $Id: FactorStackException.java,v 1.3 2003/12/09 09:40:18 slava Exp $ +./factor/FactorUndefinedWordException.java: * $Id: FactorUndefinedWordException.java,v 1.1 2003/12/09 09:52:26 slava Exp $ +./factor/lists.factor:! $Id: lists.factor,v 1.10 2004/01/06 06:59:38 slava Exp $ +./factor/FactorCallFrame.java: * $Id: FactorCallFrame.java,v 1.3 2003/12/26 00:36:30 slava Exp $ +./factor/examples/httpd.factor:! $Id: httpd.factor,v 1.1 2004/01/15 03:00:18 slava Exp $ +./factor/FactorPrimitive.java: * $Id: FactorPrimitive.java,v 1.5 2004/01/11 23:00:34 slava Exp $ +./factor/FactorList.java: * $Id: FactorList.java,v 1.9 2004/01/03 20:58:09 slava Exp $ +./factor/FactorNamespace.java: * $Id: FactorNamespace.java,v 1.10 2004/01/08 04:14:14 slava Exp $ +./factor/PublicCloneable.java: * $Id: PublicCloneable.java,v 1.3 2003/12/09 09:40:18 slava Exp $ +./factor/examples.factor:! $Id: examples.factor,v 1.4 2004/01/12 02:07:31 slava Exp $ +./factor/FactorCompoundDefinition.java:* $Id: FactorCompoundDefinition.java,v 1.2 2004/01/14 01:58:37 slava Exp $ +./factor/FactorObject.java: * $Id: FactorObject.java,v 1.5 2003/12/19 21:40:00 slava Exp $ +./factor/FactorParser.java: * $Id: FactorParser.java,v 1.13 2004/01/10 23:24:30 slava Exp $ +./factor/FactorMath.java: * $Id: FactorMath.java,v 1.7 2003/12/26 00:36:30 slava Exp $ +./factor/parser.factor:! $Id: parser.factor,v 1.2 2003/12/20 04:30:06 slava Exp $ +./factor/FactorMissingDefinition.java: * $Id: FactorWordDefinition.java,v 1.10 2003/12/18 00:01:39 slava Exp $ +./factor/stream.factor:! $Id: stream.factor,v 1.1 2004/01/03 20:58:09 slava Exp $ +./factor/FactorReflectionForm.java:* $Id: FactorReflectionForm.java,v 1.1 2004/01/14 01:58:37 slava Exp $ +./factor/strings.factor:! $Id: strings.factor,v 1.4 2004/01/06 06:59:38 slava Exp $ +./factor/FactorInterpreter.java: * $Id: FactorInterpreter.java,v 1.32 2004/01/12 02:07:31 slava Exp $ +./factor/FactorDomainException.java: * $Id: FactorDomainException.java,v 1.3 2003/12/09 09:40:18 slava Exp $ +./factor/FactorWord.java: * $Id: FactorWord.java,v 1.10 2004/01/08 04:14:14 slava Exp $ +./factor/math.factor:! $Id: math.factor,v 1.11 2004/01/11 23:00:34 slava Exp $ +./factor/FactorWordDefinition.java: * $Id: FactorWordDefinition.java,v 1.17 2004/01/14 01:58:37 slava Exp $ +./factor/interpreter.factor:! $Id: interpreter.factor,v 1.14 2004/01/11 23:00:34 slava Exp $ +./factor/miscellaneous.factor:! $Id: miscellaneous.factor,v 1.5 2003/12/21 05:29:46 slava Exp $ +./factor/FactorArrayStack.java: * $Id: FactorArrayStack.java,v 1.3 2003/12/20 05:31:01 slava Exp $ +./factor/boot.factor:! $Id: boot.factor,v 1.12 2004/01/12 02:07:31 slava Exp $ +./factor/FactorParseException.java: * $Id: FactorParseException.java,v 1.3 2003/12/09 09:40:18 slava Exp $ +./factor/namespaces.factor:! $Id: namespaces.factor,v 1.4 2004/01/05 05:03:36 slava Exp $ +./factor/FactorException.java: * $Id: FactorException.java,v 1.3 2003/12/09 09:40:18 slava Exp $ +./factor/FactorShuffleDefinition.java: * $Id: FactorShuffleDefinition.java,v 1.5 2004/01/14 01:58:37 slava Exp $ +./factor/dictionary.factor:! $Id: dictionary.factor,v 1.6 2004/01/12 02:07:31 slava Exp $ diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..5aef12b0a0 --- /dev/null +++ b/META-INF/MANIFEST.MF @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +Created-By: 1.4.2-p6-slava_18_jan_2004_13_00 (Sun Microsystems Inc.) +Ant-Version: Apache Ant 1.5.4 +Main-Class: factor.FactorInterpreter + diff --git a/factor/FactorArrayStack.java b/factor/FactorArrayStack.java new file mode 100644 index 0000000000..393f0c9b3f --- /dev/null +++ b/factor/FactorArrayStack.java @@ -0,0 +1,160 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +/** + * Factor is a stack-based language. + * @author Slava Pestov + */ +public abstract class FactorArrayStack implements FactorExternalizable +{ + public Object[] stack; + public int top; + + //{{{ FactorArrayStack constructor + public FactorArrayStack() + { + } //}}} + + //{{{ FactorArrayStack constructor + public FactorArrayStack(FactorList list) + { + if(list != null) + { + ensurePush(list.length()); + while(list != null) + { + push(list.car); + list = list.next(); + } + } + } //}}} + + //{{{ FactorArrayStack constructor + public FactorArrayStack(Object[] stack, int top) + { + this.stack = stack; + this.top = top; + } //}}} + + //{{{ pop() method + public Object pop(Class clas) throws Exception + { + return FactorJava.convertToJavaType(pop(),clas); + } //}}} + + //{{{ pop() method + public Object pop() throws FactorStackException + { + ensurePop(1); + Object returnValue = stack[--top]; + if(shouldClear(returnValue)) + stack[top] = null; + return returnValue; + } //}}} + + //{{{ shouldClear() method + /** + * Some data (arbitrary objects) should be removed from the stack as + * soon as they're popped, but some (callframes) should be left on and + * reused later. + */ + public abstract boolean shouldClear(Object o); + //}}} + + //{{{ ensurePop() method + public void ensurePop(int amount) throws FactorStackException + { + if(amount > top) + throw new FactorStackException(amount); + } //}}} + + //{{{ push() method + public void push(Object o) + { + ensurePush(1); + stack[top++] = o; + } //}}} + + //{{{ pushAll() method + public void pushAll(Object[] array) + { + ensurePush(array.length); + System.arraycopy(array,0,stack,top,array.length); + top += array.length; + } //}}} + + //{{{ ensurePush() method + public void ensurePush(int amount) + { + if(stack == null) + stack = new Object[64]; + + if(top + amount > stack.length) + { + Object[] newStack = new Object[stack.length * 2 + 1]; + System.arraycopy(stack,0,newStack,0,top); + stack = newStack; + } + } //}}} + + //{{{ toList() method + public FactorList toList() + { + FactorList first = null, last = null; + for(int i = 0; i < top; i++) + { + FactorList cons = new FactorList(stack[i],null); + if(first == null) + first = cons; + else + last.cdr = cons; + last = cons; + } + return first; + } //}}} + + //{{{ toString() method + public String toString() + { + StringBuffer buf = new StringBuffer(); + for(int i = 0; i < top; i++) + { + if(i != 0) + buf.append('\n'); + buf.append(i).append(": "); + if(stack[i] == this) + buf.append("THIS STACK"); + else + buf.append(FactorJava.factorTypeToString(stack[i])); + } + return buf.toString(); + } //}}} +} diff --git a/factor/FactorCallFrame.java b/factor/FactorCallFrame.java new file mode 100644 index 0000000000..5f80b4a58e --- /dev/null +++ b/factor/FactorCallFrame.java @@ -0,0 +1,115 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +public class FactorCallFrame implements PublicCloneable, FactorExternalizable +{ + /** + * Word being evaluated. + */ + public FactorWord word; + + /** + * Namespace. + */ + public FactorNamespace namespace; + + /** + * Next word to be evaluated. + */ + public FactorList ip; + + /** + * Collapsed tail calls? See call(). + */ + public boolean collapsed; + + //{{{ FactorCallFrame constructor + public FactorCallFrame() + { + } //}}} + + //{{{ FactorCallFrame constructor + public FactorCallFrame(FactorWord word, + FactorNamespace namespace, + FactorList ip) + { + this.word = word; + this.namespace = namespace; + this.ip = ip; + } //}}} + + //{{{ toString() method + public String toString() + { + return toString(0); + } //}}} + + //{{{ toString() method + public String toString(int level) + { + StringBuffer buf = new StringBuffer(); + for(int i = 0; i < level; i++) + { + buf.append(" "); + } + String indent = buf.toString(); + + buf = new StringBuffer(); + buf.append(indent); + + if(collapsed) + buf.append("at ... then "); + + buf.append("at ").append(word); + + buf.append('\n').append(indent); + + buf.append(" namespace: ").append(namespace) + .append('\n').append(indent); + + buf.append(" ip: ").append(ip); + + return buf.toString(); + } //}}} + + //{{{ clone() method + public Object clone() + { + try + { + return super.clone(); + } + catch(Exception e) + { + return null; + } + } //}}} +} diff --git a/factor/FactorCallStack.java b/factor/FactorCallStack.java new file mode 100644 index 0000000000..565e788816 --- /dev/null +++ b/factor/FactorCallStack.java @@ -0,0 +1,65 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +/** + * The call stack. Stores call frames. + * @author Slava Pestov + */ +public class FactorCallStack extends FactorArrayStack implements PublicCloneable +{ + //{{{ FactorCallStack constructor + public FactorCallStack() + { + } //}}} + + //{{{ FactorCallStack constructor + public FactorCallStack(Object[] stack, int top) + { + super(stack,top); + } //}}} + + //{{{ shouldClear() method + /** + * Some data (arbitrary objects) should be removed from the stack as + * soon as they're popped, but some (callframes) should be left on and + * reused later. + */ + public boolean shouldClear(Object o) + { + return !(o instanceof FactorCallFrame); + } //}}} + + //{{{ clone() method + public Object clone() + { + return new FactorCallStack(FactorLib.deepCloneArray(stack),top); + } //}}} +} diff --git a/factor/FactorCompoundDefinition.java b/factor/FactorCompoundDefinition.java new file mode 100644 index 0000000000..391f1769c7 --- /dev/null +++ b/factor/FactorCompoundDefinition.java @@ -0,0 +1,317 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* +* $Id$ +* +* Copyright (C) 2003, 2004 Slava Pestov. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package factor; + +import java.lang.reflect.*; +import org.objectweb.asm.*; + +/** + * : name ... ; + */ +public class FactorCompoundDefinition extends FactorWordDefinition +{ + public FactorList definition; + + public FactorCompoundDefinition(FactorList definition) + { + this.definition = definition; + } + + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + interp.call(word,definition); + } + + //{{{ canCompile() method + boolean canCompile() + { + return true; + } //}}} + + //{{{ compile() method + /** + * Write the definition of the eval() method in the compiled word. + * Local 0 -- this + * Local 1 -- word + * Local 2 -- interpreter + */ + boolean compile(FactorWord word, FactorInterpreter interp, + ClassWriter cw, CodeVisitor mw) + throws Exception + { + if(definition == null) + { + mw.visitInsn(RETURN); + // Max stack and locals + mw.visitMaxs(1,1); + return true; + } + + FactorList fdef = compilePass1(interp,definition); + if(fdef.car instanceof FactorReflectionForm + && fdef.cdr == null) + { + return ((FactorReflectionForm)fdef.car).compile( + word,interp,cw,mw); + } + /* else + System.err.println("WARNING: cannot compile reflection & more"); */ + + return false; + } //}}} + + //{{{ compilePass1() method + /** + * Turn reflection calls into ReflectionForm objects. + */ + private FactorList compilePass1(FactorInterpreter interp, FactorList def) + { + if(!def.isProperList()) + return def; + + FactorList rdef = def.reverse(); + + FactorDictionary dict = interp.dict; + + // A list of words and Java reflection forms + FactorList fdef = null; + while(rdef != null) + { + Object car = rdef.car; + if(car == dict.jvarGet + || car == dict.jvarSet + || car == dict.jvarGetStatic + || car == dict.jvarSetStatic + || car == dict.jnew) + { + FactorList form = rdef; + rdef = form._get(3); + fdef = new FactorList(new FactorReflectionForm(form), + fdef); + } + else if(car == dict.jinvoke + || car == dict.jinvokeStatic) + { + FactorList form = rdef; + rdef = form._get(4); + fdef = new FactorList(new FactorReflectionForm(form), + fdef); + } + else if(car instanceof FactorList) + { + fdef = new FactorList(compilePass1( + interp,((FactorList)car)),fdef); + } + else + fdef = new FactorList(car,fdef); + + rdef = rdef.next(); + } + + return fdef; + } //}}} + + //{{{ precompile() method + void precompile(FactorWord newWord, FactorInterpreter interp) + throws Exception + { + FactorDictionary dict = interp.dict; + + if(definition != null) + { + FactorList before = definition; + FactorList fed = definition.reverse(); + precompile(interp,newWord,fed); + definition = fed.reverse(); + /* if(!def.equals(before)) + { + System.out.println("BEFORE: " + before); + System.out.println("AFTER: " + def); + } */ + } + } //}}} + + //{{{ precompile() method + /** + * Precompiling turns jconstructor, jfield and jmethod calls + * with all-literal arguments into inline + * Constructor/Field/Method literals. This improves performance. + */ + private void precompile(FactorInterpreter interp, + FactorWord newWord, FactorList list) + throws Exception + { + if(interp.compile) + return; + + FactorDictionary dict = interp.dict; + + while(list != null) + { + Object o = list.car; + if(o instanceof FactorWord) + { + FactorWord word = (FactorWord)o; + if(word.def != FactorMissingDefinition.INSTANCE) + { + word.def.references++; + } + else + { + /*System.err.println( + "WARNING: " + + newWord + + " references " + + o + + " before its defined");*/ + } + + if(o == dict.jconstructor) + { + jconstructorPrecompile( + interp,list); + } + else if(o == dict.jmethod) + { + jmethodPrecompile( + interp,list); + } + else if(o == dict.jfield) + { + jfieldPrecompile( + interp,list); + } + } + else if(o instanceof FactorList) + { + if(((FactorList)o).isProperList()) + { + FactorList l = (FactorList)o; + FactorList _l = l.reverse(); + precompile(interp,newWord,_l); + list.car = _l.reverse(); + } + } + list = list.next(); + } + } //}}} + + //{{{ jconstructorPrecompile() method + private void jconstructorPrecompile( + FactorInterpreter interp, FactorList list) + throws Exception + { + FactorList cdr = list.next(); + if(cdr == null) + return; + if(!(cdr.car instanceof String)) + return; + String clazz = (String)cdr.car; + + FactorList cddr = cdr.next(); + if(cddr == null) + return; + if(!(cddr.car instanceof FactorList)) + return; + FactorList args = (FactorList)cddr.car; + + Constructor c = FactorJava.jconstructor(clazz,args); + + list.car = c; + list.cdr = cddr.next(); + } //}}} + + //{{{ jfieldPrecompile() method + private void jfieldPrecompile( + FactorInterpreter interp, FactorList list) + throws Exception + { + FactorList cdr = list.next(); + if(cdr == null) + return; + if(!(cdr.car instanceof String)) + return; + String field = (String)cdr.car; + + FactorList cddr = cdr.next(); + if(cddr == null) + return; + if(!(cddr.car instanceof String)) + return; + String clazz = (String)cddr.car; + + Field f = FactorJava.jfield(field,clazz); + + list.car = f; + list.cdr = cddr.next(); + } //}}} + + //{{{ jmethodPrecompile() method + /** + * Check if this jmethod has all-literal arguments, and if so, + * inline the result. + */ + private void jmethodPrecompile( + FactorInterpreter interp, FactorList list) + throws Exception + { + FactorList cdr = list.next(); + if(cdr == null) + return; + if(!(cdr.car instanceof String)) + return; + String method = (String)cdr.car; + + FactorList cddr = cdr.next(); + if(cddr == null) + return; + if(!(cddr.car instanceof String)) + return; + String clazz = (String)cddr.car; + + FactorList cdddr = cddr.next(); + if(cdddr == null) + return; + if(!(cdddr.car instanceof FactorList)) + return; + FactorList args = (FactorList)cdddr.car; + + Method m = FactorJava.jmethod(method,clazz,args); + + list.car = m; + list.cdr = cdddr.next(); + } //}}} + + public String toString() + { + return definition.elementsToString(); + } +} diff --git a/factor/FactorDataStack.java b/factor/FactorDataStack.java new file mode 100644 index 0000000000..87da5c185a --- /dev/null +++ b/factor/FactorDataStack.java @@ -0,0 +1,71 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +/** + * The data stack. Stores parameters passed between words. + * @author Slava Pestov + */ +public class FactorDataStack extends FactorArrayStack implements PublicCloneable +{ + //{{{ FactorDataStack constructor + public FactorDataStack() + { + } //}}} + + //{{{ FactorDataStack constructor + public FactorDataStack(FactorList list) + { + super(list); + } //}}} + + //{{{ FactorDataStack constructor + public FactorDataStack(Object[] stack, int top) + { + super(stack,top); + } //}}} + + //{{{ shouldClear() method + /** + * Some data (arbitrary objects) should be removed from the stack as + * soon as they're popped, but some (callframes) should be left on and + * reused later. + */ + public boolean shouldClear(Object o) + { + return true; + } //}}} + + //{{{ clone() method + public Object clone() + { + return new FactorDataStack(FactorLib.cloneArray(stack),top); + } //}}} +} diff --git a/factor/FactorDictionary.java b/factor/FactorDictionary.java new file mode 100644 index 0000000000..9968c58309 --- /dev/null +++ b/factor/FactorDictionary.java @@ -0,0 +1,137 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; + +public class FactorDictionary +{ + // these are defined here for use by the precompiler + FactorWord jconstructor; + FactorWord jnew; + FactorWord jfield; + FactorWord jvarGet; + FactorWord jvarSet; + FactorWord jvarGetStatic; + FactorWord jvarSetStatic; + FactorWord jmethod; + FactorWord jinvoke; + FactorWord jinvokeStatic; + + private Map intern; + + //{{{ init() method + public void init() + { + intern = new TreeMap(); + + // data stack primitives + intern("datastack$").def = new FactorPrimitive.P_datastackGet(); + intern("datastack@").def = new FactorPrimitive.P_datastackSet(); + intern("clear").def = new FactorPrimitive.P_clear(); + + // call stack primitives + intern("callstack$").def = new FactorPrimitive.P_callstackGet(); + intern("callstack@").def = new FactorPrimitive.P_callstackSet(); + intern("restack").def = new FactorPrimitive.P_restack(); + intern("unstack").def = new FactorPrimitive.P_unstack(); + intern("unwind").def = new FactorPrimitive.P_unwind(); + + // reflection primitives + jconstructor = intern("jconstructor"); + jconstructor.def = new FactorPrimitive.P_jconstructor(); + jfield = intern("jfield"); + jfield.def = new FactorPrimitive.P_jfield(); + jinvoke = intern("jinvoke"); + jinvoke.def = new FactorPrimitive.P_jinvoke(); + jinvokeStatic = intern("jinvokeStatic"); + jinvokeStatic.def = new FactorPrimitive.P_jinvokeStatic(); + jmethod = intern("jmethod"); + jmethod.def = new FactorPrimitive.P_jmethod(); + jnew = intern("jnew"); + jnew.def = new FactorPrimitive.P_jnew(); + jvarGet = intern("jvar$"); + jvarGet.def = new FactorPrimitive.P_jvarGet(); + jvarGetStatic = intern("jvarStatic$"); + jvarGetStatic.def = new FactorPrimitive.P_jvarGetStatic(); + jvarSet = intern("jvar@"); + jvarSet.def = new FactorPrimitive.P_jvarSet(); + jvarSetStatic = intern("jvarStatic@"); + jvarSetStatic.def = new FactorPrimitive.P_jvarSetStatic(); + + // namespaces + intern("$").def = new FactorPrimitive.P_get(); + intern("@").def = new FactorPrimitive.P_set(); + intern("s@").def = new FactorPrimitive.P_swap_set(); + + // definition + intern("define").def = new FactorPrimitive.P_define(); + + // combinators + intern("call").def = new FactorPrimitive.P_call(); + intern("bind").def = new FactorPrimitive.P_bind(); + } //}}} + + //{{{ intern() method + public FactorWord intern(String name) + { + FactorWord w = (FactorWord)intern.get(name); + if(w == null) + { + w = new FactorWord(name); + intern.put(name,w); + } + return w; + } //}}} + + //{{{ toWordList() method + public FactorList toWordList() + { + FactorList first = null; + FactorList last = null; + Iterator iter = intern.values().iterator(); + while(iter.hasNext()) + { + FactorWord word = (FactorWord)iter.next(); + if(word.def != FactorMissingDefinition.INSTANCE) + { + FactorList cons = new FactorList(word,null); + if(first == null) + first = cons; + else + last.cdr = cons; + last = cons; + } + } + return first; + } //}}} +} diff --git a/factor/FactorDomainException.java b/factor/FactorDomainException.java new file mode 100644 index 0000000000..df3f432ae4 --- /dev/null +++ b/factor/FactorDomainException.java @@ -0,0 +1,38 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +public class FactorDomainException extends FactorRuntimeException +{ + public FactorDomainException(Object arg, Class clas) + { + super("Cannot turn " + arg + " into " + clas); + } +} diff --git a/factor/FactorException.java b/factor/FactorException.java new file mode 100644 index 0000000000..37c0ee44e3 --- /dev/null +++ b/factor/FactorException.java @@ -0,0 +1,54 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import java.lang.reflect.InvocationTargetException; + +public class FactorException extends Exception +{ + public FactorException(String str) + { + super(str); + } + + public FactorException(String str, Throwable t) + { + super(str,unwrap(t)); + } + + private static Throwable unwrap(Throwable t) + { + if(t instanceof InvocationTargetException) + return ((InvocationTargetException)t) + .getTargetException(); + else + return t; + } +} diff --git a/factor/FactorExternalizable.java b/factor/FactorExternalizable.java new file mode 100644 index 0000000000..a04cd9443e --- /dev/null +++ b/factor/FactorExternalizable.java @@ -0,0 +1,39 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +/** + * Called if the object has a string representation that is understood + * by the parser. + */ +public interface FactorExternalizable +{ + String toString(); +} diff --git a/factor/FactorInterpreter.java b/factor/FactorInterpreter.java new file mode 100644 index 0000000000..20bf101faf --- /dev/null +++ b/factor/FactorInterpreter.java @@ -0,0 +1,357 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import java.io.*; + +public class FactorInterpreter +{ + /** + * boot.factor checks this, if its true, an interpreter is run on + * standard input. + */ + public boolean interactive = false; + + public boolean trace = false; + public boolean errorFlag = false; + public boolean compile = true; + + public FactorCallFrame callframe; + public FactorCallStack callstack = new FactorCallStack(); + public FactorDataStack datastack = new FactorDataStack(); + public final FactorDictionary dict = new FactorDictionary(); + public FactorNamespace global; + + //{{{ main() method + /** + * Need to refactor this into Factor. + */ + public static void main(String[] args) throws Exception + { + FactorInterpreter interp = new FactorInterpreter(); + + boolean virgin = false; + + for(int i = 0; i < args.length; i++) + { + if(args[i].equals("-trace")) + interp.trace = true; + else if(args[i].equals("-virgin")) + virgin = true; + else if(args[i].equals("-interp")) + interp.compile = false; + } + + interp.interactive = true; + interp.init(null,!virgin); + if(virgin) + { + System.out.println("Mini-interpreter"); + BufferedReader in = new BufferedReader( + new InputStreamReader( + System.in)); + String line; + for(;;) + { + System.out.print("] "); + System.out.flush(); + line = in.readLine(); + if(line == null) + break; + + FactorParser parser = new FactorParser( + "",new StringReader(line), + interp.dict); + FactorList parsed = parser.parse(); + interp.call(parsed); + interp.run(); + System.out.println(interp.datastack); + } + } + else + { + interp.run(); + } + + System.exit(0); + } //}}} + + //{{{ init() method + public void init(Object root, boolean bootstrap) throws Exception + { + callstack.top = 0; + datastack.top = 0; + dict.init(); + initNamespace(root); + topLevel(); + if(bootstrap) + runBootstrap(); + } //}}} + + //{{{ initNamespace() method + private void initNamespace(Object root) throws Exception + { + global = new FactorNamespace(null,root); + + global.setVariable("interpreter",this); + + String[] boundFields = { "compile", "interactive", "trace", + "dict", "errorFlag" }; + for(int i = 0; i < boundFields.length; i++) + { + global.setVariable(boundFields[i], + new FactorNamespace.VarBinding( + getClass().getField(boundFields[i]), + this)); + } + } //}}} + + //{{{ runBootstrap() method + private void runBootstrap() throws Exception + { + final String initFile = "boot.factor"; + FactorParser parser = new FactorParser( + initFile, + new InputStreamReader( + getClass().getResourceAsStream( + initFile)), + dict); + call(dict.intern("[init]"),parser.parse()); + run(); + } //}}} + + //{{{ run() method + /** + * Runs the top-level loop until there is no more code to execute. + */ + public void run() + { + for(;;) + { + try + { + if(callframe == null) + break; + + FactorList ip = callframe.ip; + + if(ip == null) + { + if(callstack.top == 0) + break; + + try + { + callframe = (FactorCallFrame) + callstack.pop(); + continue; + } + catch(ClassCastException e) + { + throw new FactorRuntimeException( + "Unbalanced >r/r> or " + + "restack/unstack"); + } + } + + callframe.ip = ip.next(); + + eval(ip.car); + } + catch(Exception e) + { + if(handleError(e)) + return; + } + } + + callframe = null; + } //}}} + + //{{{ handleError() method + private boolean handleError(Exception e) + { + /* if(throwErrors) + { + throw e; + } + else */ if(errorFlag) + { + System.err.println("Exception inside" + + " error handler:"); + e.printStackTrace(); + System.err.println( + "Original exception:"); + e.printStackTrace(); + System.err.println("Factor callstack:"); + System.err.println(callstack); + + return true; + } + else + { + errorFlag = true; + datastack.push(FactorJava.unwrapException(e)); + try + { + eval(dict.intern("break")); + return false; + } + catch(Exception e2) + { + System.err.println("Exception when calling break:"); + e.printStackTrace(); + System.err.println("Factor callstack:"); + System.err.println(callstack); + + return true; + } + } + } //}}} + + //{{{ call() method + /** + * Pushes the given list of code onto the callstack. + */ + public final void call(FactorList code) + { + call(dict.intern("call"),code); + } //}}} + + //{{{ call() method + /** + * Pushes the given list of code onto the callstack. + */ + public final void call(FactorWord word, FactorList code) + { + if(callframe == null) + call(word,global,code); + else + call(word,callframe.namespace,code); + } //}}} + + //{{{ call() method + /** + * Pushes the given list of code onto the callstack. + */ + public final void call(FactorWord word, FactorNamespace namespace, FactorList code) + { + FactorCallFrame newcf; + + // tail call optimization + if(callframe != null && callframe.ip == null) + { + if(trace) + System.err.println("-- TAIL CALL --"); + newcf = getRecycledCallFrame(callstack.top); + newcf.collapsed = true; + } + // try to get a recycled callframe from the stack + else + { + newcf = getRecycledCallFrame(callstack.top + 1); + newcf.collapsed = false; + if(callframe != null) + callstack.push(callframe); + } + + newcf.word = word; + newcf.namespace = namespace; + newcf.ip = code; + + callframe = newcf; + } //}}} + + //{{{ getRecycledCallFrame() method + private FactorCallFrame getRecycledCallFrame(int next) + { + /* if(callstack.stack != null && next < callstack.stack.length) + { + Object o = callstack.stack[next]; + if(o instanceof FactorCallFrame) + return (FactorCallFrame)o; + else + return new FactorCallFrame(); + } + else */ + return new FactorCallFrame(); + } //}}} + + //{{{ eval() method + /** + * Evaluates a word. + */ + private void eval(Object obj) throws Exception + { + if(trace) + { + StringBuffer buf = new StringBuffer(); + for(int i = 0; i < callstack.top; i++) + buf.append(' '); + buf.append(FactorJava.factorTypeToString(obj)); + System.err.println(buf); + } + + if(obj instanceof FactorWord) + { + FactorWord w = (FactorWord)obj; + + try + { + w.def.eval(w,this); + } + catch(Exception e) + { + callstack.push(callframe); + callframe = new FactorCallFrame( + w, + callframe.namespace, + null); + throw e; + } + } + else + datastack.push(obj); + } //}}} + + //{{{ topLevel() method + /** + * Returns the parser to the top level context. + */ + public void topLevel() + { + callstack.top = 0; + datastack.top = 0; + callframe = new FactorCallFrame( + dict.intern("[toplevel]"), + global, + null); + } //}}} +} diff --git a/factor/FactorJava.java b/factor/FactorJava.java new file mode 100644 index 0000000000..0c5802eb04 --- /dev/null +++ b/factor/FactorJava.java @@ -0,0 +1,522 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003, 2004 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import java.lang.reflect.*; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * A few methods for converting between Java types, and making reflection calls. + * Note that the compiler incorporates calls to some of these methods in + * generated bytecode. + */ +public class FactorJava +{ + public static final Class[] EMPTY_ARRAY = new Class[0]; + + //{{{ classNameToClassList() method + public static Class[] classNameToClassList(FactorList classes) + throws Exception + { + if(classes == null) + return EMPTY_ARRAY; + + Class[] _classes = new Class[classes.length()]; + int i = 0; + while(classes != null) + { + _classes[i++] = (Class)classes.car(Class.class); + classes = classes.next(); + } + + return _classes; + } //}}} + + //{{{ toNumber() method + public static Number toNumber(Object arg) + throws FactorDomainException + { + if(arg instanceof Number) + return (Number)arg; + else if(arg instanceof String) + { + String str = (String)arg; + if(str.indexOf('.') != -1) + return new Float(str); + else + return new Integer(str); + } + else + throw new FactorDomainException(arg,Number.class); + } //}}} + + //{{{ toString() method + public static String toString(Object arg) + { + if(arg == null) + return null; + else if(arg instanceof String) + return (String)arg; + else + return String.valueOf(arg); + } //}}} + + //{{{ toBoolean() method + public static boolean toBoolean(Object arg) + { + if(Boolean.FALSE.equals(arg) || arg == null) + return false; + else + return true; + } //}}} + + //{{{ toChar() method + public static char toChar(Object arg) + throws FactorDomainException + { + if(arg instanceof Character) + return ((Character)arg).charValue(); + else if(arg == null) + return '\0'; + else if(arg instanceof String) + { + String s = (String)arg; + if(s.length() != 1) + throw new FactorDomainException(arg,char.class); + return s.charAt(0); + } + else if(arg instanceof Number) + { + return (char)((Number)arg).intValue(); + } + else + throw new FactorDomainException(arg,char.class); + } //}}} + + //{{{ toInt() method + public static int toInt(Object arg) + throws FactorDomainException + { + if(arg instanceof Number) + return ((Number)arg).intValue(); + else if(arg instanceof String) + return Integer.parseInt((String)arg); + else + throw new FactorDomainException(arg,int.class); + } //}}} + + //{{{ toLong() method + public static long toLong(Object arg) + throws FactorDomainException + { + if(arg instanceof Number) + return ((Number)arg).longValue(); + else if(arg instanceof String) + return Long.parseLong((String)arg); + else + throw new FactorDomainException(arg,long.class); + } //}}} + + //{{{ toFloat() method + public static float toFloat(Object arg) + throws FactorDomainException + { + if(arg instanceof Number) + return ((Number)arg).floatValue(); + else if(arg instanceof String) + return Float.parseFloat((String)arg); + else + throw new FactorDomainException(arg,float.class); + } //}}} + + //{{{ toDouble() method + public static double toDouble(Object arg) + throws FactorDomainException + { + if(arg instanceof Number) + return ((Number)arg).doubleValue(); + else if(arg instanceof String) + return Double.parseDouble((String)arg); + else + throw new FactorDomainException(arg,double.class); + } //}}} + + //{{{ toClass() method + public static Class toClass(Object arg) + throws Exception + { + if(arg instanceof Class) + return (Class)arg; + else + { + return getClass((String) + convertToJavaType(arg,String.class)); + } + } //}}} + + //{{{ toArray() method + public static Object[] toArray(Object arg) + throws FactorDomainException + { + return toArray(arg,Object.class); + } //}}} + + //{{{ toArray() method + public static Object[] toArray(Object arg, Class clas) + throws FactorDomainException + { + if(arg instanceof FactorList) + { + FactorList list = (FactorList)arg; + Object[] array = (Object[]) + Array.newInstance( + clas.getComponentType(), + list.length()); + list.toArray(array); + return array; + } + else if(arg.getClass().isArray()) + return (Object[])arg; + else + throw new FactorDomainException(arg,Object[].class); + } //}}} + + //{{{ convertToJavaType() method + public static Object convertToJavaType(Object arg, Class clas) + throws Exception + { + if(clas == Object.class) + return arg; + else if(clas == Number.class) + { + return toNumber(arg); + } + else if(clas == String.class) + { + return toString(arg); + } + else if(clas == boolean.class) + { + return toBoolean(arg) + ? Boolean.TRUE + : Boolean.FALSE; + } + else if(clas == char.class) + { + return new Character(toChar(arg)); + } + else if(clas == int.class) + { + return new Integer(toInt(arg)); + } + else if(clas == long.class) + { + return new Long(toLong(arg)); + } + else if(clas == float.class) + { + return new Float(toFloat(arg)); + } + else if(clas == double.class) + { + return new Double(toDouble(arg)); + } + else if(clas == Class.class) + { + return toClass(arg); + } + else if(clas.isArray()) + { + return toArray(arg,clas); + } + + if(arg != null && !clas.isInstance(arg)) + throw new FactorDomainException(arg,clas); + else + return arg; + } //}}} + + //{{{ fromBoolean() method + public static Object fromBoolean(boolean b) + { + return (b ? Boolean.TRUE : null); + } //}}} + + //{{{ convertFromJavaType() method + public static Object convertFromJavaType(Object arg) + { + if(Boolean.FALSE.equals(arg)) + return null; + else + return arg; + } //}}} + + //{{{ factorTypeToString() method + public static String factorTypeToString(Object obj) + { + // this is for string representations of lists and stacks + if(obj == null || obj.equals(Boolean.FALSE)) + return "f"; + else if(obj.equals(Boolean.TRUE)) + return "t"; + else if(obj instanceof String) + return '"' + obj.toString() + '"'; //XXX: escape + else if(obj instanceof Number + || obj instanceof FactorExternalizable) + return obj.toString(); + else if(obj instanceof Character) + return "#\\" + ((Character)obj).charValue(); + else + return "(" + obj + ")"; + } //}}} + + //{{{ javaClassToVMClass() method + public static String javaClassToVMClass(Class clazz) + { + String name = clazz.getName(); + + if(clazz.isArray()) + return clazz.getName().replace('.','/'); + else if(name.equals("boolean")) + return "Z"; + else if(name.equals("byte")) + return "B"; + else if(name.equals("char")) + return "C"; + else if(name.equals("double")) + return "D"; + else if(name.equals("float")) + return "F"; + else if(name.equals("int")) + return "I"; + else if(name.equals("long")) + return "J"; + else if(name.equals("short")) + return "S"; + else if(name.equals("void")) + return "V"; + else + return "L" + clazz.getName().replace('.','/') + ";"; + } //}}} + + //{{{ javaBoxingType() method + public static Class javaBoxingType(Class clazz) + { + if(clazz == Byte.TYPE) + return Byte.class; + else if(clazz == Character.TYPE) + return Character.class; + else if(clazz == Double.TYPE) + return Double.class; + else if(clazz == Float.TYPE) + return Float.class; + else if(clazz == Integer.TYPE) + return Integer.class; + else if(clazz == Long.TYPE) + return Long.class; + else if(clazz == Short.TYPE) + return Short.class; + else + return null; + } //}}} + + //{{{ javaSignatureToVMSignature() method + public static String javaSignatureToVMSignature(Class[] args, + Class returnType) + { + StringBuffer buf = new StringBuffer("("); + for(int i = 0; i < args.length; i++) + { + buf.append(javaClassToVMClass(args[i])); + } + buf.append(")"); + buf.append(javaClassToVMClass(returnType)); + return buf.toString(); + } //}}} + + //{{{ getClass() method + public static Class getClass(String name) throws ClassNotFoundException + { + if(name.equals("boolean")) + return Boolean.TYPE; + else if(name.equals("byte")) + return Byte.TYPE; + else if(name.equals("char")) + return Character.TYPE; + else if(name.equals("double")) + return Double.TYPE; + else if(name.equals("float")) + return Float.TYPE; + else if(name.equals("int")) + return Integer.TYPE; + else if(name.equals("long")) + return Long.TYPE; + else if(name.equals("short")) + return Short.TYPE; + else + return Class.forName(name); + } //}}} + + //{{{ jconstructor() method + public static Constructor jconstructor(String inClass, FactorList args) + throws Exception + { + return Class.forName(inClass).getConstructor( + classNameToClassList(args)); + } //}}} + + //{{{ jfield() method + public static Field jfield(String field, String inClass) + throws Exception + { + return Class.forName(inClass).getField(field); + } //}}} + + //{{{ jinvoke() method + public static void jinvoke(FactorDataStack stack, Method method, + Object instance) throws Exception + { + if(instance != null) + { + instance = convertToJavaType(instance, + method.getDeclaringClass()); + } + + Class[] paramTypes = method.getParameterTypes(); + Object[] args = new Object[paramTypes.length]; + + try + { + for(int i = args.length - 1; i >= 0; i--) + { + args[i] = convertToJavaType(stack.pop(), + paramTypes[i]); + } + + if(Modifier.isStatic(method.getModifiers())) + { + if(instance != null) + throw new FactorRuntimeException( + "Use jinvokeStatic with static methods"); + } + else + { + if(instance == null) + throw new FactorRuntimeException( + "Cannot jinvoke on f"); + } + + if(method.getReturnType() == Void.TYPE) + method.invoke(instance,args); + else + { + stack.push(convertFromJavaType( + method.invoke(instance,args))); + } + } + catch(FactorStackException e) + { + throw new FactorStackException(paramTypes.length); + } + } //}}} + + //{{{ jinvokeStatic() method + public static void jinvokeStatic(FactorDataStack stack, Method method) + throws Exception + { + jinvoke(stack,method,null); + } //}}} + + //{{{ jmethod() method + public static Method jmethod(String method, String inClass, + FactorList args) throws Exception + { + return Class.forName(inClass).getMethod(method, + classNameToClassList(args)); + } //}}} + + //{{{ jnew() method + public static void jnew(FactorDataStack stack, Constructor method) + throws Exception + { + Class[] paramTypes = method.getParameterTypes(); + Object[] args = new Object[paramTypes.length]; + for(int i = args.length - 1; i >= 0; i--) + { + args[i] = convertToJavaType(stack.pop(), + paramTypes[i]); + } + + stack.push(method.newInstance(args)); + } //}}} + + //{{{ jvarGet() method + public static Object jvarGet(Field field, Object obj) throws Exception + { + return convertFromJavaType(field.get(obj)); + } //}}} + + //{{{ jvarGetStatic() method + public static Object jvarGetStatic(Field field) throws Exception + { + return convertFromJavaType(field.get(null)); + } //}}} + + //{{{ jvarSet() method + public static void jvarSet(Field field, Object obj, Object value) + throws Exception + { + field.set(obj,convertToJavaType(value,field.getType())); + } //}}} + + //{{{ jvarSetStatic() method + public static void jvarSetStatic(Field field, Object value) + throws Exception + { + field.set(null,convertToJavaType(value,field.getType())); + } //}}} + + //{{{ unwrapException() method + public static Throwable unwrapException(Throwable e) + { + if(e instanceof InvocationTargetException) + { + return unwrapException( + ((InvocationTargetException)e) + .getTargetException()); + } + else if(e.getCause() != null) + return unwrapException(e.getCause()); + else + return e; + } //}}} +} diff --git a/factor/FactorLib.java b/factor/FactorLib.java new file mode 100644 index 0000000000..b2230d5bef --- /dev/null +++ b/factor/FactorLib.java @@ -0,0 +1,255 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import java.io.*; + +/** + * A few useful words. + */ +public class FactorLib +{ + //{{{ branch2() method + public static Object branch2(boolean condition, Object o1, Object o2) + { + return (condition ? o1 : o2); + } //}}} + + //{{{ branch3() method + public static Object branch3(float x, float y, + Object o1, Object o2, Object o3) + { + if(x > y) + return o1; + else if(x == y) + return o2; + else + return o3; + } //}}} + + //{{{ cat() method + public static String cat(FactorList list) + { + StringBuffer buf = new StringBuffer(); + + while(list != null) + { + if(list.car instanceof FactorList) + buf.append(cat((FactorList)list.car)); + else + buf.append(list.car); + list = list.next(); + } + + return buf.toString(); + } //}}} + + //{{{ cat2() method + public static String cat2(Object str1, Object str2) + { + return new StringBuffer().append(str1).append(str2).toString(); + } //}}} + + //{{{ cat3() method + public static String cat3(Object str1, Object str2, Object str3) + { + return new StringBuffer().append(str1).append(str2).append(str3) + .toString(); + } //}}} + + //{{{ cloneArray() method + public static Object[] cloneArray(Object[] array) + { + Object[] newArray = new Object[array.length]; + System.arraycopy(array,0,newArray,0,array.length); + return newArray; + } //}}} + + //{{{ deepCloneArray() method + public static Object[] deepCloneArray(Object[] array) + { + Object[] newArray = new Object[array.length]; + for(int i = 0; i < array.length; i++) + { + Object o = array[i]; + if(o instanceof PublicCloneable) + newArray[i] = ((PublicCloneable)o).clone(); + else + newArray[i] = o; + } + return newArray; + } //}}} + + //{{{ error() method + public static void error(String str) throws FactorRuntimeException + { + throw new FactorRuntimeException(str); + } //}}} + + //{{{ equal() method + public static boolean equal(Object o1, Object o2) + { + if(o1 == null) + return o2 == null; + else if(o1 instanceof Number + && o2 instanceof Number + && o1.getClass() != o2.getClass()) + { + // to compare different types of numbers, cast to a + // double first + return ((Number)o1).doubleValue() + == ((Number)o2).doubleValue(); + } + else if(o1 instanceof Number + && o2 instanceof String) + { + try + { + return Double.parseDouble((String)o2) + == ((Number)o1).doubleValue(); + } + catch(NumberFormatException nf) + { + return false; + } + } + else if(o1 instanceof String + && o2 instanceof Number) + { + try + { + return Double.parseDouble((String)o1) + == ((Number)o2).doubleValue(); + } + catch(NumberFormatException nf) + { + return false; + } + } + else + return o1.equals(o2); + } //}}} + + //{{{ exec() method + public static int exec(String[] args) throws Exception + { + int exitCode = -1; + + try + { + Process process = Runtime.getRuntime().exec(args); + process.getInputStream().close(); + process.getOutputStream().close(); + process.getErrorStream().close(); + exitCode = process.waitFor(); + } + catch(Exception e) + { + e.printStackTrace(); + // this needs to be handled better + /* stack.push(MiscUtilities.throwableToString(e)); + Console.print(stack,namespace); */ + } + + return exitCode; + } //}}} + + //{{{ objectsEqual() method + /** + * Returns if two objects are equal. This correctly handles null + * pointers, as opposed to calling o1.equals(o2). + */ + public static boolean objectsEqual(Object o1, Object o2) + { + if(o1 == null) + { + if(o2 == null) + return true; + else + return false; + } + else if(o2 == null) + return false; + else + return o1.equals(o2); + } //}}} + + //{{{ copy() method + /** + * Copies the contents of an input stream to an output stream. + */ + public static void copy(InputStream in, OutputStream out) + throws IOException + { + byte[] buf = new byte[4096]; + + int count; + + for(;;) + { + count = in.read(buf,0,buf.length); + if(count == -1 || count == 0) + break; + + out.write(buf,0,count); + } + + in.close(); + out.close(); + } //}}} + + //{{{ readLine() method + /** + * Reads a line of text from the given input stream. + */ + public static String readLine(InputStream in) throws IOException + { + StringBuffer buf = new StringBuffer(); + int b; + while((b = in.read()) != -1) + { + if(b == '\n') + { + if(in.markSupported() && in.available() >= 1) + { + in.mark(1); + b = in.read(); + if(b != '\r') + in.reset(); + } + break; + } + else if(b == '\r') + break; + buf.append((char)b); + } + return buf.toString(); + } //}}} +} diff --git a/factor/FactorList.java b/factor/FactorList.java new file mode 100644 index 0000000000..68ec146958 --- /dev/null +++ b/factor/FactorList.java @@ -0,0 +1,347 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import java.util.LinkedList; +import java.util.List; +/** + * Used to build up linked lists. + */ +public class FactorList implements PublicCloneable, FactorExternalizable +{ + public static int COUNT; + + public Object car; + public Object cdr; + + //{{{ FactorList constructor + public FactorList(Object car, Object cdr) + { + this.car = car; + this.cdr = cdr; + + COUNT++; + } //}}} + + //{{{ car() method + public Object car(Class clas) throws Exception + { + return FactorJava.convertToJavaType(car,clas); + } //}}} + + //{{{ cdr() method + public Object cdr(Class clas) throws Exception + { + return FactorJava.convertToJavaType(cdr,clas); + } //}}} + + //{{{ next() method + public FactorList next() + { + return (FactorList)cdr; + } //}}} + + //{{{ get() method + public Object get(int index) + { + return _get(index).car; + } //}}} + + //{{{ _get() method + public FactorList _get(int index) + { + FactorList iter = this; + while(index != 0) + { + iter = (FactorList)iter.cdr; + index--; + } + return iter; + } //}}} + + //{{{ contains() method + public boolean contains(Object obj) + { + FactorList iter = this; + while(iter != null) + { + if(FactorLib.objectsEqual(obj,iter.car)) + return true; + iter = iter.next(); + } + return false; + } //}}} + + //{{{ set() method + /** + * Returns a new list where the item at the given index is changed. + */ + public FactorList set(int index, Object car, Object cdr) + { + if(index == 0) + return new FactorList(car,cdr); + else + { + return new FactorList(this.car, + this.next().set(index - 1,car,cdr)); + } + } //}}} + + //{{{ set() method + /** + * Returns a new list containing the first n elements of this list. + */ + public FactorList head(int n) + { + if(n == 0) + return null; + else if(cdr == null && n == 1) + return this; + else + { + FactorList head = next().head(n - 1); + if(head == cdr) + return this; + else + return new FactorList(car,head); + } + } //}}} + + //{{{ length() method + public int length() + { + int size = 0; + FactorList iter = this; + while(iter != null) + { + iter = (FactorList)iter.cdr; + size++; + } + return size; + } //}}} + + //{{{ append() method + public FactorList append(FactorList list) + { + if(list == null) + return this; + + FactorList returnValue = null; + FactorList end = null; + FactorList iter = this; + for(;;) + { + FactorList cons = new FactorList(iter.car,null); + if(end != null) + end.cdr = cons; + end = cons; + if(returnValue == null) + returnValue = cons; + if(iter.cdr == null) + { + end.cdr = list; + break; + } + else + iter = iter.next(); + } + return returnValue; + } //}}} + + //{{{ reverse() method + public FactorList reverse() + { + FactorList returnValue = null; + FactorList iter = this; + while(iter != null) + { + returnValue = new FactorList(iter.car,returnValue); + iter = iter.next(); + } + return returnValue; + } //}}} + + //{{{ isProperList() method + public boolean isProperList() + { + return cdr == null || (cdr instanceof FactorList + && ((FactorList)cdr).isProperList()); + } //}}} + + //{{{ elementsToString() method + /** + * Returns a whitespace separated string of the toString() of each + * item. + */ + public String elementsToString() + { + StringBuffer buf = new StringBuffer(); + FactorList iter = this; + while(iter != null) + { + if(iter.car == this) + buf.append(""); + else + buf.append(FactorJava.factorTypeToString(iter.car)); + if(iter.cdr instanceof FactorList) + { + buf.append(' '); + iter = (FactorList)iter.cdr; + continue; + } + else if(iter.cdr == null) + break; + else + { + buf.append(" , "); + buf.append(FactorJava.factorTypeToString(iter.cdr)); + iter = null; + } + } + + return buf.toString(); + } //}}} + + //{{{ toString() method + /** + * Returns elementsToString() enclosed with [ and ]. + */ + public String toString() + { + return "[ " + elementsToString() + " ]"; + } //}}} + + //{{{ toJavaList() method + public List toJavaList() + { + LinkedList returnValue = new LinkedList(); + FactorList iter = this; + while(iter != null) + { + returnValue.add(iter.car); + iter = (FactorList)iter.cdr; + } + return returnValue; + } //}}} + + //{{{ toArray() method + /** + * Note that unlike Java list toArray(), the given array must already + * be the right size. + */ + public Object[] toArray(Object[] returnValue) + { + int i = 0; + FactorList iter = this; + while(iter != null) + { + returnValue[i++] = iter.car; + iter = iter.next(); + } + return returnValue; + } //}}} + + //{{{ fromArray() method + public static FactorList fromArray(Object[] array) + { + if(array == null || array.length == 0) + return null; + else + { + FactorList first = new FactorList(array[0],null); + FactorList last = first; + for(int i = 1; i < array.length; i++) + { + FactorList cons = new FactorList(array[i],null); + last.cdr = cons; + last = cons; + } + return first; + } + } //}}} + + //{{{ equals() method + public boolean equals(Object o) + { + if(o instanceof FactorList) + { + FactorList l = (FactorList)o; + return FactorLib.objectsEqual(car,l.car) + && FactorLib.objectsEqual(cdr,l.cdr); + } + else + return false; + } //}}} + + //{{{ hashCode() method + public int hashCode() + { + if(car == null) + return 0; + else + return car.hashCode(); + } //}}} + + //{{{ clone() method + public Object clone() + { + if(cdr instanceof FactorList) + return new FactorList(car,((FactorList)cdr).clone()); + else + return new FactorList(car,cdr); + } //}}} + + //{{{ deepClone() method + public Object deepClone() + { + Object ccar; + if(car instanceof PublicCloneable) + ccar = ((PublicCloneable)car).clone(); + else + ccar = car; + if(cdr instanceof FactorList) + { + return new FactorList(ccar,next().deepClone()); + } + else if(cdr == null) + { + return new FactorList(ccar,null); + } + else + { + Object ccdr; + if(cdr instanceof PublicCloneable) + ccdr = ((PublicCloneable)cdr).clone(); + else + ccdr = cdr; + return new FactorList(ccar,ccdr); + } + } //}}} +} diff --git a/factor/FactorMath.java b/factor/FactorMath.java new file mode 100644 index 0000000000..b03ceb6d0a --- /dev/null +++ b/factor/FactorMath.java @@ -0,0 +1,363 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import java.math.*; +import java.util.Random; + +/** + * Math-related words. + */ +public class FactorMath +{ + private static Random random = new Random(); + + //{{{ add() method + public static Number add(Number x, Number y) + { + if(x instanceof FactorRatio) + return ((FactorRatio)x).add(y); + else if(y instanceof FactorRatio) + return ((FactorRatio)y).add(x); + else if(x instanceof Integer) + { + int _x = ((Integer)x).intValue(); + + if(y instanceof Integer) + { + int _y = ((Integer)y).intValue(); + long result = (long)_x + (long)_y; + if(result > Integer.MAX_VALUE + || result < Integer.MIN_VALUE) + { + return BigInteger.valueOf(result); + } + else + { + return new Integer((int)result); + } + } + else if(y instanceof BigInteger) + { + return BigInteger.valueOf(_x) + .add((BigInteger)y); + } + } + else if(x instanceof BigInteger) + { + BigInteger _x = (BigInteger)x; + + if(y instanceof Integer) + { + return _x.add(BigInteger.valueOf( + ((Integer)y).intValue())); + } + else if(y instanceof BigInteger) + return _x.add((BigInteger)y); + } + + return new Double( + ((Number)x).doubleValue() + + ((Number)y).doubleValue()); + } //}}} + + //{{{ _divide() method + /** + * Truncating division. + */ + public static Number _divide(Number x, Number y) + { + if(x instanceof Integer) + { + int _x = ((Integer)x).intValue(); + + if(y instanceof Integer) + { + int _y = ((Integer)y).intValue(); + return new Integer(_x / _y); + } + else if(y instanceof BigInteger) + { + return BigInteger.valueOf(_x) + .divide((BigInteger)y); + } + } + else if(x instanceof BigInteger) + { + BigInteger _x = (BigInteger)x; + + if(y instanceof Integer) + { + return _x.divide(BigInteger.valueOf( + ((Integer)y).intValue())); + } + else if(y instanceof BigInteger) + return _x.divide((BigInteger)y); + } + + return new Double( + ((Number)x).doubleValue() + / ((Number)y).doubleValue()); + } //}}} + + //{{{ divide() method + public static Number divide(Number x, Number y) + { + if(x instanceof FactorRatio) + return ((FactorRatio)x).divide(y); + else if(y instanceof FactorRatio) + return ((FactorRatio)y)._divide(x); + else if( + (x instanceof Integer + || x instanceof BigInteger) + && + (y instanceof Integer + || y instanceof BigInteger)) + { + return FactorRatio.reduce(x,y); + } + + return new Double( + ((Number)x).doubleValue() + / ((Number)y).doubleValue()); + } //}}} + + //{{{ gcd() method + public static Number gcd(Number x, Number y) + { + if(x instanceof BigInteger) + { + BigInteger _x = (BigInteger)x; + if(y instanceof BigInteger) + return _x.gcd((BigInteger)y); + else + { + return _x.gcd(BigInteger.valueOf( + y.longValue())); + } + } + else + { + BigInteger _x = BigInteger.valueOf(x.longValue()); + if(y instanceof BigInteger) + return _x.gcd((BigInteger)y); + else + { + return _x.gcd(BigInteger.valueOf( + y.longValue())); + } + } + } //}}} + + //{{{ is1() method + public static boolean is1(Number x) + { + if(x instanceof BigInteger) + return x.equals(BigInteger.ONE); + else if(x instanceof Integer) + return x.intValue() == 1; + else + return x.floatValue() == 1.0f; + } //}}} + + //{{{ greater() method + public static boolean greater(float x, float y) + { + return x > y; + } //}}} + + //{{{ greaterEqual() method + public static boolean greaterEqual(float x, float y) + { + return x >= y; + } //}}} + + //{{{ less() method + public static boolean less(float x, float y) + { + return x < y; + } //}}} + + //{{{ lessEqual() method + public static boolean lessEqual(float x, float y) + { + return x <= y; + } //}}} + + //{{{ multiply() method + public static Number multiply(Number x, Number y) + { + if(x instanceof FactorRatio) + return ((FactorRatio)x).multiply(y); + else if(y instanceof FactorRatio) + return ((FactorRatio)y).multiply(x); + else if(x instanceof Integer) + { + int _x = ((Integer)x).intValue(); + + if(y instanceof Integer) + { + int _y = ((Integer)y).intValue(); + long result = (long)_x * (long)_y; + if(result > Integer.MAX_VALUE + || result < Integer.MIN_VALUE) + { + return BigInteger.valueOf(result); + } + else + { + return new Integer((int)result); + } + } + else if(y instanceof BigInteger) + { + return BigInteger.valueOf(_x) + .multiply((BigInteger)y); + } + } + else if(x instanceof BigInteger) + { + BigInteger _x = (BigInteger)x; + + if(y instanceof Integer) + { + return _x.multiply(BigInteger.valueOf( + ((Integer)y).intValue())); + } + else if(y instanceof BigInteger) + return _x.multiply((BigInteger)y); + } + + return new Double( + ((Number)x).doubleValue() + * ((Number)y).doubleValue()); + } //}}} + + //{{{ randomAngle() method + public static float randomAngle() + { + return (float)Math.PI * randomInt(0,360) / 180; + } //}}} + + //{{{ randomBoolean() method + public static boolean randomBoolean() + { + return random.nextBoolean(); + } //}}} + + //{{{ randomInt() method + public static int randomInt(int min, int max) + { + if(min == max) + return min; + + int nextInt = random.nextInt(); + return min + Math.abs(nextInt % (max - min + 1)); + } //}}} + + //{{{ sgn() method + public static int sgn(float num) + { + if(num < 0.0f) + return -1; + else if(num == 0.0f) + return 0; + else + return 1; + } //}}} + + //{{{ sgn() method + public static int sgn(Number num) + { + if(num instanceof FactorRatio) + return sgn(((FactorRatio)num).numerator); + else if(num instanceof BigInteger) + return ((BigInteger)num).signum(); + else + { + double value = num.doubleValue(); + if(value < 0.0) + return -1; + else if(value == 0.0) + return 0; + else + return 1; + } + } //}}} + + //{{{ subtract() method + public static Number subtract(Number x, Number y) + { + if(x instanceof FactorRatio) + return ((FactorRatio)x).subtract(y); + else if(y instanceof FactorRatio) + return ((FactorRatio)y)._subtract(x); + else if(x instanceof Integer) + { + int _x = ((Integer)x).intValue(); + + if(y instanceof Integer) + { + int _y = ((Integer)y).intValue(); + long result = (long)_x - (long)_y; + if(result > Integer.MAX_VALUE + || result < Integer.MIN_VALUE) + { + return BigInteger.valueOf(result); + } + else + { + return new Integer((int)result); + } + } + else if(y instanceof BigInteger) + { + return BigInteger.valueOf(_x) + .subtract((BigInteger)y); + } + } + else if(x instanceof BigInteger) + { + BigInteger _x = (BigInteger)x; + + if(y instanceof Integer) + { + return _x.subtract(BigInteger.valueOf( + ((Integer)y).intValue())); + } + else if(y instanceof BigInteger) + return _x.subtract((BigInteger)y); + } + + return new Double( + ((Number)x).doubleValue() + - ((Number)y).doubleValue()); + } //}}} +} diff --git a/factor/FactorMissingDefinition.java b/factor/FactorMissingDefinition.java new file mode 100644 index 0000000000..52c79b6837 --- /dev/null +++ b/factor/FactorMissingDefinition.java @@ -0,0 +1,52 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +/** + * A placeholder for an undefined word. + */ +public class FactorMissingDefinition extends FactorWordDefinition +{ + public static final FactorMissingDefinition INSTANCE + = new FactorMissingDefinition(); + + //{{{ eval() method + public void eval(FactorWord word, FactorInterpreter interp) + throws FactorUndefinedWordException + { + throw new FactorUndefinedWordException(word); + } //}}} + + //{{{ toString() method + public String toString() + { + return "undefined"; + } //}}} +} diff --git a/factor/FactorNamespace.java b/factor/FactorNamespace.java new file mode 100644 index 0000000000..4bc89a7518 --- /dev/null +++ b/factor/FactorNamespace.java @@ -0,0 +1,336 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.TreeMap; +import java.util.Iterator; +import java.util.Map; +import java.util.LinkedList; +import java.util.List; + +/** + * Manages the set of available words. + */ +public class FactorNamespace implements PublicCloneable +{ + private static FactorWord NULL = new FactorWord("(represent-f)"); + private static FactorWord CHECK_PARENT = new FactorWord("(check-parent)"); + + public Object obj; + private FactorNamespace parent; + private Map words; + + //{{{ FactorNamespace constructor + public FactorNamespace(FactorNamespace parent) throws Exception + { + this(parent,null,null); + } //}}} + + //{{{ FactorNamespace constructor + public FactorNamespace(FactorNamespace parent, Object obj) throws Exception + { + this(parent,null,obj); + } //}}} + + //{{{ FactorNamespace constructor + /** + * Cloning constructor. + */ + public FactorNamespace(FactorNamespace parent, Map words, Object obj) + throws Exception + { + this.parent = parent; + + this.words = new TreeMap(); + + // used by clone() + if(words != null) + { + Iterator iter = words.entrySet().iterator(); + while(iter.hasNext()) + { + Map.Entry entry = (Map.Entry)iter.next(); + Object key = entry.getKey(); + Object value = entry.getValue(); + if(!(value instanceof VarBinding)) + this.words.put(key,value); + } + } + + try + { + setVariable("namespace",this); + setVariable("parent",parent); + } + catch(Exception e) + { + e.printStackTrace(); + } + + if(obj != null) + { + this.obj = obj; + setVariable("this",obj); + } + } //}}} + + //{{{ getParent() method + public FactorNamespace getParent() + { + return parent; + } //}}} + + //{{{ importVars() method + /** + * Defines a variable bound to a Java field. + */ + public void importVars(String clazz, FactorList vars) + throws Exception + { + Class clas = Class.forName(clazz); + while(vars != null) + { + String field = (String)vars.car(String.class); + vars = vars.next(); + String word = (String)vars.car(String.class); + + setVariable(word,new VarBinding( + clas.getField(field), + null)); + + vars = vars.next(); + } + } //}}} + + //{{{ getVariable() method + public Object getVariable(String name) throws Exception + { + Object o = words.get(name); + if(o instanceof VarBinding) + return ((VarBinding)o).get(); + else if(o == NULL) + return null; + else if(o == CHECK_PARENT) + { + // we know this is not a field binding + return parent == null ? null + : parent.getVariable(name); + } + else if(o == null) + { + // lazily instantiate object field binding + lazyFieldInit(name); + return getVariable(name); + } + else + return o; + } //}}} + + //{{{ _setVariable() method + /** + * Alternative form for bindings. + */ + public void _setVariable(Object value, String name) + throws Exception + { + setVariable(name,value); + } //}}} + + //{{{ setVariable() method + public void setVariable(String name, Object value) + throws Exception + { + Object o = words.get(name); + if(o instanceof VarBinding && !(value instanceof VarBinding)) + ((VarBinding)o).set(value); + else if(o == null) + { + // lazily instantiate object field binding + lazyFieldInit(name); + setVariable(name,value); + return; + } + else if(value == null) + words.put(name,NULL); + else + words.put(name,value); + } //}}} + + //{{{ lazyFieldInit() method + private void lazyFieldInit(String name) + { + if(obj != null) + { + try + { + Field f = obj.getClass().getField(name); + if(!Modifier.isStatic(f.getModifiers())) + { + words.put(name,new VarBinding(f,obj)); + return; + } + } + catch(Exception e) + { + } + } + + // not a field, don't check again + words.put(name,CHECK_PARENT); + } //}}} + + //{{{ toVarList() method + /** + * Returns a list of variable and word names defined in this namespace. + */ + public FactorList toVarList() + { + FactorList first = null; + FactorList last = null; + Iterator iter = words.entrySet().iterator(); + while(iter.hasNext()) + { + Map.Entry entry = (Map.Entry)iter.next(); + if(entry.getValue() == CHECK_PARENT) + continue; + + String name = (String)entry.getKey(); + FactorList cons = new FactorList(name,null); + if(first == null) + first = last = cons; + else + { + last.cdr = cons; + last = cons; + } + } + + return first; + } //}}} + + //{{{ toValueList() method + /** + * Returns a list of pairs of variable and word names, and their values. + */ + public FactorList toValueList() + { + FactorList first = null; + FactorList last = null; + Iterator iter = words.entrySet().iterator(); + while(iter.hasNext()) + { + Map.Entry entry = (Map.Entry)iter.next(); + if(entry.getValue() == CHECK_PARENT) + continue; + + FactorList cons = new FactorList( + new FactorList(entry.getKey(), + entry.getValue()),null); + if(first == null) + first = last = cons; + else + { + last.cdr = cons; + last = cons; + } + } + + return first; + } //}}} + + //{{{ VarBinding class + /** + * This is messy. + */ + static class VarBinding + { + private Field field; + private Object instance; + + VarBinding(Field field, Object instance) + throws FactorRuntimeException + { + this.field = field; + this.instance = instance; + } + + Object get() throws Exception + { + return FactorJava.jvarGet(field,instance); + } + + void set(Object value) throws Exception + { + FactorJava.jvarSet(field,instance,value); + } + } //}}} + + //{{{ toString() method + public String toString() + { + return "Namespace[" + obj + "]"; + } //}}} + + //{{{ clone() method + public FactorNamespace clone(Object rebind) + { + if(rebind.getClass() != obj.getClass()) + throw new RuntimeException("Cannot rebind to different type"); + + try + { + return new FactorNamespace(parent,words,rebind); + } + catch(Exception e) + { + throw new InternalError(); + } + } //}}} + + //{{{ clone() method + public Object clone() + { + if(obj != null) + throw new RuntimeException("Cannot clone namespace that's bound to an object"); + + try + { + return new FactorNamespace(parent,new TreeMap(words),null); + } + catch(Exception e) + { + throw new InternalError(); + } + } //}}} +} diff --git a/factor/FactorObject.java b/factor/FactorObject.java new file mode 100644 index 0000000000..cde55123c1 --- /dev/null +++ b/factor/FactorObject.java @@ -0,0 +1,35 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +public interface FactorObject +{ + FactorNamespace getNamespace(FactorInterpreter interp) throws Exception; +} diff --git a/factor/FactorParseException.java b/factor/FactorParseException.java new file mode 100644 index 0000000000..4be4c53473 --- /dev/null +++ b/factor/FactorParseException.java @@ -0,0 +1,38 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +public class FactorParseException extends FactorException +{ + public FactorParseException(String filename, int lineno, String str) + { + super(filename + ":" + lineno + ":" + str); + } +} diff --git a/factor/FactorParser.java b/factor/FactorParser.java new file mode 100644 index 0000000000..eb40d57730 --- /dev/null +++ b/factor/FactorParser.java @@ -0,0 +1,540 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import java.io.*; +import java.util.HashMap; + +public class FactorParser +{ + private static final Object EOF = new Object(); + + private FactorWord DEF; + private FactorWord INE; + + private FactorWord SHU; + private FactorWord F; + private FactorWord FLE; + + private FactorWord DEFINE; + + private FactorWord BRA; + private FactorWord KET; + + private FactorWord COMMA; + + private String filename; + private Reader in; + private FactorDictionary dict; + private StreamTokenizer st; + + // sometimes one token is expanded into two words + private Object next; + + //{{{ FactorParser constructor + public FactorParser(String filename, Reader in, FactorDictionary dict) + { + this.filename = (filename == null ? "" : filename); + this.in = in; + this.dict = dict; + + DEF = dict.intern(":"); + INE = dict.intern(";"); + + SHU = dict.intern("~<<"); + F = dict.intern("--"); + FLE = dict.intern(">>~"); + + DEFINE = dict.intern("define"); + + BRA = dict.intern("["); + KET = dict.intern("]"); + + COMMA = dict.intern(","); + + st = new StreamTokenizer(in); + st.resetSyntax(); + st.whitespaceChars(0,' '); + /* all printable ASCII characters */ + st.wordChars('#','~'); + st.wordChars('0','9'); + st.commentChar('!'); + st.quoteChar('"'); + st.commentChar('('); + st.eolIsSignificant(false); + } //}}} + + //{{{ isParsingWord() method + private boolean isParsingWord(Object word) + { + return word == DEF + || word == INE + || word == SHU + || word == FLE + || word == BRA + || word == KET + || word == COMMA; + } //}}} + + //{{{ parse() method + /** + * Reads the file being parsed, and returns a list of all tokens that + * were read in. This list can be evaluated to run the file. + */ + public FactorList parse() throws IOException, FactorParseException + { + FactorList first = null; + FactorList last = null; + + try + { + for(;;) + { + Object next = next(); + if(next == EOF) + return first; + /* : foo bar baz ; is equivalent to + "foo" [ bar baz ] define */ + else if(next == DEF) + { + Object obj = next(); + if(!(obj instanceof FactorWord) + || isParsingWord(obj)) + { + error("Expected word name after " + next); + } + + FactorWordDefinition def = readDef(); + + FactorList l = new FactorList(DEFINE,null); + FactorList cons = new FactorList( + ((FactorWord)obj).name, + new FactorList(def,l)); + if(first == null) + first = cons; + else + last.cdr = cons; + last = l; + } + else if(next == SHU) + { + Object obj = next(); + if(!(obj instanceof FactorWord) + || isParsingWord(obj)) + { + error("Expected word name after " + next); + } + + FactorWordDefinition def = readShuffle(); + + FactorList l = new FactorList(DEFINE,null); + FactorList cons = new FactorList( + ((FactorWord)obj).name, + new FactorList(def,l)); + if(first == null) + first = cons; + else + last.cdr = cons; + last = l; + } + else if(next == BRA) + { + FactorList cons = new FactorList( + readList(),null); + if(first == null) + first = cons; + else + last.cdr = cons; + last = cons; + } + else if(isParsingWord(next)) + { + error("Unexpected " + next); + } + else + { + FactorList cons = new FactorList(next,null); + if(first == null) + first = cons; + else + last.cdr = cons; + last = cons; + } + } + } + finally + { + try + { + in.close(); + } + catch(IOException io) + { + } + } + } //}}} + + //{{{ next() method + private Object next() throws IOException, FactorParseException + { + if(next != null) + { + Object _next = next; + next = null; + return _next; + } + + int type = st.nextToken(); + switch(type) + { + case StreamTokenizer.TT_EOF: + return EOF; + case StreamTokenizer.TT_WORD: + boolean number = true; + boolean floating = false; + boolean exponent = false; + + for(int i = 0; i < st.sval.length(); i++) + { + char ch = st.sval.charAt(i); + if(ch == '-') + { + if((i != 0 && Character.toLowerCase( + st.sval.charAt(i - 1)) + != 'e') || st.sval.length() == 1) + { + number = false; + break; + } + } + else if((ch == 'e' || ch == 'E') + && st.sval.length() != 1) + { + if(exponent) + { + number = false; + break; + } + else + exponent = true; + } + else if(ch == '.' && st.sval.length() != 1) + { + if(floating) + { + number = false; + break; + } + else + floating = true; + } + else if(!Character.isDigit(ch)) + { + number = false; + break; + } + } + + if(number) + { + if(floating || exponent) + return new Float(st.sval); + else + return new Integer(st.sval); + } + + if(st.sval.length() == 1) + { + switch(st.sval.charAt(0)) + { + case 'f': + return null; + case 't': + return Boolean.TRUE; + } + } + else if(st.sval.startsWith("#\\")) + return toChar(st.sval.substring(2)); + else + { + // $foo is expanded into "foo" $ + if(st.sval.charAt(0) == '$') + { + next = dict.intern("$"); + return st.sval.substring(1); + } + // @foo is expanded into "foo" @ + else if(st.sval.charAt(0) == '@') + { + next = dict.intern("@"); + return st.sval.substring(1); + } + } + + // |foo is the same as "foo" + if(st.sval.charAt(0) == '|') + return st.sval.substring(1); + + return dict.intern(st.sval); + case '"': case '\'': + return st.sval; + default: + throw new FactorParseException(filename, + st.lineno(),"Unknown error: " + type); + } + } //}}} + + //{{{ toChar() method + private Character toChar(String spec) throws FactorParseException + { + if(spec.length() != 1) + error("Not a character literal: #\\" + spec); + return new Character(spec.charAt(0)); + } //}}} + + //{{{ readDef() method + /** + * Read list until ;. + */ + private FactorWordDefinition readDef() + throws IOException, FactorParseException + { + return new FactorCompoundDefinition(readList(INE,false)); + } //}}} + + //{{{ readShuffle() method + /** + * Shuffle notation looks like this: + * ~<< a b -- b a >>~ + * On the left is inputs, on the right is their arrangement on the + * stack. + */ + private FactorWordDefinition readShuffle() + throws IOException, FactorParseException + { + // 0 in consume map is last consumed, n is first consumed. + HashMap consumeMap = new HashMap(); + int consumeD = 0; + int consumeR = 0; + + for(;;) + { + Object next = next(); + if(next == EOF) + error("Unexpected EOF"); + if(next == F) + break; + else if(next instanceof FactorWord) + { + String name = ((FactorWord)next).name; + int counter; + if(name.startsWith("r:")) + { + next = dict.intern(name.substring(2)); + counter = (FactorShuffleDefinition + .FROM_R_MASK + | consumeR++); + } + else + counter = consumeD++; + + Object existing = consumeMap.put(next, + new Integer(counter)); + if(existing != null) + error("Appears twice in shuffle LHS: " + next); + } + else + { + error("Unexpected " + FactorJava.factorTypeToString( + next)); + } + } + + FactorList _shuffle = readList(FLE,false); + + int consume = consumeMap.size(); + + if(_shuffle == null) + { + return new FactorShuffleDefinition(consumeD,consumeR, + null,0,null,0); + } + + int[] shuffle = new int[_shuffle.length()]; + + int shuffleDlength = 0; + int shuffleRlength = 0; + + int i = 0; + while(_shuffle != null) + { + if(_shuffle.car instanceof FactorWord) + { + FactorWord word = ((FactorWord)_shuffle.car); + String name = word.name; + if(name.startsWith("r:")) + word = dict.intern(name.substring(2)); + + Integer _index = (Integer)consumeMap.get(word); + if(_index == null) + error("Does not appear in shuffle LHS: " + _shuffle.car); + int index = _index.intValue(); + + if(name.startsWith("r:")) + { + shuffleRlength++; + shuffle[i++] = (index + | FactorShuffleDefinition + .TO_R_MASK); + } + else + { + shuffleDlength++; + shuffle[i++] = index; + } + } + else + { + error("Unexpected " + FactorJava.factorTypeToString( + _shuffle.car)); + } + _shuffle = _shuffle.next(); + } + + int[] shuffleD = new int[shuffleDlength]; + int[] shuffleR = new int[shuffleRlength]; + int j = 0, k = 0; + for(i = 0; i < shuffle.length; i++) + { + int index = shuffle[i]; + if((index & FactorShuffleDefinition.TO_R_MASK) + == FactorShuffleDefinition.TO_R_MASK) + { + index = (index + & ~FactorShuffleDefinition.TO_R_MASK); + shuffleR[j++] = index; + } + else + shuffleD[k++] = index; + } + + return new FactorShuffleDefinition(consumeD,consumeR, + shuffleD,shuffleDlength,shuffleR,shuffleRlength); + } //}}} + + //{{{ readList() method + /** + * Read list until ]. + */ + private FactorList readList() + throws IOException, FactorParseException + { + return readList(KET,true); + } //}}} + + //{{{ readList() method + /** + * Read list until a given word. + */ + private FactorList readList(FactorWord until, boolean allowCommaPair) + throws IOException, FactorParseException + { + FactorList first = null; + FactorList last = null; + + for(;;) + { + Object next = next(); + if(next == until) + return first; + else if(next == EOF) + { + error("Unexpected EOF"); + } + // read a dotted pair + else if(allowCommaPair && next == COMMA) + { + if(first == null) + { + error("Expected at least 1 word before " + next); + } + + next = next(); + if(next == BRA) + { + last.cdr = readList(); + next = next(); + if(next == EOF) + error("Unexpected EOF"); + else if(next != KET) + error("Expected 1 word after ,"); + return first; + } + else if(next != EOF && !isParsingWord(next)) + { + last.cdr = next; + next = next(); + if(next == until) + return first; + } + + error("Expected 1 word after ,"); + } + else if(next == BRA) + { + FactorList list = readList(); + if(first == null) + first = last = new FactorList(list,null); + else + { + FactorList nextList = new FactorList(list,null); + last.cdr = nextList; + last = nextList; + } + } + else if(isParsingWord(next)) + error("Unexpected " + next); + else if(first == null) + first = last = new FactorList(next,null); + else + { + FactorList nextList = new FactorList(next,null); + last.cdr = nextList; + last = nextList; + } + } + } //}}} + + //{{{ error() method + private void error(String msg) throws FactorParseException + { + throw new FactorParseException(filename,st.lineno(),msg); + } //}}} +} diff --git a/factor/FactorPrimitive.java b/factor/FactorPrimitive.java new file mode 100644 index 0000000000..8e4635c9b2 --- /dev/null +++ b/factor/FactorPrimitive.java @@ -0,0 +1,374 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import java.lang.reflect.*; + +public abstract class FactorPrimitive extends FactorWordDefinition +{ + //{{{ P_callstackGet class + static class P_callstackGet extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + interp.datastack.push(interp.callstack.clone()); + } + } //}}} + + //{{{ P_callstackSet class + static class P_callstackSet extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + interp.callstack = (FactorCallStack)((FactorCallStack) + interp.datastack.pop(FactorCallStack.class)) + .clone(); + } + } //}}} + + //{{{ P_datastackGet class + static class P_datastackGet extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + interp.datastack.push(interp.datastack.clone()); + } + } //}}} + + //{{{ P_datastackSet class + static class P_datastackSet extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + interp.datastack = (FactorDataStack)((FactorDataStack) + interp.datastack.pop(FactorDataStack.class)) + .clone(); + } + } //}}} + + //{{{ P_clear class + static class P_clear extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + interp.datastack.top = 0; + } + } //}}} + + //{{{ P_restack class + static class P_restack extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + FactorList list = (FactorList)datastack.pop(FactorList.class); + interp.callstack.push(datastack); + interp.datastack = new FactorDataStack(list); + } + } //}}} + + //{{{ P_unstack class + static class P_unstack extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorList unstack = interp.datastack.toList(); + interp.datastack = (FactorDataStack)interp.callstack.pop(); + interp.datastack.push(unstack); + } + } //}}} + + //{{{ P_unwind class + static class P_unwind extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + interp.callstack.top = 0; + } + } //}}} + + //{{{ P_jconstructor class + static class P_jconstructor extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + datastack.push( + FactorJava.jconstructor( + (String)datastack.pop(String.class), + (FactorList)datastack.pop(FactorList.class))); + } + } //}}} + + //{{{ P_jfield class + static class P_jfield extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + datastack.push( + FactorJava.jfield( + (String)datastack.pop(String.class), + (String)datastack.pop(String.class))); + } + } //}}} + + //{{{ P_jinvoke class + static class P_jinvoke extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + FactorJava.jinvoke(datastack, + (Method)datastack.pop(), + datastack.pop()); + } + } //}}} + + //{{{ P_jinvokeStatic class + static class P_jinvokeStatic extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + FactorJava.jinvokeStatic(datastack, + (Method)datastack.pop()); + } + } //}}} + + //{{{ P_jmethod class + static class P_jmethod extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + datastack.push( + FactorJava.jmethod( + (String)datastack.pop(String.class), + (String)datastack.pop(String.class), + (FactorList)datastack.pop(FactorList.class))); + } + } //}}} + + //{{{ P_jnew class + static class P_jnew extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + FactorJava.jnew(datastack, + (Constructor)datastack.pop()); + } + } //}}} + + //{{{ P_jvarGet class + static class P_jvarGet extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + datastack.push( + FactorJava.jvarGet( + (Field)datastack.pop(Field.class), + datastack.pop())); + } + } //}}} + + //{{{ P_jvarGetStatic class + static class P_jvarGetStatic extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + datastack.push( + FactorJava.jvarGetStatic( + (Field)datastack.pop(Field.class))); + } + } //}}} + + //{{{ P_jvarSet class + static class P_jvarSet extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + FactorJava.jvarSet( + (Field)datastack.pop(Field.class), + datastack.pop(), + datastack.pop()); + } + } //}}} + + //{{{ P_jvarSetStatic class + static class P_jvarSetStatic extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + FactorJava.jvarSetStatic( + (Field)datastack.pop(Field.class), + datastack.pop()); + } + } //}}} + + //{{{ P_get class + static class P_get extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + datastack.push(interp.callframe.namespace.getVariable( + (String)datastack.pop(String.class))); + } + } //}}} + + //{{{ P_set class + static class P_set extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + interp.callframe.namespace.setVariable( + (String)datastack.pop(String.class), + datastack.pop()); + } + } //}}} + + //{{{ P_swap_set class + static class P_swap_set extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + interp.callframe.namespace._setVariable(datastack.pop(), + (String)datastack.pop(String.class)); + } + } //}}} + + //{{{ P_define class + static class P_define extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + FactorDictionary dict = interp.dict; + // handle old define syntax + Object obj = datastack.pop(); + if(obj instanceof FactorList) + obj = new FactorCompoundDefinition((FactorList)obj); + FactorWordDefinition def = (FactorWordDefinition)obj; + + FactorWord newWord = interp.dict.intern( + (String)datastack.pop(String.class)); + + def.precompile(newWord,interp); + try + { + if(interp.compile) + def = def.compile(newWord,interp); + } + catch(Throwable t) + { + System.err.println("WARNING: cannot compile " + newWord); + t.printStackTrace(); + } + + if(newWord.def != FactorMissingDefinition.INSTANCE) + { + System.err.println("WARNING: redefining " + newWord); + newWord.history = new FactorList(newWord.def, + newWord.history); + } + newWord.def = def; + } + } //}}} + + //{{{ P_call class + static class P_call extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + interp.call(word,(FactorList)interp.datastack.pop( + FactorList.class)); + } + } //}}} + + //{{{ P_bind class + static class P_bind extends FactorPrimitive + { + public void eval(FactorWord word, FactorInterpreter interp) + throws Exception + { + FactorDataStack datastack = interp.datastack; + FactorList code = (FactorList)datastack.pop(FactorList.class); + Object obj = datastack.pop(); + FactorNamespace ns; + if(obj instanceof FactorNamespace) + ns = (FactorNamespace)obj; + else if(obj instanceof FactorObject) + { + ns = ((FactorObject)obj).getNamespace(interp); + if(ns == null) + throw new FactorRuntimeException( + obj + " has a null" + + " namespace"); + } + else + { + throw new FactorDomainException(obj, + FactorObject.class); + } + interp.call(word,ns,code); + } + } //}}} +} diff --git a/factor/FactorRatio.java b/factor/FactorRatio.java new file mode 100644 index 0000000000..edfe6e0e0f --- /dev/null +++ b/factor/FactorRatio.java @@ -0,0 +1,286 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +public class FactorRatio extends Number implements FactorExternalizable +{ + public final Number numerator, denominator; + + //{{{ FactorRatio constructor + /** + * Creates a new ratio. + */ + public FactorRatio(Number numerator, Number denominator) + { + this.numerator = numerator; + this.denominator = denominator; + } //}}} + + //{{{ add() method + public Number add(Number num) + { + if(num instanceof FactorRatio) + { + // a c ad + bc + // - + - = ------- + // b d bd + FactorRatio r = (FactorRatio)num; + return reduce( + FactorMath.add( + FactorMath.multiply(numerator,r.denominator), + FactorMath.multiply(denominator,r.numerator)), + FactorMath.multiply(denominator,r.denominator)); + } + else if(num instanceof Float + || num instanceof Double) + { + return new Double(doubleValue() + num.doubleValue()); + } + else + { + return reduce( + FactorMath.add(numerator, + FactorMath.multiply(denominator,num)), + denominator); + } + } //}}} + + //{{{ subtract() method + /** + * this - num + */ + public Number subtract(Number num) + { + if(num instanceof FactorRatio) + { + // a c ad - bc + // - - - = ------- + // b d bd + FactorRatio r = (FactorRatio)num; + return reduce( + FactorMath.subtract( + FactorMath.multiply(numerator,r.denominator), + FactorMath.multiply(denominator,r.numerator)), + FactorMath.multiply(denominator,r.denominator)); + } + else if(num instanceof Float + || num instanceof Double) + { + return new Double(doubleValue() - num.doubleValue()); + } + else + { + return reduce( + FactorMath.subtract(numerator, + FactorMath.multiply(denominator,num)), + denominator); + } + } //}}} + + //{{{ _subtract() method + /** + * num - this + */ + public Number _subtract(Number num) + { + if(num instanceof FactorRatio) + { + // a c bc - ad + // - - - = ------- + // b d bd + FactorRatio r = (FactorRatio)num; + return reduce( + FactorMath.subtract( + FactorMath.multiply(denominator,r.numerator), + FactorMath.multiply(numerator,r.denominator)), + FactorMath.multiply(denominator,r.denominator)); + } + else if(num instanceof Float + || num instanceof Double) + { + return new Double(num.doubleValue() - doubleValue()); + } + else + { + return reduce( + FactorMath.subtract(FactorMath.multiply(denominator,num), + numerator), + denominator); + } + } //}}} + + //{{{ multiply() method + public Number multiply(Number num) + { + if(num instanceof FactorRatio) + { + // a c ac + // - * - = -- + // b d bd + FactorRatio r = (FactorRatio)num; + return reduce( + FactorMath.multiply(numerator,r.numerator), + FactorMath.multiply(denominator,r.denominator)); + } + else if(num instanceof Float + || num instanceof Double) + { + return new Double(doubleValue() * num.doubleValue()); + } + else + { + return reduce( + FactorMath.multiply(numerator,num), + denominator); + } + } //}}} + + //{{{ divide() method + /** + * this / num + */ + public Number divide(Number num) + { + if(num instanceof FactorRatio) + { + // a c ad + // - / - = -- + // b d bc + FactorRatio r = (FactorRatio)num; + return reduce( + FactorMath.multiply(numerator,r.denominator), + FactorMath.multiply(denominator,r.numerator)); + } + else if(num instanceof Float + || num instanceof Double) + { + return new Double(doubleValue() / num.doubleValue()); + } + else + { + return reduce(numerator, + FactorMath.multiply(denominator,num)); + } + } //}}} + + //{{{ _divide() method + /** + * num / this + */ + public Number _divide(Number num) + { + if(num instanceof FactorRatio) + { + // c a cb + // - / - = -- + // d b da + FactorRatio r = (FactorRatio)num; + return reduce( + FactorMath.multiply(denominator,r.numerator), + FactorMath.multiply(numerator,r.denominator)); + } + else if(num instanceof Float + || num instanceof Double) + { + return new Double(num.doubleValue() / doubleValue()); + } + else + { + return reduce( + FactorMath.multiply(denominator,num), + numerator); + } + } //}}} + + //{{{ reduce() method + public static Number reduce(Number numerator, Number denominator) + { + /* if(FactorMath.sgn(denominator) == 0) + signal(new DivisionByZero()); */ + /* if(FactorMath.sgn(denominator) == -1) + { + numerator = FactorMath.neg(numerator); + denominator = FactorMath.neg(denominator); + } */ + Number gcd = FactorMath.gcd(numerator,denominator); + if(!FactorMath.is1(gcd)) + { + numerator = FactorMath._divide(numerator,gcd); + denominator = FactorMath._divide(denominator,gcd); + } + + if(FactorMath.is1(denominator)) + return numerator; + else + return new FactorRatio(numerator,denominator); + } //}}} + + //{{{ intValue() method + public int intValue() + { + return (int)doubleValue(); + } //}}} + + //{{{ longValue() method + public long longValue() + { + return (long)doubleValue(); + } //}}} + + //{{{ floatValue() method + public float floatValue() + { + return (float)doubleValue(); + } //}}} + + //{{{ doubleValue() method + public double doubleValue() + { + return numerator.doubleValue() / denominator.doubleValue(); + } //}}} + + //{{{ byteValue() method + public byte byteValue() + { + return (byte)doubleValue(); + } //}}} + + //{{{ shortValue() method + public short shortValue() + { + return (short)doubleValue(); + } //}}} + + //{{{ toString() method + public String toString() + { + return numerator + "/" + denominator; + } //}}} +} diff --git a/factor/FactorReflectionForm.java b/factor/FactorReflectionForm.java new file mode 100644 index 0000000000..d270a0282c --- /dev/null +++ b/factor/FactorReflectionForm.java @@ -0,0 +1,394 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* +* $Id$ +* +* Copyright (C) 2003, 2004 Slava Pestov. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package factor; + +import java.lang.reflect.*; +import org.objectweb.asm.*; + +class FactorReflectionForm implements Constants +{ + private FactorList form; + + //{{{ FactorReflectionForm constructor + FactorReflectionForm(FactorList form) + { + this.form = form; + } //}}} + + //{{{ compile() method + boolean compile(FactorWord word, FactorInterpreter interp, + ClassWriter cw, CodeVisitor mw) + throws Exception + { + FactorDictionary dict = interp.dict; + if(form.car == dict.jvarGet) + { + return compileVarGet(word,interp,cw,mw, + form.next(),false); + } + else if(form.car == dict.jvarGetStatic) + { + return compileVarGet(word,interp,cw,mw, + form.next(),true); + } + else if(form.car == dict.jinvoke) + { + return compileInvoke(word,interp,cw,mw, + form.next(),false); + } + else if(form.car == dict.jinvokeStatic) + { + return compileInvoke(word,interp,cw,mw, + form.next(),true); + } + else if(form.car == dict.jnew) + { + return compileNew(word,interp,cw,mw, + form.next()); + } + else + throw new FactorRuntimeException("Cannot compile " + form.car); + } //}}} + + //{{{ compileVarGet() method + private boolean compileVarGet(FactorWord word, + FactorInterpreter interp, + ClassWriter cw, + CodeVisitor mw, + FactorList form, + boolean staticGet) throws Exception + { + FactorDictionary dict = interp.dict; + if(form.car != dict.jfield) + return false; + + form = form.next(); + String field = (String)form.car; + String clazz = (String)form.next().car; + + mw.visitVarInsn(ALOAD,2); + mw.visitFieldInsn(GETFIELD, + "factor/FactorInterpreter", "datastack", + "Lfactor/FactorDataStack;"); + + if(!staticGet) + { + mw.visitInsn(DUP); + mw.visitMethodInsn(INVOKEVIRTUAL, + "factor/FactorArrayStack", "pop", + "()Ljava/lang/Object;"); + } + + Class cls = FactorJava.getClass(clazz); + + generateFromConversion(mw,cls); + + Field fld = cls.getField(field); + + clazz = clazz.replace('.','/'); + + mw.visitFieldInsn(staticGet ? GETSTATIC : GETFIELD, + clazz, + field, + FactorJava.javaClassToVMClass(fld.getType())); + + generateToConversion(mw,fld.getType()); + + mw.visitMethodInsn(INVOKEVIRTUAL, + "factor/FactorArrayStack", "push", + "(Ljava/lang/Object;)V"); + + mw.visitInsn(RETURN); + + mw.visitMaxs(3,3); + + return true; + } //}}} + + //{{{ compileInvoke() method + private boolean compileInvoke(FactorWord word, + FactorInterpreter interp, + ClassWriter cw, + CodeVisitor mw, + FactorList form, + boolean staticInvoke) throws Exception + { + FactorDictionary dict = interp.dict; + if(form.car != dict.jmethod) + return false; + + form = form.next(); + String method = (String)form.car; + String clazz = (String)form.next().car; + Class[] args = FactorJava.classNameToClassList( + (FactorList)form.next().next().car); + + mw.visitVarInsn(ALOAD,2); + mw.visitFieldInsn(GETFIELD, + "factor/FactorInterpreter", "datastack", + "Lfactor/FactorDataStack;"); + + Class cls = FactorJava.getClass(clazz); + clazz = clazz.replace('.','/'); + + if(!staticInvoke) + { + mw.visitInsn(DUP); + mw.visitMethodInsn(INVOKEVIRTUAL, + "factor/FactorArrayStack", "pop", + "()Ljava/lang/Object;"); + generateFromConversion(mw,cls); + if(args.length != 0) + mw.visitInsn(SWAP); + } + + generateArgs(mw,args,!staticInvoke); + + Method mth = cls.getMethod(method,args); + + Class returnType = mth.getReturnType(); + int opcode; + if(staticInvoke) + opcode = INVOKESTATIC; + else if(cls.isInterface()) + opcode = INVOKEINTERFACE; + else + opcode = INVOKEVIRTUAL; + mw.visitMethodInsn(opcode, + clazz, + method, + FactorJava.javaSignatureToVMSignature( + args,returnType)); + + if(returnType != Void.TYPE) + { + generateToConversion(mw,returnType); + mw.visitMethodInsn(INVOKEVIRTUAL, + "factor/FactorArrayStack", "push", + "(Ljava/lang/Object;)V"); + } + else + mw.visitInsn(POP); + + mw.visitInsn(RETURN); + + mw.visitMaxs(4 + args.length,5); + + return true; + } //}}} + + //{{{ compileNew() method + private boolean compileNew(FactorWord word, + FactorInterpreter interp, + ClassWriter cw, + CodeVisitor mw, + FactorList form) throws Exception + { + FactorDictionary dict = interp.dict; + if(form.car != dict.jconstructor) + return false; + + form = form.next(); + String clazz = (String)form.car; + Class[] args = FactorJava.classNameToClassList( + (FactorList)form.next().car); + + clazz = clazz.replace('.','/'); + mw.visitTypeInsn(NEW,clazz); + mw.visitInsn(DUP); + + mw.visitVarInsn(ALOAD,2); + mw.visitFieldInsn(GETFIELD, + "factor/FactorInterpreter", "datastack", + "Lfactor/FactorDataStack;"); + + generateArgs(mw,args,true); + + mw.visitMethodInsn(INVOKESPECIAL, + clazz, + "", + FactorJava.javaSignatureToVMSignature( + args,void.class)); + + mw.visitInsn(SWAP); + mw.visitMethodInsn(INVOKEVIRTUAL, + "factor/FactorArrayStack", "push", + "(Ljava/lang/Object;)V"); + + mw.visitInsn(RETURN); + + mw.visitMaxs(5 + args.length,5); + + return true; + } //}}} + + //{{{ generateArgs() method + /** + * Generate instructions for copying arguments from the Factor + * datastack to the JVM stack. The types array is used to + * perform type conversions. + */ + private void generateArgs(CodeVisitor mw, Class[] args, + boolean generateSwap) throws Exception + { + if(args.length != 0) + { + // ensure the stack has enough elements + mw.visitInsn(DUP); + mw.visitIntInsn(BIPUSH,args.length); + mw.visitMethodInsn(INVOKEVIRTUAL, + "factor/FactorArrayStack", "ensurePop", + "(I)V"); + + // datastack.stack -> 3 + mw.visitInsn(DUP); + mw.visitFieldInsn(GETFIELD, + "factor/FactorArrayStack", "stack", + "[Ljava/lang/Object;"); + mw.visitVarInsn(ASTORE,3); + // datastack.top-args.length -> 4 + mw.visitInsn(DUP); + mw.visitFieldInsn(GETFIELD, + "factor/FactorArrayStack", "top", + "I"); + mw.visitIntInsn(BIPUSH,args.length); + mw.visitInsn(ISUB); + + // datastack.top -= args.length + mw.visitInsn(DUP2); + mw.visitFieldInsn(PUTFIELD, + "factor/FactorArrayStack", "top", + "I"); + + mw.visitVarInsn(ISTORE,4); + + if(generateSwap) + mw.visitInsn(SWAP); + + for(int i = 0; i < args.length; i++) + { + mw.visitVarInsn(ALOAD,3); + mw.visitVarInsn(ILOAD,4); + mw.visitInsn(AALOAD); + generateFromConversion(mw,args[i]); + if(i != args.length - 1) + mw.visitIincInsn(4,1); + } + } + } //}}} + + //{{{ generateFromConversion() method + /** + * Unbox value at top of the stack. + */ + private void generateFromConversion(CodeVisitor mw, Class type) + throws Exception + { + if(type == Object.class) + return; + + String methodName = null; + + if(type == Number.class) + methodName = "toNumber"; + else if(type == String.class) + methodName = "toString"; + else if(type == boolean.class) + methodName = "toBoolean"; + else if(type == char.class) + methodName = "toChar"; + else if(type == int.class) + methodName = "toInt"; + else if(type == long.class) + methodName = "toLong"; + else if(type == float.class) + methodName = "toFloat"; + else if(type == double.class) + methodName = "toDouble"; + else if(type == Class.class) + methodName = "toClass"; + else if(type.isArray()) + methodName = "toArray"; + + if(methodName == null) + { + mw.visitTypeInsn(CHECKCAST, + type.getName() + .replace('.','/')); + } + else + { + mw.visitMethodInsn(INVOKESTATIC, + "factor/FactorJava", + methodName, + "(Ljava/lang/Object;)" + + FactorJava.javaClassToVMClass(type)); + } + } //}}} + + //{{{ generateToConversion() method + /** + * Box return value, if needed. + */ + private void generateToConversion(CodeVisitor mw, Class type) + throws Exception + { + if(type == boolean.class) + { + // this case is handled specially + mw.visitMethodInsn(INVOKESTATIC, + "factor/FactorJava", + "fromBoolean", + "(Z)Ljava/lang/Object;"); + } + else + { + Class boxingType = FactorJava.javaBoxingType(type); + if(boxingType != null) + { + String boxingName = boxingType.getName() + .replace('.','/'); + mw.visitTypeInsn(NEW,boxingName); + mw.visitInsn(DUP_X1); + mw.visitInsn(SWAP); + mw.visitMethodInsn(INVOKESPECIAL,boxingName, + "", + "(" + FactorJava.javaClassToVMClass( + type) + ")V"); + } + } + } //}}} + + //{{{ toString() method + public String toString() + { + return form.toString(); + } //}}} +} diff --git a/factor/FactorRuntimeException.java b/factor/FactorRuntimeException.java new file mode 100644 index 0000000000..2c122a0888 --- /dev/null +++ b/factor/FactorRuntimeException.java @@ -0,0 +1,43 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +public class FactorRuntimeException extends FactorException +{ + public FactorRuntimeException(String str) + { + super(str); + } + + public FactorRuntimeException(String str, Throwable t) + { + super(str,t); + } +} diff --git a/factor/FactorShuffleDefinition.java b/factor/FactorShuffleDefinition.java new file mode 100644 index 0000000000..cfd03e33c9 --- /dev/null +++ b/factor/FactorShuffleDefinition.java @@ -0,0 +1,490 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003, 2004 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import org.objectweb.asm.*; + +/** + * ~<< name ... -- >>~ + */ +public class FactorShuffleDefinition extends FactorWordDefinition +{ + public static final int FROM_R_MASK = (1<<15); + public static final int TO_R_MASK = (1<<16); + public static final int SPECIFIER = FROM_R_MASK | TO_R_MASK; + + /** + * Elements to consume from stacks. + */ + private int consumeD; + private int consumeR; + + /** + * Permutation for elements on stack. + */ + private int[] shuffleD; + private int shuffleDstart; + private int shuffleDlength; + private int[] shuffleR; + private int shuffleRstart; + private int shuffleRlength; + + /** + * This is not thread-safe! + */ + private Object[] temporaryD; + private Object[] temporaryR; + + //{{{ FactorShuffleDefinition constructor + public FactorShuffleDefinition(int consumeD, int consumeR, + int[] shuffleD, int shuffleDlength, + int[] shuffleR, int shuffleRlength) + { + this.consumeD = consumeD; + this.consumeR = consumeR; + this.shuffleD = shuffleD; + this.shuffleDlength = shuffleDlength; + this.shuffleR = shuffleR; + this.shuffleRlength = shuffleRlength; + + if(this.shuffleD != null && this.shuffleDlength == 0) + this.shuffleD = null; + if(this.shuffleR != null && this.shuffleRlength == 0) + this.shuffleR = null; + if(this.shuffleD != null) + { + temporaryD = new Object[shuffleDlength]; + for(int i = 0; i < shuffleDlength; i++) + { + if(shuffleD[i] == i) + shuffleDstart++; + else + break; + } + } + if(this.shuffleR != null) + { + temporaryR = new Object[shuffleRlength]; + for(int i = 0; i < shuffleRlength; i++) + { + if(shuffleR[i] == (i | FROM_R_MASK)) + shuffleRstart++; + else + break; + } + } + } //}}} + + //{{{ canCompile() method + boolean canCompile() + { + return true; + } //}}} + + //{{{ compile() method + /** + * Write the definition of the eval() method in the compiled word. + * Local 0 -- this + * Local 1 -- word + * Local 2 -- interpreter + */ + boolean compile(FactorWord word, FactorInterpreter interp, + ClassWriter cw, CodeVisitor mw) + throws Exception + { + boolean fromD = false; + boolean fromR = false; + for(int i = 0; i < shuffleDlength; i++) + { + fromD = true; + if((shuffleD[i] & FROM_R_MASK) == FROM_R_MASK) + { + fromR = true; + break; + } + } + for(int i = 0; i < shuffleRlength; i++) + { + fromR = true; + if((shuffleR[i] & FROM_R_MASK) == FROM_R_MASK) + { + fromR = true; + break; + } + } + + // Local 3 -- datastack + // Local 4 -- datastack top-consumeD + // Local 5 -- datastack array + if(consumeD != 0 || fromD) + { + // (datastack datastack datastack) + mw.visitVarInsn(ALOAD,2); + mw.visitFieldInsn(GETFIELD, + "factor/FactorInterpreter", "datastack", + "Lfactor/FactorDataStack;"); + mw.visitInsn(DUP); + if(consumeD != 0) + { + mw.visitInsn(DUP); + mw.visitIntInsn(BIPUSH,consumeD); + mw.visitMethodInsn(INVOKEVIRTUAL, + "factor/FactorArrayStack", "ensurePop", + "(I)V"); + } + + mw.visitInsn(DUP); + // datastack -> 3 + mw.visitVarInsn(ASTORE,3); + // datastack.top-consumeD -> 4 + mw.visitFieldInsn(GETFIELD, + "factor/FactorArrayStack", "top", + "I"); + if(consumeD != 0) + { + mw.visitIntInsn(BIPUSH,consumeD); + mw.visitInsn(ISUB); + } + mw.visitVarInsn(ISTORE,4); + // datastack.stack -> 5 + mw.visitFieldInsn(GETFIELD, + "factor/FactorArrayStack", "stack", + "[Ljava/lang/Object;"); + mw.visitVarInsn(ASTORE,5); + } + + // Local 6 -- callstack + // Local 7 -- callstack top-consumeR + // Local 8 -- callstack array + if(consumeR != 0 || fromR) + { + // (callstack callstack) + mw.visitVarInsn(ALOAD,2); + mw.visitFieldInsn(GETFIELD, + "factor/FactorInterpreter", "callstack", + "Lfactor/FactorCallStack;"); + mw.visitInsn(DUP); + if(consumeR != 0) + { + mw.visitInsn(DUP); + mw.visitIntInsn(BIPUSH,consumeR); + mw.visitMethodInsn(INVOKEVIRTUAL, + "factor/FactorArrayStack", "ensurePop", + "(I)V"); + } + + mw.visitInsn(DUP); + // callstack -> 6 + mw.visitVarInsn(ASTORE,6); + // callstack.top-consumeR -> 7 + mw.visitFieldInsn(GETFIELD, + "factor/FactorArrayStack", "top", + "I"); + if(consumeR != 0) + { + mw.visitIntInsn(BIPUSH,consumeR); + mw.visitInsn(ISUB); + } + mw.visitVarInsn(ISTORE,7); + // callstack.stack -> 8 + mw.visitFieldInsn(GETFIELD, + "factor/FactorArrayStack", "stack", + "[Ljava/lang/Object;"); + mw.visitVarInsn(ASTORE,8); + } + + int locals = 9; + + if(shuffleD != null) + { + for(int i = shuffleDstart; i < shuffleDlength; i++) + { + // stack[top-consumeD+shuffleD[i]] -> 9+i + int index = shuffleD[i]; + if((index & FROM_R_MASK) == FROM_R_MASK) + { + mw.visitVarInsn(ALOAD,8); + mw.visitVarInsn(ILOAD,7); + index &= ~FROM_R_MASK; + } + else + { + mw.visitVarInsn(ALOAD,5); + mw.visitVarInsn(ILOAD,4); + } + + if(index != 0) + { + mw.visitIntInsn(BIPUSH,index); + mw.visitInsn(IADD); + } + + mw.visitInsn(AALOAD); + mw.visitVarInsn(ASTORE,9 + i); + } + + locals += shuffleDlength; + } + + if(shuffleR != null) + { + for(int i = shuffleRstart; i < shuffleRlength; i++) + { + // stack[top-consumeR+shuffleR[i]] -> 9+i + int index = shuffleR[i]; + if((index & FROM_R_MASK) == FROM_R_MASK) + { + mw.visitVarInsn(ALOAD,8); + mw.visitVarInsn(ILOAD,7); + index &= ~FROM_R_MASK; + } + else + { + mw.visitVarInsn(ALOAD,5); + mw.visitVarInsn(ILOAD,4); + } + + if(index != 0) + { + mw.visitIntInsn(BIPUSH,index); + mw.visitInsn(IADD); + } + + mw.visitInsn(AALOAD); + mw.visitVarInsn(ASTORE,locals + i); + } + } + + if(shuffleD != null) + { + // ensure that the stack array has enough space. + mw.visitVarInsn(ALOAD,3); + mw.visitInsn(DUP); + mw.visitIntInsn(BIPUSH,shuffleDlength); + mw.visitMethodInsn(INVOKEVIRTUAL, + "factor/FactorArrayStack", "ensurePush", "(I)V"); + // the datastack.stack array might have changed. + // reload it. + mw.visitFieldInsn(GETFIELD, + "factor/FactorArrayStack", "stack", + "[Ljava/lang/Object;"); + mw.visitVarInsn(ASTORE,5); + + for(int i = shuffleDstart; i < shuffleDlength; i++) + { + // stack[top - consumeD + i] <- 9+i + mw.visitVarInsn(ALOAD,5); + mw.visitVarInsn(ILOAD,4); + if(i != 0) + { + mw.visitIntInsn(BIPUSH,i); + mw.visitInsn(IADD); + } + mw.visitVarInsn(ALOAD,9 + i); + mw.visitInsn(AASTORE); + } + + // increment the 'top' field. + mw.visitVarInsn(ALOAD,3); + mw.visitVarInsn(ILOAD,4); + mw.visitIntInsn(BIPUSH,shuffleDlength); + mw.visitInsn(IADD); + mw.visitFieldInsn(PUTFIELD, + "factor/FactorArrayStack", "top", + "I"); + } + else if(consumeD != 0) + { + mw.visitVarInsn(ALOAD,3); + mw.visitVarInsn(ILOAD,4); + mw.visitFieldInsn(PUTFIELD, + "factor/FactorArrayStack", "top", + "I"); + } + + if(shuffleR != null) + { + // ensure that the stack array has enough space. + mw.visitVarInsn(ALOAD,6); + mw.visitInsn(DUP); + mw.visitIntInsn(BIPUSH,shuffleDlength); + mw.visitMethodInsn(INVOKEVIRTUAL, + "factor/FactorArrayStack", "ensurePush", "(I)V"); + // the callstack.stack array might have changed. + // reload it. + mw.visitFieldInsn(GETFIELD, + "factor/FactorArrayStack", "stack", + "[Ljava/lang/Object;"); + mw.visitVarInsn(ASTORE,8); + + for(int i = shuffleRstart; i < shuffleRlength; i++) + { + // stack[top - consumeD + i] <- locals+i + mw.visitVarInsn(ALOAD,8); + mw.visitVarInsn(ILOAD,7); + if(i != 0) + { + mw.visitIntInsn(BIPUSH,i); + mw.visitInsn(IADD); + } + mw.visitVarInsn(ALOAD,locals + i); + mw.visitInsn(AASTORE); + } + + // increment the 'top' field. + mw.visitVarInsn(ALOAD,6); + mw.visitVarInsn(ILOAD,7); + mw.visitIntInsn(BIPUSH,shuffleRlength); + mw.visitInsn(IADD); + mw.visitFieldInsn(PUTFIELD, + "factor/FactorArrayStack", "top", + "I"); + } + else if(consumeR != 0) + { + mw.visitVarInsn(ALOAD,6); + mw.visitVarInsn(ILOAD,7); + mw.visitFieldInsn(PUTFIELD, + "factor/FactorArrayStack", "top", + "I"); + } + + mw.visitInsn(RETURN); + + // Max stack and locals + mw.visitMaxs(4,9 + shuffleDlength + shuffleRlength); + + return true; + } //}}} + + //{{{ eval() method + public void eval(FactorWord word, FactorInterpreter interp) + throws FactorStackException + { + FactorArrayStack datastack = interp.datastack; + FactorArrayStack callstack = interp.callstack; + + if(datastack.top < consumeD) + throw new FactorStackException(consumeD); + + if(callstack.top < consumeR) + throw new FactorStackException(consumeR); + + if(shuffleD != null) + shuffle(interp,datastack,consumeD,consumeR,shuffleD,temporaryD); + + if(shuffleR != null) + shuffle(interp,callstack,consumeD,consumeR,shuffleR,temporaryR); + + datastack.top -= consumeD; + if(temporaryD != null) + datastack.pushAll(temporaryD); + + callstack.top -= consumeR; + if(temporaryR != null) + callstack.pushAll(temporaryR); + + } //}}} + + //{{{ shuffle() method + private void shuffle(FactorInterpreter interp, FactorArrayStack stack, + int consumeD, int consumeR, int[] shuffle, Object[] temporary) + throws FactorStackException + { + for(int i = 0; i < temporary.length; i++) + { + Object[] array; + int top; + int index = shuffle[i]; + int consume; + if((index & FROM_R_MASK) == FROM_R_MASK) + { + array = interp.callstack.stack; + top = interp.callstack.top; + index = (index & ~FROM_R_MASK); + consume = consumeR; + } + else + { + array = interp.datastack.stack; + top = interp.datastack.top; + consume = consumeD; + } + temporary[i] = array[top - consume + index]; + } + } //}}} + + //{{{ toString() method + public String toString() + { + StringBuffer buf = new StringBuffer(); + + for(int i = 0; i < consumeD; i++) + { + buf.append((char)('A' + i)); + buf.append(' '); + } + + for(int i = 0; i < consumeR; i++) + { + buf.append("r:"); + buf.append((char)('A' + i)); + buf.append(' '); + } + + buf.append("--"); + + if(shuffleD != null) + { + for(int i = 0; i < shuffleDlength; i++) + { + int index = shuffleD[i]; + if((index & FROM_R_MASK) == FROM_R_MASK) + index &= ~FROM_R_MASK; + buf.append(' '); + buf.append((char)('A' + index)); + } + } + + if(shuffleR != null) + { + for(int i = 0; i < shuffleRlength; i++) + { + int index = shuffleR[i]; + if((index & FROM_R_MASK) == FROM_R_MASK) + index &= ~FROM_R_MASK; + buf.append(" r:"); + buf.append((char)('A' + index)); + } + } + + return buf.toString(); + } //}}} +} diff --git a/factor/FactorStackException.java b/factor/FactorStackException.java new file mode 100644 index 0000000000..6990166ec5 --- /dev/null +++ b/factor/FactorStackException.java @@ -0,0 +1,38 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +public class FactorStackException extends FactorRuntimeException +{ + public FactorStackException(int expected) + { + super("Expected " + expected); + } +} diff --git a/factor/FactorUndefinedWordException.java b/factor/FactorUndefinedWordException.java new file mode 100644 index 0000000000..c25f529e7f --- /dev/null +++ b/factor/FactorUndefinedWordException.java @@ -0,0 +1,38 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +public class FactorUndefinedWordException extends FactorRuntimeException +{ + public FactorUndefinedWordException(FactorWord word) + { + super(word + " not defined"); + } +} diff --git a/factor/FactorWord.java b/factor/FactorWord.java new file mode 100644 index 0000000000..1929f109f9 --- /dev/null +++ b/factor/FactorWord.java @@ -0,0 +1,69 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import java.util.HashMap; +import java.util.Map; + +/** + * An internalized symbol. + */ +public class FactorWord implements FactorExternalizable +{ + public final String name; + + /** + * Always non-null. + */ + public FactorWordDefinition def; + + /** + * "define" pushes previous definitions onto this list, like a stack. + */ + public FactorList history; + + //{{{ FactorWord constructor + /** + * Do not use this constructor unless you're writing a packages + * implementation or something. Use an FactorDictionary's + * intern() method instead. + */ + public FactorWord(String name) + { + this.name = name; + def = FactorMissingDefinition.INSTANCE; + } //}}} + + //{{{ toString() method + public String toString() + { + return name; + } //}}} +} diff --git a/factor/FactorWordDefinition.java b/factor/FactorWordDefinition.java new file mode 100644 index 0000000000..7dec13c5b5 --- /dev/null +++ b/factor/FactorWordDefinition.java @@ -0,0 +1,191 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +import java.io.FileOutputStream; +import java.util.Iterator; +import org.objectweb.asm.*; +import org.objectweb.asm.util.*; + +/** + * A word definition. + */ +public abstract class FactorWordDefinition implements FactorObject, Constants +{ + private FactorNamespace namespace; + + /** + * Number of times this word has been referenced from a + * compound word (incremented by the precompiler). + */ + public int references; + + public abstract void eval(FactorWord word, FactorInterpreter interp) + throws Exception; + + void precompile(FactorWord word, FactorInterpreter interp) + throws Exception {} + + public FactorNamespace getNamespace(FactorInterpreter interp) throws Exception + { + if(namespace == null) + namespace = new FactorNamespace(interp.global,this); + + return namespace; + } + + //{{{ canCompile() method + boolean canCompile() + { + return false; + } //}}} + + private static int compileCount; + + //{{{ compile() method + /** + * Compile the given word, returning a new word definition. + */ + FactorWordDefinition compile(FactorWord word, FactorInterpreter interp) + throws Exception + { + if(!canCompile()) + return this; + + //System.out.println("Compiling " + word); + + StringBuffer sanitizedName = new StringBuffer(); + for(int i = 0; i < word.name.length(); i++) + { + char ch = word.name.charAt(i); + if(!Character.isJavaIdentifierStart(ch)) + sanitizedName.append("_"); + else + sanitizedName.append(ch); + } + String className = "factor/__compiler__/" + sanitizedName + + "_" + (compileCount++); + + ClassWriter cw = new ClassWriter(false); + cw.visit(ACC_PUBLIC, className, + "factor/FactorWordDefinition", null, null); + + // creates a MethodWriter for the (implicit) constructor + CodeVisitor mw = cw.visitMethod(ACC_PUBLIC, + "", "()V", null, null); + // pushes the 'this' variable + mw.visitVarInsn(ALOAD, 0); + // invokes the super class constructor + mw.visitMethodInsn(INVOKESPECIAL, + "factor/FactorWordDefinition", "", "()V"); + mw.visitInsn(RETURN); + // this code uses a maximum of one stack element and one local + // variable + mw.visitMaxs(1, 1); + + // creates a MethodWriter for the 'toString' method + mw = cw.visitMethod(ACC_PUBLIC, + "toString", "()Ljava/lang/String;", null, null); + mw.visitLdcInsn("( compiled ) " + toString()); + mw.visitInsn(ARETURN); + mw.visitMaxs(1, 1); + + // pushes the 'this' variable + mw.visitVarInsn(ALOAD, 0); + // invokes the super class constructor + mw.visitMethodInsn(INVOKESPECIAL, + "factor/FactorWordDefinition", "", "()V"); + mw.visitInsn(RETURN); + // this code uses a maximum of one stack element and one local + // variable + mw.visitMaxs(1, 1); + + // creates a MethodWriter for the 'eval' method + mw = cw.visitMethod(ACC_PUBLIC, + "eval", "(Lfactor/FactorWord;Lfactor/FactorInterpreter;)V", + null, null); + + // We store a string with disassembly for debugging + // purposes. + TraceCodeVisitor disasm = new TraceCodeVisitor(mw); + if(!compile(word,interp,cw,disasm)) + return this; + + // Save the disassembly + StringBuffer buf = new StringBuffer(); + Iterator bytecodes = disasm.getText().iterator(); + while(bytecodes.hasNext()) + { + buf.append(bytecodes.next()); + } + + // gets the bytecode of the class, and loads it dynamically + byte[] code = cw.toByteArray(); + + /* FileOutputStream fos = new FileOutputStream(className + ".class"); + fos.write(code); + fos.close(); */ + + SimpleClassLoader loader = new SimpleClassLoader(); + Class compiledWordClass = loader._defineClass(className, + code, 0, code.length); + + FactorWordDefinition compiledWord = (FactorWordDefinition) + compiledWordClass.newInstance(); + + compiledWord.getNamespace(interp).setVariable("asm",buf.toString()); + + return compiledWord; + } //}}} + + //{{{ compile() method + /** + * Write the definition of the eval() method in the compiled word. + * Local 0 -- this + * Local 1 -- word + * Local 2 -- interpreter + */ + boolean compile(FactorWord word, FactorInterpreter interp, + ClassWriter cw, CodeVisitor mw) + throws Exception + { + throw new FactorRuntimeException("Don't know how to compile " + word); + } //}}} + + //{{{ SimpleClassLoader class + static class SimpleClassLoader extends ClassLoader + { + public Class _defineClass(String name, + byte[] code, int off, int len) + { + return defineClass(name,code,off,len); + } + } //}}} +} diff --git a/factor/PublicCloneable.java b/factor/PublicCloneable.java new file mode 100644 index 0000000000..70e47031e0 --- /dev/null +++ b/factor/PublicCloneable.java @@ -0,0 +1,35 @@ +/* :folding=explicit:collapseFolds=1: */ + +/* + * $Id$ + * + * Copyright (C) 2003 Slava Pestov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package factor; + +public interface PublicCloneable extends Cloneable +{ + Object clone(); +} diff --git a/factor/boot.factor b/factor/boot.factor new file mode 100644 index 0000000000..947c0be553 --- /dev/null +++ b/factor/boot.factor @@ -0,0 +1,102 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +!!! The stack operators are defined using shuffle notation. This saves several +!!! hundred lines of code! + +~<< drop A -- >>~ +~<< 2drop A B -- >>~ +~<< dup A -- A A >>~ +~<< 2dup A B -- A B A B >>~ +~<< dupd A B -- A A B >>~ +~<< 2dupd A B C D -- A B A B C D >>~ +~<< nip A B -- B >>~ +~<< 2nip A B C D -- C D >>~ +~<< nop -- >>~ ! Does nothing! +~<< over A B -- A B A >>~ +~<< 2over A B C D -- A B C D A B >>~ +~<< pick A B C -- A B C A >>~ ! Not the Forth pick! +~<< rot A B C -- B C A >>~ +~<< 2rot A B C D E F -- C D E F A B >>~ +~<< -rot A B C -- C A B >>~ +~<< 2-rot A B C D E F -- E F A B C D >>~ +~<< swap A B -- B A >>~ +~<< 2swap A B C D -- C D A B >>~ +~<< swapd A B C -- B A C >>~ +~<< 2swapd A B C D E F -- C D A B E F >>~ +~<< tuck A B -- B A B >>~ +~<< 2tuck A B C D -- C D A B C D >>~ + +~<< rdrop r:A -- >>~ +~<< >r A -- r:A >>~ +~<< 2>r A B -- r:A r:B >>~ +~<< r> r:A -- A >>~ +~<< 2r> r:A r:B -- A B >>~ + +!!! Minimum amount of I/O words needed to be able to read other resources. +!!! Remaining I/O operations are defined in io.factor and parser.factor. +: (reader -- breader) + [ |java.io.Reader ] |java.io.BufferedReader jconstructor jnew ; + +: (inputstream -- breader) + [ |java.io.InputStream ] |java.io.InputStreamReader jconstructor jnew ; + +: (path -- inputstream) + |factor.FactorInterpreter + [ |java.lang.String ] |java.lang.Class |getResourceAsStream jmethod jinvoke + ; + +: parse* (filename reader -- list) + $dict + [ |java.lang.String |java.io.Reader |factor.FactorDictionary ] + |factor.FactorParser jconstructor jnew + [ ] |factor.FactorParser |parse jmethod jinvoke ; + +: runResource (path --) + dup parse* call ; +!!! + +!!! Load the standard library. +"/version.factor" runResource + +"/factor/combinators.factor" runResource +"/factor/continuations.factor" runResource +"/factor/dictionary.factor" runResource +"/factor/examples.factor" runResource +"/factor/interpreter.factor" runResource +"/factor/lists.factor" runResource +"/factor/math.factor" runResource +"/factor/miscellaneous.factor" runResource +"/factor/namespaces.factor" runResource +"/factor/network.factor" runResource +"/factor/parser.factor" runResource +"/factor/random.factor" runResource +"/factor/stream.factor" runResource +"/factor/strings.factor" runResource + +! If we're run stand-alone, start the interpreter in the current tty. +$interactive [ initialInterpreterLoop ] when diff --git a/factor/combinators.factor b/factor/combinators.factor new file mode 100644 index 0000000000..b5f88db181 --- /dev/null +++ b/factor/combinators.factor @@ -0,0 +1,228 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +: apply2 (x y [ code ] --) + ! First applies the code to x, then to y. + 2dup 2>r + nip call + 2r> + call ; + +~<< binrecP + ! Put P on top of the data stack, save everything on callstack. + P T R1 R2 -- P r:P r:T r:R1 r:R2 >>~ + +~<< binrecT + ! Put T on top of the data stack, discard all saved objects from + ! callstack. + r:P r:T r:R1 r:R2 -- T >>~ + +~<< binrecR1 + ! Put R1 on top of the data stack, retaining all saved objects on the + ! callstack. + r:P r:T r:R1 r:R2 -- R1 r:P r:T r:R1 r:R2 >>~ + +~<< binrecLeft + ! Left recursion setup; put second value on callstack, put P, T, R1, R2 + ! on data stack (and leave them on the callstack too). + Value2 r:P r:T r:R1 r:R2 -- P T R1 R2 r:Value2 r:P r:T r:R1 r:R2 >>~ + +~<< binrecRight + ! Right recursion setup; put second value back on datastack, put + ! P, T, R1, R2 on data stack. All quotations except for R2 are + ! discarded from the callstack, since they're not needed anymore. + r:Value2 r:P r:T r:R1 r:R2 -- Value2 P T R1 R2 r:R2 >>~ + +: binrec ( [ P ] [ T ] [ R1 ] [ R2 ] -- ... ) + ! Evaluate P. If it evaluates to t, evaluate T. Otherwise, evaluate R1, + ! which is expected to produce two values, recurse on each value, and + ! evaluate R2. + binrecP call [ + binrecT call + ] [ + binrecR1 call + ! R1 has now produced two values on top of the data stack. + ! Recurse twice. + binrecLeft binrec + binrecRight binrec + ! Now call R2. + r> call + ] ifte ; + +: compare (x y [if x < y] [if x = y] [if x > y] --) + >=< call ; + +: cleave (x [ code1 ] [ code2 ] --) + ! Executes each quotation, with x on top of the stack. + >r + over >r + call + r> + r> + call ; + +: cond (list --) + ! The list is of this form: + ! [ [ condition 1 ] [ code 1 ] + ! [ condition 2 ] [ code 2 ] + ! ... ] + ! Each condition is evaluated in turn. If it returns true, the code + ! is evaluated. If it returns false, the next condition is checked. + [ + uncons >r + call + r> + swap [ + car call + ] [ + cdr cond + ] ifte + ] when* ; + +: dip (a [ b ] -- b a) + ! Calls b as if b was not even present on the stack -- b has no way of + ! knowing that a even exists. + swap + >r + call + r> ; + +: 2dip (a b [ c ] -- c a b) + ! Calls c as if a and b were not even present on the stack -- c has no way + ! of knowing that a and b even exist. + -rot + 2>r + call + 2r> ; + +: each ([ list ] [ code ] --) + ! Applies the code to each element of the list. + over [ + >r + uncons + r> + tuck + 2>r + call + 2r> + each + ] [ + 2drop + ] ifte ; + +: expand (list -- list) + ! Evaluates the list on a new stack, and pushes the reversed stack onto the + ! original stack. For example, "[ 0 1 2 dup * + ] expand" will evaluate to + ! [ 0 5 ]. + unit + restack + call + unstack ; + +: interleave ( X list -- ... ) + ! Evaluate each element of the list with X on top of the + ! stack. + dup [ + over [ unswons dip ] dip swap interleave + ] [ + 2drop + ] ifte ; + +: ifte (cond [if true] [if false] --) + ? call ; + +: linrec ( [ P ] [ T ] [ R1 ] [ R2 ] -- ) + ! Evaluate P, if it pushes t, evaluate T. Otherwise, evaluate R1, recurse, + ! and evaluate R2. This combinator is similar to the linrec combinator in + ! Joy, except in Joy, P does not affect the stack. + >r >r >r dup >r call [ + r> drop r> call + r> drop r> drop + ] [ + r> r> r> dup >r swap >r swap >r call + r> r> r> r> dup >r linrec + r> call + ] ifte ; + +: map ([ items ] [ initial ] [ code ] -- [ mapping ]) + ! Applies the code to each item, returns a list that begins with the initial + ! list and contains the result of each application. + swapd 2list append + restack + each + unstack ; + +: push ([ a b c ... ] -- a b c ...) + ! Pushes values onto the stack literally (even if they are words). + [ uncons push ] when* ; + +: subset (list code -- list) + ! Applies code to each element of the given list, creating a new list + ! containing the elements where the code returned a non-null value. + [ dupd call [ drop ] unless ] cons 2list + restack + each + unstack ; + +: times (n [ code ] --) + ! Evaluates code n times. + [ + over 0 > + ] [ + tuck >r pred >r call r> r> + ] while 2drop ; + +: times* (n [ code ] --) + ! Evaluates code n times, each time the index is pushed onto the stack. + ! The index ranges from 0 to n-1. + 0 rot + [ + 2dup < + ] [ + >r 2dup succ >r >r swap call r> r> r> + ] while + drop drop drop ; + +: unless (cond [if false] --) + f swap ? call ; + +: when (cond [if true] --) + f ? call ; + +: when* (cond [ code ] --) + ! If the condition is true, it is left on the stack, and the code is + ! evaluated. Otherwise, the condition is popped off the stack. + dupd [ drop ] ifte ; + +: while ( [ P ] [ R ] -- ... ) + ! Evaluates P. If it leaves t on the stack, evaluate R, and recurse. + >r dup >r call [ + r> r> dup >r swap >r call + r> r> while + ] [ + r> drop r> drop + ] ifte ; diff --git a/factor/continuations.factor b/factor/continuations.factor new file mode 100644 index 0000000000..af08fc053f --- /dev/null +++ b/factor/continuations.factor @@ -0,0 +1,73 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +: continue (datastack callstack push --) + ! Do not call this directly. Used by callcc. + ! Use a trick to carry the push parameter onto the new data stack. + 2dip + callstack@ (push` datastack) + swap (datastack push`) + >r (datastack) + datastack@ (... [ code ]) + drop (...) + r> (... push`) + call ; + +: callcc ([ code ] --) + ! Calls the code with a special object on the top of the stack. This object, + ! when called, restores execution state to just after the callcc call that + ! generated this object, and pushes each element of the list at the top of + ! the caller's data stack onto the original data stack. + + ! We do a cdr since we don't want the [ code ] to be at the top of the + ! stack when execution is restored. Also note that $callstack's car is the + ! parent callframe, not the current callframe -- the current callframe is in + ! $callframe. + datastack$ callstack$ [ [ ] continue ] cons cons + swap call ; + +: callcc0 ([ code ] --) + ! Like callcc except no data is pushed onto the original datastack. + datastack$ callstack$ [ [ f ] continue ] cons cons + swap call ; + +: callcc1 ([ code ] --) + ! Like callcc except the continuation that is pushed onto the stack before + ! executing the given code takes the top of the caller's data stack and + ! pushes it onto the original datastack, instead of prepending it to the + ! original datastack as a list. + datastack$ callstack$ [ [ unit ] continue ] cons cons + swap call ; + +: suspend (--) + ! Suspend the current fiber. + ! Not really implemented yet. + $initialInterpreterContinuation dup [ + call + ] [ + clear unwind + ] ifte ; diff --git a/factor/dictionary.factor b/factor/dictionary.factor new file mode 100644 index 0000000000..35ed791e29 --- /dev/null +++ b/factor/dictionary.factor @@ -0,0 +1,67 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +! Undefined words's def field is equal to this. +"factor.FactorMissingDefinition" "INSTANCE" jfield jvarStatic$ @undefinedWord + +: asm. (word -- assembly) + ! Prints JVM bytecode disassembly of the given word. + worddef [ $asm ] bind dup [ + print + ] [ + drop "Not a compiled word." print + ] ifte ; + +: word? (obj -- boolean) + "factor.FactorWord" is ; + +: str>word ("word" -- word) + ! Returns the top of the stack if it already been interned. + dup word? [ + $dict [ "java.lang.String" ] "factor.FactorDictionary" "intern" + jmethod jinvoke + ] unless ; + +: worddef? (obj -- boolean) + "factor.FactorWordDefinition" is ; + +: compound? (obj -- boolean) + "factor.FactorCompoundDefinition" is ; + +: shuffle? (obj -- boolean) + "factor.FactorShuffleDefinition" is ; + +: worddef (word -- worddef) + str>word + ! Get the 'def' field + "factor.FactorWord" "def" jfield jvar$ + ! Is it equal to the missing word placeholder? Then push f. + dup $undefinedWord = [ drop f ] when ; + +: words (-- list) + ! Pushes a list of all defined words. + $dict [ ] "factor.FactorDictionary" "toWordList" jmethod jinvoke ; diff --git a/factor/examples.factor b/factor/examples.factor new file mode 100644 index 0000000000..50b3d97e0a --- /dev/null +++ b/factor/examples.factor @@ -0,0 +1,38 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +: examples/httpd + "/factor/examples/httpd.factor" runResource + "Enter a port number: " write + read >fixnum + "Enter document root (eg, /home/www/): " write + read + httpd ; + +: examples/httpd* + "/factor/examples/httpd.factor" runResource + 8888 "/home/slava/ExampleHTTPD/" httpd ; diff --git a/factor/examples/httpd.factor b/factor/examples/httpd.factor new file mode 100644 index 0000000000..60fca65b53 --- /dev/null +++ b/factor/examples/httpd.factor @@ -0,0 +1,151 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +! To make this a bit more useful: +! - URL encoding +! - log with date +! - log user agent +! - add a socket timeout +! - if a directory is requested and URL does not end with /, redirect +! - return more header fields, like Content-Length, Last-Modified, and so on +! - HEAD request +! - make httpdFiletype generic, specify file types in a list of comma pairs +! - basic authentication, using httpdAuth function from a config file +! - when string formatting is added, some code can be simplified +! - use nio to handle multiple requests +! - implement an LSP that does an "apropos" search + +: httpdGetPath ( request -- file ) + dup ".*\\.\\.*" matches [ + f + ] [ + dup [ "GET (.*?)( HTTP.*|)" groups dup [ car ] when ] when + ] ifte ; + +: httpdResponse (stream msg contentType --) + [ "HTTP/1.0 " over fwrite ] 2dip + [ over fwriteln "Content-type: " over fwriteln ] dip + swap fwriteln ; + +: httpdError (stream error --) + "Error: " write dup print + 2dup "text/html" httpdResponse + "\n

" swap "

" cat3 swap fwriteln ; + +: httpdFiletype (filename -- mime-type) + [ + [ dup ".*\.gif" matches ] [ drop "image/gif" ] + [ dup ".*\.png" matches ] [ drop "image/png" ] + [ dup ".*\.html" matches ] [ drop "text/html" ] + [ dup ".*\.txt" matches ] [ drop "text/plain" ] + [ dup ".*\.lsd" matches ] [ drop "text/plain" ] + [ t ] [ drop "application/octet-stream" ] + ] cond ; + +: httpdUriToPath (uri -- path) + $httpdDocRoot swap + dup "http://.*?(/.*)" groups [ car ] when* + cat2 ; + +: httpdPathToAbsolute (path -- absolute) + $httpdDocRoot swap cat2 + "Serving " over cat2 print + dup directory? [ "/index.html" cat2 ] when ; + +: httpdServeFile (stream argument filename --) + nip + 2dup "200 Document follows" swap httpdFiletype httpdResponse + [ "" over fwriteln ] dip + swap fcopy ; + +: httpdListDirectory (stream directory -- string) + [ "" over fwrite ] dip + 2dup swap fwrite + [ "

" over fwrite ] dip + 2dup swap fwrite + [ "

" swap fwrite ; + +: httpdServeDirectory (stream argument directory --) + dup "/index.html" cat2 dup exists? [ + nip httpdServeFile + ] [ + drop nip + over "200 Document follows" "text/plain" httpdResponse + [ "" over fwriteln ] dip + httpdListDirectory + ] ifte ; + +: httpdServeScript (stream argument filename --) + [ [ @argument @stdio ] dip runFile ] bind ; + +: httpdParseObjectName ( filename -- argument filename ) + dup "(.*?)\\?(.*)" groups dup [ nip push ] when swap ; + +: httpdServeObject (stream filename --) + "Serving " write dup print + httpdParseObjectName + dup exists? [ + dup directory? [ + httpdServeDirectory + ] [ + dup ".*\.lhtml" matches [ + httpdServeScript + ] [ + httpdServeFile + ] ifte + ] ifte + ] [ + 2drop "404 Not Found" httpdError + ] ifte ; + +: httpdRequest (stream request --) + httpdGetPath dup [ + httpdUriToPath httpdServeObject + ] [ + drop "400 Bad request" httpdError + ] ifte ; + +: httpdClient (socket --) + "Accepted connection from " write dup [ $socket ] bind . + [ dup freadln httpdRequest ] [ fclose ] cleave ; + +: httpdLoop (server --) + dup accept httpdClient $httpdQuit [ fclose ] [ httpdLoop ] ifte ; + +: httpd (port docroot --) + @httpdDocRoot httpdLoop ; diff --git a/factor/interpreter.factor b/factor/interpreter.factor new file mode 100644 index 0000000000..a847cc4f00 --- /dev/null +++ b/factor/interpreter.factor @@ -0,0 +1,175 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003, 2004 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +: exception? (exception -- boolean) + "java.lang.Throwable" is ; + +: printStackTrace (exception --) + [ ] "java.lang.Throwable" "printStackTrace" jmethod jinvoke ; + +: exception. (exception --) + ! If this is an Factor exception, just print the message, otherwise print + ! the entire exception as a string. + dup "factor.FactorException" is [ + [ ] "java.lang.Throwable" "getMessage" jmethod jinvoke + ] [ + >str + ] ifte print ; + +: break (exception --) + dup @error + + ! Called when the interpreter catches an exception. + "break called." print + "" print + ":w prints the callstack." print + ":j prints the Java stack." print + ":r returns to top level." print + ":s returns to top level, retaining the data stack." print + ":g continues execution (but expect another error)." print + "" print + "ERROR: " write exception. + callstack$ @errorCallStack + [ + @errorContinuation + interpreterLoop + ! If we end up here, the user just exited the err interpreter. + ! If we just call returnFromError here, its like :g and this + ! is probably not what they wanted. So we :r instead. + :r + ] callcc0 ; + +: returnFromError (--) + "Returning from break." print + f @errorCallStack + f @errorFlag + f @error ; + +: :g (--) + ! Continues execution from the point of the error. Can be dangerous. + returnFromError + $errorContinuation call ; + +: :r (--) + ! Returns to the top level. + returnFromError + !XXX + $initialInterpreterContinuation dup [ + call + ] [ + suspend + ] ifte ; + +: :s (--) + ! Returns to the top level, retaining the stack. + returnFromError + $initialInterpreterCallStack callstack@ ; + +: :j (--) + ! Print the stack trace from the exception that caused the last break. + $error dup exception? [ + printStackTrace + ] [ + "Not an exception: " write . + ] ifte ; + +: :w (--) + ! Print the current callstack, or the callstack of the last error inside an + ! error context. + $errorCallStack dup [ + drop callstack$ + ] unless . ; + +: printPrompt (--) + $errorFlag " err> " " ok> " ? write ; + +: interpreterLoop (--) + printPrompt read [ + eval + $quitFlag [ interpreterLoop ] unless + ] when* ; + +: initialInterpreterLoop (--) + ! Run the stand-alone interpreter + "Factor " $version cat2 print + "Copyright (C) 2003, 2004 Slava Pestov" print + "Enter ``help'' for help." print + "Enter ``exit'' to exit." print + ! Used by :r + [ @initialInterpreterContinuation ] callcc0 + ! Used by :s + ! We use the slightly redundant 'call' to push the current callframe. + [ callstack$ @initialInterpreterCallStack ] call + interpreterLoop ; + +: words. (--) + ! Print all defined words. + words [ . ] each ; + +: see (word --) + dup worddef [ + (word -- worddef word) + dup [ worddef dup shuffle? "~<< " ": " ? write ] dip + + (worddef word -- worddef) + write "\n " write + + dup >str write + + shuffle? " >>~\n" " ;\n" ? write + ] [ + "Not defined: " write print + ] ifte ; + +: vars. (--) + ! Print a list of defined variables. + vars [ . ] each ; + +: .s (--) + ! Prints the contents of the data stack + datastack$ . ; + +: help + "" print + "= Dynamic, interpreted, stack-based scripting language" print + "= Arbitrary precision math, ratio math" print + "= First-class, higher-order, and anonymous functions" print + "= Prototype-based object system" print + "= Continuations" print + "= Tail call optimization" print + "= Rich set of primitives based on recursion" print + "" print + "Some basic commands:" print + "clear -- clear stack." print + ".s -- print stack." print + ". -- print top of stack." print + "vars. -- list all variables." print + "$variable . -- show value of variable." print + "words. -- list all words." print + "\"word\" see -- show definition of word." print + "exit -- exit the interpreter." print + "" print ; diff --git a/factor/lists.factor b/factor/lists.factor new file mode 100644 index 0000000000..3a272a2b50 --- /dev/null +++ b/factor/lists.factor @@ -0,0 +1,149 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +: 2list (a b -- [ a b ]) + unit cons ; + +: 2rlist (a b -- [ b a ]) + swap unit cons ; + +: append ([ list1 ] [ list2 ] -- [ list1 list2 ]) + swap rappend ; + +: add ([ list1 ] elem -- [ list1 elem ]) + unit append ; + +: append@ ([ list ] variable --) + ! Adds the list to the end of the list stored in the given variable. + dup [ $ swap append ] dip @ ; + +: array>list ( array -- list ) + [ "[Ljava.lang.Object;" ] "factor.FactorList" "fromArray" jmethod jinvokeStatic ; + +: add@ (elem variable --) + ! Adds the element to the end of the list stored in the given variable. + dup [ $ swap add ] dip @ ; + +: assoc (key alist -- value) + ! Looks up the key in the given alist. An alist is a list of comma pairs, + ! the car of each pair is a key, the cdr is the value. For example: + ! [ [ 1 , "one" ] [ 2 , "two" ] [ 3 , "three" ] ] + dup [ + 2dup car car = [ + nip car cdr + ] [ + cdr assoc + ] ifte + ] [ + 2drop f + ] ifte ; + +: car ([ car , cdr ] -- car) + |factor.FactorList |car jfield jvar$ ; + +: cdr ([ car , cdr ] -- cdr) + |factor.FactorList |cdr jfield jvar$ ; + +: caar (list -- caar) + car car ; + +: cdar (list -- cadr) + cdr car ; + +: cadr (list -- cdar) + car cdr ; + +: cddr (list -- cddr) + cdr cdr ; + +: cloneList (list -- list) + ! Returns a new list where each element is a clone of the elements of + ! the given list. + dup [ [ ] "factor.FactorList" "deepClone" jmethod jinvoke ] when ; + +: cons (car cdr -- [ car , cdr ]) + [ |java.lang.Object |java.lang.Object ] |factor.FactorList jconstructor jnew ; + +: contains (elem list -- boolean) + dup [ + 2dup car = [ + 2drop t + ] [ + cdr contains + ] ifte + ] [ + 2drop f + ] ifte ; + +: cons@ (x var --) + ! Prepends x to the list stored in var. + dup [ $ cons ] dip @ ; + +: count (n -- [ 1 2 3 ... n ]) + [ [ ] times* ] cons expand ; + +: swons@ (var x --) + ! Prepends x to the list stored in var. + over $ cons s@ ; + +: get (list n -- list[n]) + [ cdr ] times car ; + +: last (list -- last) + ! Pushes last element of the list. + [ dup cdr ] [ cdr ] while car ; + +: length (list -- length) + 0 swap [ drop succ ] each ; + +: list (list[0] ... list[n] n -- list) + [ ] swap [ cons ] times ; + +: list? (list -- boolean) + dup pair? [ cdr list? ] [ f ] ifte ; + +: pair? (list -- boolean) + |factor.FactorList is ; + +: rappend ([ list2 ] [ list1 ] -- [ list1 list2 ]) + [ [ |factor.FactorList ] |factor.FactorList |append jmethod jinvoke ] when* ; + +: reverse (list -- list) + [ ] swap [ swons ] each ; + +: swons (cdr car -- [ car , cdr ]) + swap [ |java.lang.Object |java.lang.Object ] + |factor.FactorList jconstructor jnew ; + +: uncons ([ car , cdr ] -- car cdr) + dup car swap cdr ; + +: unit (a -- [ a ]) + f cons ; + +: unswons ([ car , cdr ] -- cdr car) + dup cdr swap car ; diff --git a/factor/math.factor b/factor/math.factor new file mode 100644 index 0000000000..8a833771f7 --- /dev/null +++ b/factor/math.factor @@ -0,0 +1,174 @@ +!:folding=indent:collapseFolds=0: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +: 0= (x -- boolean) + 0 = ; + +: 1= (x -- boolean) + 1 = ; + +: fixnum? (obj -- boolean) + "java.lang.Integer" is ; + +: >fixnum (num -- fixnum) + [ ] "java.lang.Number" "intValue" jmethod jinvoke ; + +: bignum? (obj -- boolean) + "java.math.BigInteger" is ; + +: >bignum (num -- bignum) + [ ] "java.lang.Number" "longValue" jmethod jinvoke + [ "long" ] "java.math.BigInteger" "valueOf" jmethod jinvokeStatic ; + +: realnum? (obj -- boolean) + dup "java.lang.Float" is + swap "java.lang.Double" is or ; + +: >realnum (num -- realnum) + [ ] "java.lang.Number" "doubleValue" jmethod jinvoke ; + +: ratio? (obj -- boolean) + "factor.FactorRatio" is ; + +: + (a b -- a+b) + [ "java.lang.Number" "java.lang.Number" ] "factor.FactorMath" "add" + jmethod jinvokeStatic ; + +: +@ (num var --) + dup [ $ + ] dip @ ; + +: - (a b -- a-b) + [ "java.lang.Number" "java.lang.Number" ] "factor.FactorMath" "subtract" + jmethod jinvokeStatic ; + +: -@ (num var --) + dup [ $ -- ] dip @ ; + +: -- (a b -- b-a) + swap - ; + +: --@ (var num --) + [ dup $ - ] dip s@ ; + +: * (a b -- a*b) + [ "java.lang.Number" "java.lang.Number" ] "factor.FactorMath" "multiply" + jmethod jinvokeStatic ; + +: *@ (num var --) + dup [ $ * ] dip @ ; + +: / (a b -- a/b) + [ "java.lang.Number" "java.lang.Number" ] "factor.FactorMath" "divide" + jmethod jinvokeStatic ; + +: /@ (num var --) + dup [ $ / ] dip @ ; + +: // (a b -- b/a) + swap / ; + +: //@ (num var --) + [ dup $ / ] dip s@ ; + +: > (a b -- boolean) + [ "float" "float" ] "factor.FactorMath" "greater" jmethod jinvokeStatic ; + +: >= (a b -- boolean) + [ "float" "float" ] "factor.FactorMath" "greaterEqual" jmethod jinvokeStatic ; + +: < (a b -- boolean) + [ "float" "float" ] "factor.FactorMath" "less" jmethod jinvokeStatic ; + +: <= (a b -- boolean) + [ "float" "float" ] "factor.FactorMath" "lessEqual" jmethod jinvokeStatic ; + +: and (a b -- a&b) + f ? ; + +: mag2 (x y -- mag) + ! Returns the magnitude of the vector (x,y). + sq swap sq + sqrt ; + +: neg (x -- -x) + 0 swap - ; + +: neg@ (var --) + dup $ 0 swap - s@ ; + +: not (a -- a) + ! Pushes f is the object is not f, t if the object is f. + f t ? ; + +: not@ (boolean -- boolean) + dup $ not s@ ; + +: pred (n -- n-1) + 1 - ; + +: succ (n -- nsucc) + 1 + ; + +: pred@ (var --) + dup $ 1 - s@ ; + +: or (a b -- a|b) + t swap ? ; + +: recip (x -- 1/x) + 1 // ; + +: sq (x -- x^2) + dup * ; + +: sqrt (x -- sqrt x) + [ "double" ] "java.lang.Math" "sqrt" jmethod jinvokeStatic ; + +: succ@ (var --) + dup $ 1 + s@ ; + +: deg2rad (degrees -- radians) + $pi * 180 / ; + +: rad2deg (radians -- degrees) + 180 * $pi / ; + +: fib (n -- nth fibonacci number) + ! This is the naive implementation, for benchmarking purposes. + [ dup 1 <= ] [ ] [ pred dup pred ] [ + ] binrec ; + +: fac (n -- n!) + ! This is the naive implementation, for benchmarking purposes. + 1 swap [ succ * ] times* ; + +: harmonic (n -- 1 + 1/2 + 1/3 + ... + 1/n) + 0 swap [ succ recip + ] times* ; + +2.7182818284590452354 @e +3.14159265358979323846 @pi + +1.0 0.0 / @inf +-1.0 0.0 / @-inf diff --git a/factor/miscellaneous.factor b/factor/miscellaneous.factor new file mode 100644 index 0000000000..5c3bf79b88 --- /dev/null +++ b/factor/miscellaneous.factor @@ -0,0 +1,93 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +: >str (obj -- string) + ! Returns the Java string representation of this object. + [ ] "java.lang.Object" "toString" jmethod jinvoke ; + +: = (a b -- boolean) + ! Returns true if a = b. + [ "java.lang.Object" "java.lang.Object" ] + "factor.FactorLib" "equal" jmethod jinvokeStatic ; + +: clone (obj -- obj) + [ ] "factor.PublicCloneable" "clone" jmethod jinvoke ; + +: cloneArray (obj -- obj) + [ "[Ljava.lang.Object;" ] "factor.FactorLib" "cloneArray" + jmethod jinvokeStatic ; + +: deepCloneArray (obj -- obj) + [ "[Ljava.lang.Object;" ] "factor.FactorLib" "deepCloneArray" + jmethod jinvokeStatic ; + +: is (obj class -- boolean) + ! Like "instanceof" in Java. + [ "java.lang.Object" ] "java.lang.Class" "isInstance" + jmethod jinvoke ; + +: not= (a b -- boolean) + = not ; + +: 2= (a b c d -- boolean) + ! Returns true if a = c, b = d. + swapd = [ = ] dip and ; + +: ? (cond obj1 obj2 -- obj) + ! Pushes obj1 if cond is true, obj2 if cond is false. + [ "boolean" "java.lang.Object" "java.lang.Object" ] + "factor.FactorLib" "branch2" jmethod jinvokeStatic ; + +: >=< (x y obj1 obj2 obj3 -- obj) + ! If x > y, pushes obj1, if x = y, pushes obj2, else obj3. + [ + "float" "float" + "java.lang.Object" "java.lang.Object" "java.lang.Object" + ] + "factor.FactorLib" "branch3" jmethod jinvokeStatic ; + +: error (msg --) + [ "java.lang.String" ] "factor.FactorLib" "error" jmethod jinvokeStatic ; + +: exit* (code --) + [ |int ] |java.lang.System |exit jmethod jinvokeStatic ; + +: exit (--) + 0 exit* ; + +: millis (-- millis) + ! Pushes the current time, in milliseconds. + [ ] |java.lang.System |currentTimeMillis jmethod jinvokeStatic + >bignum ; + +: stack>list (stack -- list) + ! Turns a callstack or datastack object into a list. + [ ] "factor.FactorArrayStack" "toList" jmethod jinvoke ; + +: time (code --) + ! Evaluates the given code and prints the time taken to execute it. + millis swap dip millis -- . ; diff --git a/factor/namespaces.factor b/factor/namespaces.factor new file mode 100644 index 0000000000..b5c4554cae --- /dev/null +++ b/factor/namespaces.factor @@ -0,0 +1,68 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003, 2004 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +: lazy (var [ a ] -- value) + ! If the value of the variable is f, set the value to the result of + ! evaluating [ a ]. + over $ [ drop $ ] [ dip dupd @ ] ifte ; + +: namespace? (a -- boolean) + |factor.FactorNamespace is ; + +: (-- namespace) + $namespace [ |factor.FactorNamespace ] |factor.FactorNamespace + jconstructor jnew ; + +: ( object -- namespace ) + $namespace swap + [ "factor.FactorNamespace" "java.lang.Object" ] + "factor.FactorNamespace" jconstructor jnew ; + +: extend (object code -- object) + ! Used in code like this: + ! : + ! [ + ! .... + ! ] extend ; + over [ bind ] dip ; + +: import (class pairs --) + ! Import some static variables from a Java class into the current namespace. + $namespace [ |java.lang.String |factor.FactorList ] + |factor.FactorNamespace |importVars + jmethod jinvoke ; + +: vars (-- list) + $namespace [ ] |factor.FactorNamespace |toVarList jmethod jinvoke ; + +: uvar? (name --) + [ "namespace" "parent" ] contains not ; + +: uvars (-- list) + ! Does not include "namespace" and "parent" variables; ie, all user-defined + ! variables in given namespace. + vars [ uvar? ] subset ; diff --git a/factor/network.factor b/factor/network.factor new file mode 100644 index 0000000000..7d97870fc1 --- /dev/null +++ b/factor/network.factor @@ -0,0 +1,60 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +: ( port -- stream ) + ! Starts listening on localhost:port. Returns a stream that you can close + ! with fclose. No other stream operations are supported. + [ "int" ] "java.net.ServerSocket" jconstructor jnew + [ + @socket + + ( -- ) + [ + $socket [ ] "java.net.ServerSocket" "close" jmethod jinvoke + ] @fclose + ] extend ; + +: ( socket -- stream ) + ! Wraps a socket inside a bytestream. + dup + [ [ ] "java.net.Socket" "getInputStream" jmethod jinvoke ] + [ [ ] "java.net.Socket" "getOutputStream" jmethod jinvoke ] + cleave + [ + @socket + + ! We "extend" bytestream's fclose. + ( -- ) + $fclose [ + $socket [ ] "java.net.Socket" "close" jmethod jinvoke + ] append @fclose + ] extend ; + +: accept ( server -- client ) + ! Accept a connection from a server socket. + [ $socket ] bind + [ ] "java.net.ServerSocket" "accept" jmethod jinvoke ; diff --git a/factor/parser.factor b/factor/parser.factor new file mode 100644 index 0000000000..fdf1f76711 --- /dev/null +++ b/factor/parser.factor @@ -0,0 +1,42 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +: parse (string -- list) + f swap parse* ; + +: eval ("X" -- X) + parse call ; + +: runFile (path --) + dup parse* call ; + +: unparse (X -- "X") + [ |java.lang.Object ] |factor.FactorJava |factorTypeToString + jmethod jinvokeStatic ; + +: . (expr --) + unparse print ; diff --git a/factor/random.factor b/factor/random.factor new file mode 100644 index 0000000000..1c8afd29b9 --- /dev/null +++ b/factor/random.factor @@ -0,0 +1,95 @@ +!:folding=indent:collapseFolds=0: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +: randomAngle (-- theta) + [ ] "factor.FactorMath" "randomAngle" jmethod jinvokeStatic ; + +: randomBoolean (-- boolean) + [ ] "factor.FactorMath" "randomBoolean" jmethod jinvokeStatic ; + +: randomInt (min max -- random) + [ "int" "int" ] "factor.FactorMath" "randomInt" jmethod jinvokeStatic ; + +: randomSymmetricInt (max -- random) + ! Return a random integer between -max and max. + dup neg swap randomInt ; + +: chance (n -- boolean) + ! Returns true with a 1/n probability, false with a (n-1)/n probability. + 1 swap randomInt 1 = ; + +: randomElement (list -- random) + ! Returns a random element from the given list. + dup length pred 0 swap randomInt get ; + +: randomSubset (list -- list) + ! Returns a random subset of the given list. Each item is chosen with a 50% + ! probability. + [ ] [ randomBoolean [ drop ] when ] map ; + +: car+ (list -- sum) + ! Adds the car of each element of the given list. + 0 swap [ car + ] each ; + +: randomProbability (list -- sum) + ! Adds the car of each element of the given list, and returns a random + ! number between 1 and this sum. + 1 swap car+ randomInt ; + +: randomElementIter (list index -- elem) + ! Used by randomElement*. Do not call directly. + [ unswons unswons ] dip (list elem probability index) + -- (list elem index) + dup 0 <= [ + drop nip + ] [ + nip randomElementIter + ] ifte ; + +: randomElement* (list -- elem) + ! Returns a random element of the given list of comma pairs. The + ! car of each pair is a probability, the cdr is the item itself. + ! Only the cdr of the comma pair is returned. + dup 1 swap car+ randomInt randomElementIter ; + +: randomSubset* (list -- list) + ! Returns a random subset of the given list of comma pairs. The + ! car of each pair is a probability, the cdr is the item itself. + ! Only the cdr of the comma pair is returned. + dup [ [ [ ] ] dip car+ ] dip ([ ] probabilitySum list) + [ + [ 1 over randomInt ] dip ([ ] probabilitySum probability elem) + uncons ([ ] probabilitySum probability elema elemd) + -rot ([ ] probabilitySum elemd probability elema) + > ([ ] probabilitySum elemd boolean) + [ + drop + ] [ + -rot (elemd [ ] probabilitySum) + [ cons ] dip ([ elemd ] probabilitySum) + ] ifte + ] each drop ; diff --git a/factor/stream.factor b/factor/stream.factor new file mode 100644 index 0000000000..e61a79ff71 --- /dev/null +++ b/factor/stream.factor @@ -0,0 +1,230 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +: ( -- stream ) + ! Create a stream object. A stream is a namespace with the following + ! entries: + ! - fflush + ! - freadln -- you must provide an implementation! + ! - fwriteln + ! - fwrite -- you must provide an implementation! + ! - fclose + ! Note that you must extend this object and provide your own implementations + ! of all entries except for fwriteln, which is defined to fwrite the string + ! followed by the newline by default. + [ + + ( -- string ) + [ "freadln not implemented." break ] @freadln + ( string -- ) + [ "fwrite not implemented." break ] @fwrite + ( -- ) + [ ] @fflush + ( -- ) + [ ] @fclose + ( string -- ) + [ $namespace fwrite "\n" $namespace fwrite ] @fwriteln + ] extend ; + +: ( in out -- stream ) + ! Creates a new stream for reading from the java.io.InputStream in, and + ! writing to the java.io.OutputStream out. + [ + @out + @in + + ( -- string ) + [ + $in [ "java.io.InputStream" ] "factor.FactorLib" "readLine" + jmethod jinvokeStatic + ] @freadln + + ( string -- ) + [ + >bytes + $out [ "[B" ] "java.io.OutputStream" "write" jmethod jinvoke + ] @fwrite + + ( -- ) + [ + $out [ ] "java.io.OutputStream" "flush" jmethod jinvoke + ] @fflush + + ( -- ) + [ + $in [ ] "java.io.InputStream" "close" jmethod jinvoke + $out [ ] "java.io.OutputStream" "close" jmethod jinvoke + ] @fclose + ] extend ; + +: ( in out -- stream ) + ! Creates a new stream for reading from the java.io.BufferedReader in, and + ! writing to the java.io.Reader out. + [ + @out + @in + + ( -- string ) + [ + $in [ ] "java.io.BufferedReader" "readLine" jmethod jinvoke + ] @freadln + + ( string -- ) + [ + $out [ "java.lang.String" ] "java.io.Writer" "write" jmethod jinvoke + ] @fwrite + + ( -- ) + [ + $out [ ] "java.io.Writer" "flush" jmethod jinvoke + ] @fflush + + ( -- ) + [ + $in [ ] "java.io.Reader" "close" jmethod jinvoke + $out [ ] "java.io.Writer" "close" jmethod jinvoke + ] @fclose + ] extend ; + +: ( path -- stream ) + [ |java.lang.String ] |java.io.FileReader jconstructor jnew + f + ; + +: ( path -- stream ) + f + [ |java.lang.String ] |java.io.FileWriter jconstructor jnew + ; + +: ( path -- stream ) + [ |java.lang.String ] |java.io.FileInputStream jconstructor jnew + f + ; + +: ( path -- stream ) + f + [ |java.lang.String ] |java.io.FileOutputStream jconstructor jnew + ; + +: (writer -- bwriter) + [ |java.io.Writer ] |java.io.BufferedWriter jconstructor jnew ; + +: (outputstream -- owriter) + [ |java.io.OutputStream ] |java.io.OutputStreamWriter jconstructor jnew ; + +: read ( -- string ) + $stdio freadln ; + +: write ( string -- ) + $stdio [ fwrite ] [ fflush ] cleave ; + +: print ( string -- ) + $stdio [ fwriteln ] [ fflush ] cleave ; + +: fflush ( stream -- ) + [ $fflush call ] bind ; + +: freadln ( stream -- string ) + [ $freadln call ] bind ; + +: fwriteln ( string stream -- ) + [ $fwriteln call ] bind ; + +: fwrite ( string stream -- ) + [ $fwrite call ] bind ; + +: fclose ( stream -- ) + [ $fclose call ] bind ; + +: fcopy ( from to -- ) + ! Copy the contents of the bytestream 'from' to the bytestream 'to'. + [ [ $in ] bind ] dip + [ $out ] bind + [ "java.io.InputStream" "java.io.OutputStream" ] + "factor.FactorLib" "copy" jmethod jinvokeStatic ; + +"java.lang.System" "in" jfield jvarStatic$ @stdin +"java.lang.System" "out" jfield jvarStatic$ @stdout +$stdin $stdout @stdio + +!(file -- freader) +| [ + [ |java.lang.String ] |java.io.FileReader jconstructor jnew +] define + +: (path -- file) + dup "java.io.File" is not [ + [ "java.lang.String" ] "java.io.File" jconstructor jnew + ] when ; + +: exists? (file -- boolean) + [ ] "java.io.File" "exists" jmethod jinvoke ; + +: directory? (file -- boolean) + [ ] "java.io.File" "isDirectory" jmethod jinvoke ; + +: directory ( file -- listing ) + [ ] "java.io.File" "list" jmethod jinvoke + array>list ; + +: rename ( from to -- ) + ! Rename file 'from' to 'to'. These can be paths or + ! java.io.File instances. + swap + [ "java.io.File" ] "java.io.File" "renameTo" + jmethod jinvoke ; + +!(string -- reader) +| [ + [ |java.lang.String ] |java.io.StringReader jconstructor jnew +] define + +: close (stream --) + dup "java.io.Reader" is + [ ] "java.io.Reader" "close" jmethod + [ ] "java.io.Writer" "close" jmethod + ? + jinvoke ; + +: exec (args -- exitCode) + [ "[Ljava.lang.String;" ] "factor.FactorLib" "exec" jmethod jinvokeStatic ; + +!(stream -- string) +|read* [ + [ ] |java.io.BufferedReader |readLine jmethod jinvoke +] define + +: print* (string stream --) + tuck write* + "\n" swap write* ; + +!(string stream --) +|write* [ + tuck + [ |java.lang.String ] |java.io.Writer |write jmethod jinvoke + [ ] |java.io.Writer |flush jmethod jinvoke +] define diff --git a/factor/strings.factor b/factor/strings.factor new file mode 100644 index 0000000000..ed7bfb7dfe --- /dev/null +++ b/factor/strings.factor @@ -0,0 +1,137 @@ +!:folding=indent:collapseFolds=1: + +! $Id$ +! +! Copyright (C) 2003 Slava Pestov. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, +! this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +! Used by chars>entities +[ + [ #\< , "<" ] + [ #\> , ">" ] + [ #\& , "&" ] +! Bad parser! +! [ #\' , "'" ] +! [ #\" , """ ] +] @entities + +: >bytes ( string -- array ) + ! Converts a string to an array of ASCII bytes. An exception is thrown + ! if the string contains non-ASCII characters. + "ASCII" swap + [ "java.lang.String" ] "java.lang.String" "getBytes" jmethod jinvoke ; + +: cat ([ "a" "b" "c" ] -- "abc") + [ "factor.FactorList" ] "factor.FactorLib" "cat" jmethod jinvokeStatic ; + +: cat2 ("a" "b" -- "ab") + [ "java.lang.Object" "java.lang.Object" ] + "factor.FactorLib" "cat2" jmethod jinvokeStatic ; + +: cat3 ("a" "b" "c" -- "abc") + [ "java.lang.Object" "java.lang.Object" "java.lang.Object" ] + "factor.FactorLib" "cat3" jmethod jinvokeStatic ; + +: cat4 ("a" "b" "c" "d" -- "abcd") + cat2 cat3 ; + +: chars>entities (str -- str) + ! Convert <, >, &, ' and " to HTML entities. + "" [ dup $entities assoc dup [ nip ] [ drop ] ifte ] strmap ; + +: group (index match --) + [ "int" ] "java.util.regex.Matcher" "group" + jmethod jinvoke ; + +: groupCount (matcher -- count) + [ ] "java.util.regex.Matcher" "groupCount" + jmethod jinvoke ; + +: groups* (matcher -- list) + [ + [ + dup groupCount [ + succ over group swap + ] times* drop + ] cons expand + ] [matches] ; + +: groups (input regex -- list) + groups* ; + +: [matches] ( matcher code -- boolean ) + ! If the matcher's matches* function returns true, + ! evaluate the code with the matcher at the top of the + ! stack. Otherwise, pop the matcher off the stack and + ! push f. + [ dup matches* ] dip [ drop f ] ifte ; + +: (string pattern -- matcher) + [ "java.lang.CharSequence" ] + "java.util.regex.Pattern" "matcher" + jmethod jinvoke ; + +: matches* (matcher -- boolean) + [ ] "java.util.regex.Matcher" "matches" + jmethod jinvoke ; + +: matches (input regex -- boolean) + matches* ; + +: replace* ( replace matcher -- string ) + [ "java.lang.String" ] "java.util.regex.Matcher" + "replaceAll" jmethod jinvoke ; + +: replace ( input regex replace -- string ) + ! Replaces all occurrences of the regex in the input string + ! with the replace string. + -rot replace* ; + +: (pattern -- regex) + ! Compile the regex, if its not already compiled. + dup "java.util.regex.Pattern" is not [ + [ "java.lang.String" ] "java.util.regex.Pattern" "compile" + jmethod jinvokeStatic + ] when ; + +: strget (index str -- char) + [ "int" ] "java.lang.String" "charAt" jmethod jinvoke ; + +: strlen (str -- length) + [ ] "java.lang.String" "length" jmethod jinvoke ; + +: streach (str [ code ] --) + ! Execute the code, with each character of the string pushed onto the + ! stack. + over strlen [ + -rot 2dup [ [ strget ] dip call ] 2dip + ] times* 2drop ; + +: strmap (str initial [ code ] -- [ mapping ]) + ! If the 'initial' parameter is f, turn it into "". + ! Maybe cat should handle this instead? + [ dup [ drop "" ] unless ] dip + swapd [ ] cons cons cons + restack + streach + unstack cat ; diff --git a/org/objectweb/asm/Attribute.java b/org/objectweb/asm/Attribute.java new file mode 100644 index 0000000000..1cb311e442 --- /dev/null +++ b/org/objectweb/asm/Attribute.java @@ -0,0 +1,213 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +/** + * A non standard class, field, method or code attribute. + */ + +public abstract class Attribute { + + /** + * The type of this attribute. + */ + + public final String type; + + /** + * The next attribute in this attribute list. May be null. + */ + + public Attribute next; + + /** + * Constructs a new empty attribute. + * + * @param type the type of the attribute. + */ + + public Attribute (final String type) { + this.type = type; + } + + /** + * Returns the labels corresponding to this attribute. + * + * @return the labels corresponding to this attribute, or null if + * this attribute is not a code attribute that contains labels. + */ + + protected Label[] getLabels () { + return null; + } + + /** + * Reads a {@link #type type} attribute. This method must return a new + * {@link Attribute} object, of type {@link #type type}, corresponding to the + * len bytes starting at the given offset, in the given class reader. + * + * @param cr the class that contains the attribute to be read. + * @param off index of the first byte of the attribute's content in {@link + * ClassReader#b cr.b}. The 6 attribute header bytes, containing the type + * and the length of the attribute, are not taken into account here. + * @param len the length of the attribute's content. + * @param buf buffer to be used to call {@link ClassReader#readUTF8 readUTF8}, + * {@link ClassReader#readClass readClass} or {@link + * ClassReader#readConst readConst}. + * @param codeOff index of the first byte of code's attribute content in + * {@link ClassReader#b cr.b}, or -1 if the attribute to be read is not a + * code attribute. The 6 attribute header bytes, containing the type and + * the length of the attribute, are not taken into account here. + * @param labels the labels of the method's code, or null if the + * attribute to be read is not a code attribute. + * @return a new {@link Attribute} object corresponding to the given + * bytes. + */ + + protected abstract Attribute read ( + ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels); + + /** + * Returns the byte array form of this attribute. + * + * @param cw the class to which this attribute must be added. This parameter + * can be used to add to the constant pool of this class the items that + * corresponds to this attribute. + * @param code the bytecode of the method corresponding to this code + * attribute, or null if this attribute is not a code + * attributes. + * @param len the length of the bytecode of the method corresponding to this + * code attribute, or null if this attribute is not a code + * attribute. + * @param maxStack the maximum stack size of the method corresponding to this + * code attribute, or -1 if this attribute is not a code attribute. + * @param maxLocals the maximum number of local variables of the method + * corresponding to this code attribute, or -1 if this attribute is not + * a code attribute. + * @return the byte array form of this attribute. + */ + + protected abstract ByteVector write ( + ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals); + + /** + * Returns the length of the attribute list that begins with this attribute. + * + * @return the length of the attribute list that begins with this attribute. + */ + + final int getCount () { + int count = 0; + Attribute attr = this; + while (attr != null) { + count += 1; + attr = attr.next; + } + return count; + } + + /** + * Returns the size of all the attributes in this attribute list. + * + * @param cw the class writer to be used to convert the attributes into byte + * arrays, with the {@link #write write} method. + * @param code the bytecode of the method corresponding to these code + * attributes, or null if these attributes are not code + * attributes. + * @param len the length of the bytecode of the method corresponding to these + * code attributes, or null if these attributes are not code + * attributes. + * @param maxStack the maximum stack size of the method corresponding to these + * code attributes, or -1 if these attributes are not code attributes. + * @param maxLocals the maximum number of local variables of the method + * corresponding to these code attributes, or -1 if these attributes are + * not code attributes. + * @return the size of all the attributes in this attribute list. This size + * includes the size of the attribute headers. + */ + + final int getSize ( + final ClassWriter cw, + final byte[] code, + final int len, + final int maxStack, + final int maxLocals) + { + int size = 0; + Attribute attr = this; + while (attr != null) { + cw.newUTF8(attr.type); + size += attr.write(cw, code, len, maxStack, maxLocals).length + 6; + attr = attr.next; + } + return size; + } + + /** + * Writes all the attributes of this attribute list in the given byte vector. + * + * @param cw the class writer to be used to convert the attributes into byte + * arrays, with the {@link #write write} method. + * @param code the bytecode of the method corresponding to these code + * attributes, or null if these attributes are not code + * attributes. + * @param len the length of the bytecode of the method corresponding to these + * code attributes, or null if these attributes are not code + * attributes. + * @param maxStack the maximum stack size of the method corresponding to these + * code attributes, or -1 if these attributes are not code attributes. + * @param maxLocals the maximum number of local variables of the method + * corresponding to these code attributes, or -1 if these attributes are + * not code attributes. + * @param out where the attributes must be written. + */ + + final void put ( + final ClassWriter cw, + final byte[] code, + final int len, + final int maxStack, + final int maxLocals, + final ByteVector out) + { + Attribute attr = this; + while (attr != null) { + ByteVector b = attr.write(cw, code, len, maxStack, maxLocals); + out.putShort(cw.newUTF8(attr.type)).putInt(b.length); + out.putByteArray(b.data, 0, b.length); + attr = attr.next; + } + } +} diff --git a/org/objectweb/asm/ByteVector.java b/org/objectweb/asm/ByteVector.java new file mode 100644 index 0000000000..8110c5977b --- /dev/null +++ b/org/objectweb/asm/ByteVector.java @@ -0,0 +1,293 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +/** + * A dynamically extensible vector of bytes. This class is roughly equivalent to + * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient. + */ + +public class ByteVector { + + /** + * The content of this vector. + */ + + byte[] data; + + /** + * Actual number of bytes in this vector. + */ + + int length; + + /** + * Constructs a new {@link ByteVector ByteVector} with a default initial size. + */ + + public ByteVector () { + data = new byte[64]; + } + + /** + * Constructs a new {@link ByteVector ByteVector} with the given initial size. + * + * @param initialSize the initial size of the byte vector to be constructed. + */ + + public ByteVector (final int initialSize) { + data = new byte[initialSize]; + } + + /** + * Puts a byte into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param b a byte. + * @return this byte vector. + */ + + public ByteVector putByte (final int b) { + int length = this.length; + if (length + 1 > data.length) { + enlarge(1); + } + data[length++] = (byte)b; + this.length = length; + return this; + } + + /** + * Puts two bytes into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param b1 a byte. + * @param b2 another byte. + * @return this byte vector. + */ + + ByteVector put11 (final int b1, final int b2) { + int length = this.length; + if (length + 2 > data.length) { + enlarge(2); + } + byte[] data = this.data; + data[length++] = (byte)b1; + data[length++] = (byte)b2; + this.length = length; + return this; + } + + /** + * Puts a short into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param s a short. + * @return this byte vector. + */ + + public ByteVector putShort (final int s) { + int length = this.length; + if (length + 2 > data.length) { + enlarge(2); + } + byte[] data = this.data; + data[length++] = (byte)(s >>> 8); + data[length++] = (byte)s; + this.length = length; + return this; + } + + /** + * Puts a byte and a short into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param b a byte. + * @param s a short. + * @return this byte vector. + */ + + ByteVector put12 (final int b, final int s) { + int length = this.length; + if (length + 3 > data.length) { + enlarge(3); + } + byte[] data = this.data; + data[length++] = (byte)b; + data[length++] = (byte)(s >>> 8); + data[length++] = (byte)s; + this.length = length; + return this; + } + + /** + * Puts an int into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param i an int. + * @return this byte vector. + */ + + public ByteVector putInt (final int i) { + int length = this.length; + if (length + 4 > data.length) { + enlarge(4); + } + byte[] data = this.data; + data[length++] = (byte)(i >>> 24); + data[length++] = (byte)(i >>> 16); + data[length++] = (byte)(i >>> 8); + data[length++] = (byte)i; + this.length = length; + return this; + } + + /** + * Puts a long into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param l a long. + * @return this byte vector. + */ + + public ByteVector putLong (final long l) { + int length = this.length; + if (length + 8 > data.length) { + enlarge(8); + } + byte[] data = this.data; + int i = (int)(l >>> 32); + data[length++] = (byte)(i >>> 24); + data[length++] = (byte)(i >>> 16); + data[length++] = (byte)(i >>> 8); + data[length++] = (byte)i; + i = (int)l; + data[length++] = (byte)(i >>> 24); + data[length++] = (byte)(i >>> 16); + data[length++] = (byte)(i >>> 8); + data[length++] = (byte)i; + this.length = length; + return this; + } + + /** + * Puts an UTF8 string into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param s a String. + * @return this byte vector. + */ + + public ByteVector putUTF8 (final String s) { + int charLength = s.length(); + int byteLength = 0; + for (int i = 0; i < charLength; ++i) { + char c = s.charAt(i); + if (c >= '\001' && c <= '\177') { + byteLength++; + } else if (c > '\u07FF') { + byteLength += 3; + } else { + byteLength += 2; + } + } + if (byteLength > 65535) { + throw new IllegalArgumentException(); + } + int length = this.length; + if (length + 2 + byteLength > data.length) { + enlarge(2 + byteLength); + } + byte[] data = this.data; + data[length++] = (byte)(byteLength >>> 8); + data[length++] = (byte)(byteLength); + for (int i = 0; i < charLength; ++i) { + char c = s.charAt(i); + if (c >= '\001' && c <= '\177') { + data[length++] = (byte)c; + } else if (c > '\u07FF') { + data[length++] = (byte)(0xE0 | c >> 12 & 0xF); + data[length++] = (byte)(0x80 | c >> 6 & 0x3F); + data[length++] = (byte)(0x80 | c & 0x3F); + } else { + data[length++] = (byte)(0xC0 | c >> 6 & 0x1F); + data[length++] = (byte)(0x80 | c & 0x3F); + } + } + this.length = length; + return this; + } + + /** + * Puts an array of bytes into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param b an array of bytes. May be null to put len null + * bytes into this byte vector. + * @param off index of the fist byte of b that must be copied. + * @param len number of bytes of b that must be copied. + * @return this byte vector. + */ + + public ByteVector putByteArray ( + final byte[] b, + final int off, + final int len) + { + if (length + len > data.length) { + enlarge(len); + } + if (b != null) { + System.arraycopy(b, off, data, length, len); + } + length += len; + return this; + } + + /** + * Enlarge this byte vector so that it can receive n more bytes. + * + * @param size number of additional bytes that this byte vector should be + * able to receive. + */ + + private void enlarge (final int size) { + int length1 = 2 * data.length; + int length2 = length + size; + byte[] newData = new byte[length1 > length2 ? length1 : length2]; + System.arraycopy(data, 0, newData, 0, length); + data = newData; + } +} diff --git a/org/objectweb/asm/ClassAdapter.java b/org/objectweb/asm/ClassAdapter.java new file mode 100644 index 0000000000..a71e741177 --- /dev/null +++ b/org/objectweb/asm/ClassAdapter.java @@ -0,0 +1,109 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +/** + * An empty {@link ClassVisitor ClassVisitor} that delegates to another {@link + * ClassVisitor ClassVisitor}. This class can be used as a super class to + * quickly implement usefull class adapter classes, just by overriding the + * necessary methods. + */ + +public class ClassAdapter implements ClassVisitor { + + /** + * The {@link ClassVisitor ClassVisitor} to which this adapter delegates + * calls. + */ + + protected ClassVisitor cv; + + /** + * Constructs a new {@link ClassAdapter ClassAdapter} object. + * + * @param cv the class visitor to which this adapter must delegate calls. + */ + + public ClassAdapter (final ClassVisitor cv) { + this.cv = cv; + } + + public void visit ( + final int access, + final String name, + final String superName, + final String[] interfaces, + final String sourceFile) + { + cv.visit(access, name, superName, interfaces, sourceFile); + } + + public void visitInnerClass ( + final String name, + final String outerName, + final String innerName, + final int access) + { + cv.visitInnerClass(name, outerName, innerName, access); + } + + public void visitField ( + final int access, + final String name, + final String desc, + final Object value, + final Attribute attrs) + { + cv.visitField(access, name, desc, value, attrs); + } + + public CodeVisitor visitMethod ( + final int access, + final String name, + final String desc, + final String[] exceptions, + final Attribute attrs) + { + return new CodeAdapter(cv.visitMethod(access, name, desc, exceptions, attrs)); + } + + public void visitAttribute (final Attribute attr) { + cv.visitAttribute(attr); + } + + public void visitEnd () { + cv.visitEnd(); + } +} diff --git a/org/objectweb/asm/ClassReader.java b/org/objectweb/asm/ClassReader.java new file mode 100644 index 0000000000..57ca8eabb5 --- /dev/null +++ b/org/objectweb/asm/ClassReader.java @@ -0,0 +1,1011 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +import java.io.InputStream; +import java.io.IOException; + +/** + * A Java class parser to make a {@link ClassVisitor ClassVisitor} visit an + * existing class. This class parses a byte array conforming to the Java class + * file format and calls the appropriate visit methods of a given class visitor + * for each field, method and bytecode instruction encountered. + */ + +public class ClassReader { + + /** + * The class to be parsed. The content of this array must not be + * modified. This field is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + */ + + public final byte[] b; + + /** + * The start index of each constant pool item in {@link #b b}, plus one. The + * one byte offset skips the constant pool item tag that indicates its type. + */ + + private int[] items; + + /** + * The String objects corresponding to the CONSTANT_Utf8 items. This cache + * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item, which + * GREATLY improves performances (by a factor 2 to 3). This caching strategy + * could be extended to all constant pool items, but its benefit would not be + * so great for these items (because they are much less expensive to parse + * than CONSTANT_Utf8 items). + */ + + private String[] strings; + + /** + * Maximum length of the strings contained in the constant pool of the class. + */ + + private int maxStringLength; + + /** + * Start index of the class header information (access, name...) in {@link #b + * b}. + */ + + private int header; + + // -------------------------------------------------------------------------- + // Constructors + // -------------------------------------------------------------------------- + + /** + * Constructs a new {@link ClassReader ClassReader} object. + * + * @param b the bytecode of the class to be read. + */ + + public ClassReader (final byte[] b) { + this(b, 0, b.length); + } + + /** + * Constructs a new {@link ClassReader ClassReader} object. + * + * @param b the bytecode of the class to be read. + * @param off the start offset of the class data. + * @param len the length of the class data. + */ + + public ClassReader (final byte[] b, final int off, final int len) { + this.b = b; + // parses the constant pool + items = new int[readUnsignedShort(off + 8)]; + strings = new String[items.length]; + int max = 0; + int index = off + 10; + for (int i = 1; i < items.length; ++i) { + items[i] = index + 1; + int tag = b[index]; + int size; + switch (tag) { + case ClassWriter.FIELD: + case ClassWriter.METH: + case ClassWriter.IMETH: + case ClassWriter.INT: + case ClassWriter.FLOAT: + case ClassWriter.NAME_TYPE: + size = 5; + break; + case ClassWriter.LONG: + case ClassWriter.DOUBLE: + size = 9; + ++i; + break; + case ClassWriter.UTF8: + size = 3 + readUnsignedShort(index + 1); + max = (size > max ? size : max); + break; + //case ClassWriter.CLASS: + //case ClassWriter.STR: + default: + size = 3; + break; + } + index += size; + } + maxStringLength = max; + // the class header information starts just after the constant pool + header = index; + } + + /** + * Constructs a new {@link ClassReader ClassReader} object. + * + * @param is an input stream from which to read the class. + * @throws IOException if a problem occurs during reading. + */ + + public ClassReader (final InputStream is) throws IOException { + this(readClass(is)); + } + + /** + * Constructs a new {@link ClassReader ClassReader} object. + * + * @param name the fully qualified name of the class to be read. + * @throws IOException if an exception occurs during reading. + */ + + public ClassReader (final String name) throws IOException { + this((ClassReader.class.getClassLoader() == null + ? ClassLoader.getSystemClassLoader() + : ClassReader.class.getClassLoader()) + .getSystemResourceAsStream(name.replace('.','/') + ".class")); + } + + /** + * Reads the bytecode of a class. + * + * @param is an input stream from which to read the class. + * @return the bytecode read from the given input stream. + * @throws IOException if a problem occurs during reading. + */ + + private static byte[] readClass (final InputStream is) throws IOException { + if (is == null) { + throw new IOException("Class not found"); + } + byte[] b = new byte[is.available()]; + int len = 0; + while (true) { + int n = is.read(b, len, b.length - len); + if (n == -1) { + if (len < b.length) { + byte[] c = new byte[len]; + System.arraycopy(b, 0, c, 0, len); + b = c; + } + return b; + } else { + len += n; + if (len == b.length) { + byte[] c = new byte[b.length + 1000]; + System.arraycopy(b, 0, c, 0, len); + b = c; + } + } + } + } + + // -------------------------------------------------------------------------- + // Public methods + // -------------------------------------------------------------------------- + + /** + * Returns the major and minor version numbers of this class. + * + * @return the int array { major version, minor version } + */ + + public int[] getVersion () { + return new int[] { readShort(6), readShort(4) }; + } + + /** + * Makes the given visitor visit the Java class of this {@link ClassReader + * ClassReader}. This class is the one specified in the constructor (see + * {@link #ClassReader ClassReader}). + * + * @param classVisitor the visitor that must visit this class. + * @param skipDebug true if the debug information of the class must + * not be visited. In this case the {@link CodeVisitor#visitLocalVariable + * visitLocalVariable} and {@link CodeVisitor#visitLineNumber + * visitLineNumber} methods will not be called. + */ + + public void accept ( + final ClassVisitor classVisitor, + final boolean skipDebug) + { + accept(classVisitor, new Attribute[0], skipDebug); + } + + /** + * Makes the given visitor visit the Java class of this {@link ClassReader + * ClassReader}. This class is the one specified in the constructor (see + * {@link #ClassReader ClassReader}). + * + * @param classVisitor the visitor that must visit this class. + * @param attrs prototypes of the attributes that must be parsed during the + * visit of the class. Any attribute whose type is not equal to the type + * of one the prototypes will be ignored. + * @param skipDebug true if the debug information of the class must + * not be visited. In this case the {@link CodeVisitor#visitLocalVariable + * visitLocalVariable} and {@link CodeVisitor#visitLineNumber + * visitLineNumber} methods will not be called. + */ + + public void accept ( + final ClassVisitor classVisitor, + final Attribute[] attrs, + final boolean skipDebug) + { + byte[] b = this.b; // the bytecode array + char[] c = new char[maxStringLength]; // buffer used to read strings + int i, j, k; // loop variables + int u, v, w; // indexes in b + Attribute attr; + + // visits the header + u = header; + int access = readUnsignedShort(u); + String className = readClass(u + 2, c); + v = items[readUnsignedShort(u + 4)]; + String superClassName = v == 0 ? null : readUTF8(v, c); + String[] implementedItfs = new String[readUnsignedShort(u + 6)]; + String sourceFile = null; + Attribute clattrs = null; + w = 0; + u += 8; + for (i = 0; i < implementedItfs.length; ++i) { + implementedItfs[i] = readClass(u, c); u += 2; + } + // skips fields and methods + v = u; + i = readUnsignedShort(v); v += 2; + for ( ; i > 0; --i) { + j = readUnsignedShort(v + 6); + v += 8; + for ( ; j > 0; --j) { + v += 6 + readInt(v + 2); + } + } + i = readUnsignedShort(v); v += 2; + for ( ; i > 0; --i) { + j = readUnsignedShort(v + 6); + v += 8; + for ( ; j > 0; --j) { + v += 6 + readInt(v + 2); + } + } + // reads the class's attributes + i = readUnsignedShort(v); v += 2; + for ( ; i > 0; --i) { + String attrName = readUTF8(v, c); + if (attrName.equals("SourceFile")) { + sourceFile = readUTF8(v + 6, c); + } else if (attrName.equals("Deprecated")) { + access |= Constants.ACC_DEPRECATED; + } else if (attrName.equals("InnerClasses")) { + w = v + 6; + } else { + attr = readAttribute( + attrs, attrName, v + 6, readInt(v + 2), c, -1, null); + if (attr != null) { + attr.next = clattrs; + clattrs = attr; + } + } + v += 6 + readInt(v + 2); + } + // calls the visit method + classVisitor.visit( + access, className, superClassName, implementedItfs, sourceFile); + + // visits the inner classes info + if (w != 0) { + i = readUnsignedShort(w); w += 2; + for ( ; i > 0; --i) { + classVisitor.visitInnerClass( + readUnsignedShort(w) == 0 ? null : readClass(w, c), + readUnsignedShort(w + 2) == 0 ? null : readClass(w + 2, c), + readUnsignedShort(w + 4) == 0 ? null : readUTF8(w + 4, c), + readUnsignedShort(w + 6)); + w += 8; + } + } + + // visits the fields + i = readUnsignedShort(u); u += 2; + for ( ; i > 0; --i) { + access = readUnsignedShort(u); + String fieldName = readUTF8(u + 2, c); + String fieldDesc = readUTF8(u + 4, c); + Attribute fattrs = null; + // visits the field's attributes and looks for a ConstantValue attribute + int fieldValueItem = 0; + j = readUnsignedShort(u + 6); + u += 8; + for ( ; j > 0; --j) { + String attrName = readUTF8(u, c); + if (attrName.equals("ConstantValue")) { + fieldValueItem = readUnsignedShort(u + 6); + } else if (attrName.equals("Synthetic")) { + access |= Constants.ACC_SYNTHETIC; + } else if (attrName.equals("Deprecated")) { + access |= Constants.ACC_DEPRECATED; + } else { + attr = readAttribute( + attrs, attrName, u + 6, readInt(u + 2), c, -1, null); + if (attr != null) { + attr.next = fattrs; + fattrs = attr; + } + } + u += 6 + readInt(u + 2); + } + // reads the field's value, if any + Object value = (fieldValueItem == 0 ? null : readConst(fieldValueItem, c)); + // visits the field + classVisitor.visitField(access, fieldName, fieldDesc, value, fattrs); + } + + // visits the methods + i = readUnsignedShort(u); u += 2; + for ( ; i > 0; --i) { + access = readUnsignedShort(u); + String methName = readUTF8(u + 2, c); + String methDesc = readUTF8(u + 4, c); + Attribute mattrs = null; + Attribute cattrs = null; + v = 0; + w = 0; + // looks for Code and Exceptions attributes + j = readUnsignedShort(u + 6); + u += 8; + for ( ; j > 0; --j) { + String attrName = readUTF8(u, c); u += 2; + int attrSize = readInt(u); u += 4; + if (attrName.equals("Code")) { + v = u; + } else if (attrName.equals("Exceptions")) { + w = u; + } else if (attrName.equals("Synthetic")) { + access |= Constants.ACC_SYNTHETIC; + } else if (attrName.equals("Deprecated")) { + access |= Constants.ACC_DEPRECATED; + } else { + attr = readAttribute(attrs, attrName, u, attrSize, c, -1, null); + if (attr != null) { + attr.next = mattrs; + mattrs = attr; + } + } + u += attrSize; + } + // reads declared exceptions + String[] exceptions; + if (w == 0) { + exceptions = null; + } else { + exceptions = new String[readUnsignedShort(w)]; w += 2; + for (j = 0; j < exceptions.length; ++j) { + exceptions[j] = readClass(w, c); w += 2; + } + } + + // visits the method's code, if any + CodeVisitor cv; + cv = classVisitor.visitMethod( + access, methName, methDesc, exceptions, mattrs); + if (cv != null && v != 0) { + int maxStack = readUnsignedShort(v); + int maxLocals = readUnsignedShort(v + 2); + int codeLength = readInt(v + 4); + v += 8; + + int codeStart = v; + int codeEnd = v + codeLength; + + // 1st phase: finds the labels + int label; + Label[] labels = new Label[codeLength + 1]; + while (v < codeEnd) { + int opcode = b[v] & 0xFF; + switch (ClassWriter.TYPE[opcode]) { + case ClassWriter.NOARG_INSN: + case ClassWriter.IMPLVAR_INSN: + v += 1; + break; + case ClassWriter.LABEL_INSN: + label = v - codeStart + readShort(v + 1); + if (labels[label] == null) { + labels[label] = new Label(); + } + v += 3; + break; + case ClassWriter.LABELW_INSN: + label = v - codeStart + readInt(v + 1); + if (labels[label] == null) { + labels[label] = new Label(); + } + v += 5; + break; + case ClassWriter.WIDE_INSN: + opcode = b[v + 1] & 0xFF; + if (opcode == Constants.IINC) { + v += 6; + } else { + v += 4; + } + break; + case ClassWriter.TABL_INSN: + // skips 0 to 3 padding bytes + w = v - codeStart; + v = v + 4 - (w & 3); + // reads instruction + label = w + readInt(v); v += 4; + if (labels[label] == null) { + labels[label] = new Label(); + } + j = readInt(v); v += 4; + j = readInt(v) - j + 1; v += 4; + for ( ; j > 0; --j) { + label = w + readInt(v); v += 4; + if (labels[label] == null) { + labels[label] = new Label(); + } + } + break; + case ClassWriter.LOOK_INSN: + // skips 0 to 3 padding bytes + w = v - codeStart; + v = v + 4 - (w & 3); + // reads instruction + label = w + readInt(v); v += 4; + if (labels[label] == null) { + labels[label] = new Label(); + } + j = readInt(v); v += 4; + for ( ; j > 0; --j) { + v += 4; // skips key + label = w + readInt(v); v += 4; + if (labels[label] == null) { + labels[label] = new Label(); + } + } + break; + case ClassWriter.VAR_INSN: + case ClassWriter.SBYTE_INSN: + case ClassWriter.LDC_INSN: + v += 2; + break; + case ClassWriter.SHORT_INSN: + case ClassWriter.LDCW_INSN: + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.TYPE_INSN: + case ClassWriter.IINC_INSN: + v += 3; + break; + case ClassWriter.ITFMETH_INSN: + v += 5; + break; + // case MANA_INSN: + default: + v += 4; + break; + } + } + // parses the try catch entries + j = readUnsignedShort(v); v += 2; + for ( ; j > 0; --j) { + label = readUnsignedShort(v); + if (labels[label] == null) { + labels[label] = new Label(); + } + label = readUnsignedShort(v + 2); + if (labels[label] == null) { + labels[label] = new Label(); + } + label = readUnsignedShort(v + 4); + if (labels[label] == null) { + labels[label] = new Label(); + } + v += 8; + } + // parses the local variable, line number tables, and code attributes + j = readUnsignedShort(v); v += 2; + for ( ; j > 0; --j) { + String attrName = readUTF8(v, c); + if (attrName.equals("LocalVariableTable")) { + if (!skipDebug) { + k = readUnsignedShort(v + 6); + w = v + 8; + for ( ; k > 0; --k) { + label = readUnsignedShort(w); + if (labels[label] == null) { + labels[label] = new Label(); + } + label += readUnsignedShort(w + 2); + if (labels[label] == null) { + labels[label] = new Label(); + } + w += 10; + } + } + } else if (attrName.equals("LineNumberTable")) { + if (!skipDebug) { + k = readUnsignedShort(v + 6); + w = v + 8; + for ( ; k > 0; --k) { + label = readUnsignedShort(w); + if (labels[label] == null) { + labels[label] = new Label(); + } + w += 4; + } + } + } else { + for (k = 0; k < attrs.length; ++k) { + if (attrs[k].type.equals(attrName)) { + attr = attrs[k].read( + this, v + 6, readInt(v + 2), c, codeStart - 8, labels); + if (attr != null) { + attr.next = cattrs; + cattrs = attr; + } + } + } + } + v += 6 + readInt(v + 2); + } + + // 2nd phase: visits each instruction + v = codeStart; + Label l; + while (v < codeEnd) { + w = v - codeStart; + l = labels[w]; + if (l != null) { + cv.visitLabel(l); + } + int opcode = b[v] & 0xFF; + switch (ClassWriter.TYPE[opcode]) { + case ClassWriter.NOARG_INSN: + cv.visitInsn(opcode); + v += 1; + break; + case ClassWriter.IMPLVAR_INSN: + if (opcode > Constants.ISTORE) { + opcode -= 59; //ISTORE_0 + cv.visitVarInsn(Constants.ISTORE + (opcode >> 2), opcode & 0x3); + } else { + opcode -= 26; //ILOAD_0 + cv.visitVarInsn(Constants.ILOAD + (opcode >> 2), opcode & 0x3); + } + v += 1; + break; + case ClassWriter.LABEL_INSN: + cv.visitJumpInsn(opcode, labels[w + readShort(v + 1)]); + v += 3; + break; + case ClassWriter.LABELW_INSN: + cv.visitJumpInsn(opcode, labels[w + readInt(v + 1)]); + v += 5; + break; + case ClassWriter.WIDE_INSN: + opcode = b[v + 1] & 0xFF; + if (opcode == Constants.IINC) { + cv.visitIincInsn(readUnsignedShort(v + 2), readShort(v + 4)); + v += 6; + } else { + cv.visitVarInsn(opcode, readUnsignedShort(v + 2)); + v += 4; + } + break; + case ClassWriter.TABL_INSN: + // skips 0 to 3 padding bytes + v = v + 4 - (w & 3); + // reads instruction + label = w + readInt(v); v += 4; + int min = readInt(v); v += 4; + int max = readInt(v); v += 4; + Label[] table = new Label[max - min + 1]; + for (j = 0; j < table.length; ++j) { + table[j] = labels[w + readInt(v)]; + v += 4; + } + cv.visitTableSwitchInsn(min, max, labels[label], table); + break; + case ClassWriter.LOOK_INSN: + // skips 0 to 3 padding bytes + v = v + 4 - (w & 3); + // reads instruction + label = w + readInt(v); v += 4; + j = readInt(v); v += 4; + int[] keys = new int[j]; + Label[] values = new Label[j]; + for (j = 0; j < keys.length; ++j) { + keys[j] = readInt(v); v += 4; + values[j] = labels[w + readInt(v)]; v += 4; + } + cv.visitLookupSwitchInsn(labels[label], keys, values); + break; + case ClassWriter.VAR_INSN: + cv.visitVarInsn(opcode, b[v + 1] & 0xFF); + v += 2; + break; + case ClassWriter.SBYTE_INSN: + cv.visitIntInsn(opcode, b[v + 1]); + v += 2; + break; + case ClassWriter.SHORT_INSN: + cv.visitIntInsn(opcode, readShort(v + 1)); + v += 3; + break; + case ClassWriter.LDC_INSN: + cv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c)); + v += 2; + break; + case ClassWriter.LDCW_INSN: + cv.visitLdcInsn(readConst(readUnsignedShort(v + 1), c)); + v += 3; + break; + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.ITFMETH_INSN: + int cpIndex = items[readUnsignedShort(v + 1)]; + String iowner = readClass(cpIndex, c); + cpIndex = items[readUnsignedShort(cpIndex + 2)]; + String iname = readUTF8(cpIndex, c); + String idesc = readUTF8(cpIndex + 2, c); + if (opcode < Constants.INVOKEVIRTUAL) { + cv.visitFieldInsn(opcode, iowner, iname, idesc); + } else { + cv.visitMethodInsn(opcode, iowner, iname, idesc); + } + if (opcode == Constants.INVOKEINTERFACE) { + v += 5; + } else { + v += 3; + } + break; + case ClassWriter.TYPE_INSN: + cv.visitTypeInsn(opcode, readClass(v + 1, c)); + v += 3; + break; + case ClassWriter.IINC_INSN: + cv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]); + v += 3; + break; + // case MANA_INSN: + default: + cv.visitMultiANewArrayInsn(readClass(v + 1, c), b[v + 3] & 0xFF); + v += 4; + break; + } + } + l = labels[codeEnd - codeStart]; + if (l != null) { + cv.visitLabel(l); + } + // visits the try catch entries + j = readUnsignedShort(v); v += 2; + for ( ; j > 0; --j) { + Label start = labels[readUnsignedShort(v)]; + Label end = labels[readUnsignedShort(v + 2)]; + Label handler = labels[readUnsignedShort(v + 4)]; + int type = readUnsignedShort(v + 6); + if (type == 0) { + cv.visitTryCatchBlock(start, end, handler, null); + } else { + cv.visitTryCatchBlock(start, end, handler, readUTF8(items[type], c)); + } + v += 8; + } + // visits the local variable and line number tables + j = readUnsignedShort(v); v += 2; + if (!skipDebug) { + for ( ; j > 0; --j) { + String attrName = readUTF8(v, c); + if (attrName.equals("LocalVariableTable")) { + k = readUnsignedShort(v + 6); + w = v + 8; + for ( ; k > 0; --k) { + label = readUnsignedShort(w); + Label start = labels[label]; + label += readUnsignedShort(w + 2); + Label end = labels[label]; + cv.visitLocalVariable( + readUTF8(w + 4, c), + readUTF8(w + 6, c), + start, + end, + readUnsignedShort(w + 8)); + w += 10; + } + } else if (attrName.equals("LineNumberTable")) { + k = readUnsignedShort(v + 6); + w = v + 8; + for ( ; k > 0; --k) { + cv.visitLineNumber( + readUnsignedShort(w + 2), + labels[readUnsignedShort(w)]); + w += 4; + } + } + v += 6 + readInt(v + 2); + } + } + // visits the other attributes + while (cattrs != null) { + attr = cattrs.next; + cattrs.next = null; + cv.visitAttribute(cattrs); + cattrs = attr; + } + // visits the max stack and max locals values + cv.visitMaxs(maxStack, maxLocals); + } + } + // visits the class attributes + while (clattrs != null) { + classVisitor.visitAttribute(clattrs); + clattrs = clattrs.next; + } + // visits the end of the class + classVisitor.visitEnd(); + } + + // -------------------------------------------------------------------------- + // Utility methods: low level parsing + // -------------------------------------------------------------------------- + + /** + * Reads a byte value in {@link #b b}. This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + + public int readByte (final int index) { + return b[index] & 0xFF; + } + + /** + * Reads an unsigned short value in {@link #b b}. This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + + public int readUnsignedShort (final int index) { + byte[] b = this.b; + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); + } + + /** + * Reads a signed short value in {@link #b b}. This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + + public short readShort (final int index) { + byte[] b = this.b; + return (short)(((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); + } + + /** + * Reads a signed int value in {@link #b b}. This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + + public int readInt (final int index) { + byte[] b = this.b; + return ((b[index] & 0xFF) << 24) | + ((b[index + 1] & 0xFF) << 16) | + ((b[index + 2] & 0xFF) << 8) | + (b[index + 3] & 0xFF); + } + + /** + * Reads a signed long value in {@link #b b}. This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + + public long readLong (final int index) { + long l1 = readInt(index); + long l0 = readInt(index + 4) & 0xFFFFFFFFL; + return (l1 << 32) | l0; + } + + /** + * Reads an UTF8 string constant pool item in {@link #b b}. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param index the start index of an unsigned short value in {@link #b b}, + * whose value is the index of an UTF8 constant pool item. + * @param buf buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified UTF8 item. + */ + + public String readUTF8 (int index, final char[] buf) { + // consults cache + int item = readUnsignedShort(index); + String s = strings[item]; + if (s != null) { + return s; + } + // computes the start index of the CONSTANT_Utf8 item in b + index = items[item]; + // reads the length of the string (in bytes, not characters) + int utfLen = readUnsignedShort(index); + index += 2; + // parses the string bytes + int endIndex = index + utfLen; + byte[] b = this.b; + int strLen = 0; + int c, d, e; + while (index < endIndex) { + c = b[index++] & 0xFF; + switch (c >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + buf[strLen++] = (char)c; + break; + case 12: + case 13: + // 110x xxxx 10xx xxxx + d = b[index++]; + buf[strLen++] = (char)(((c & 0x1F) << 6) | (d & 0x3F)); + break; + default: + // 1110 xxxx 10xx xxxx 10xx xxxx + d = b[index++]; + e = b[index++]; + buf[strLen++] = + (char)(((c & 0x0F) << 12) | ((d & 0x3F) << 6) | (e & 0x3F)); + break; + } + } + s = new String(buf, 0, strLen); + strings[item] = s; + return s; + } + + /** + * Reads a class constant pool item in {@link #b b}. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param index the start index of an unsigned short value in {@link #b b}, + * whose value is the index of a class constant pool item. + * @param buf buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified class item. + */ + + public String readClass (final int index, final char[] buf) { + // computes the start index of the CONSTANT_Class item in b + // and reads the CONSTANT_Utf8 item designated by + // the first two bytes of this CONSTANT_Class item + return readUTF8(items[readUnsignedShort(index)], buf); + } + + /** + * Reads a numeric or string constant pool item in {@link #b b}. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param item the index of a constant pool item. + * @param buf buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the {@link java.lang.Integer Integer}, {@link java.lang.Float + * Float}, {@link java.lang.Long Long}, {@link java.lang.Double Double} + * or {@link String String} corresponding to the given constant pool + * item. + */ + + public Object readConst (final int item, final char[] buf) { + int index = items[item]; + switch (b[index - 1]) { + case ClassWriter.INT: + return new Integer(readInt(index)); + case ClassWriter.FLOAT: + return new Float(Float.intBitsToFloat(readInt(index))); + case ClassWriter.LONG: + return new Long(readLong(index)); + case ClassWriter.DOUBLE: + return new Double(Double.longBitsToDouble(readLong(index))); + //case ClassWriter.STR: + default: + return readUTF8(index, buf); + } + } + + /** + * Reads an attribute in {@link #b b}. The default implementation of this + * method returns null for all attributes. + * + * @param attrs prototypes of the attributes that must be parsed during the + * visit of the class. Any attribute whose type is not equal to the type + * of one the prototypes will be ignored. + * @param type the type of the attribute. + * @param off index of the first byte of the attribute's content in {@link #b + * b}. The 6 attribute header bytes, containing the type and the length + * of the attribute, are not taken into account here (they have already + * been read). + * @param len the length of the attribute's content. + * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, {@link + * #readClass readClass} or {@link #readConst readConst}. + * @param codeOff index of the first byte of code's attribute content in + * {@link #b b}, or -1 if the attribute to be read is not a code + * attribute. The 6 attribute header bytes, containing the type and the + * length of the attribute, are not taken into account here. + * @param labels the labels of the method's code, or null if the + * attribute to be read is not a code attribute. + * @return the attribute that has been read, or null to skip this + * attribute. + */ + + protected Attribute readAttribute ( + final Attribute[] attrs, + final String type, + final int off, + final int len, + final char[] buf, + final int codeOff, + final Label[] labels) + { + for (int i = 0; i < attrs.length; ++i) { + if (attrs[i].type.equals(type)) { + return attrs[i].read(this, off, len, buf, codeOff, labels); + } + } + return null; + } +} diff --git a/org/objectweb/asm/ClassVisitor.java b/org/objectweb/asm/ClassVisitor.java new file mode 100644 index 0000000000..b837a341cd --- /dev/null +++ b/org/objectweb/asm/ClassVisitor.java @@ -0,0 +1,158 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +/** + * A visitor to visit a Java class. The methods of this interface must be called + * in the following order: visit (visitField | + * visitMethod | visitInnerClass | visitAttribute)* + * visitEnd. + */ + +public interface ClassVisitor { + + /** + * Visits the header of the class. + * + * @param access the class's access flags (see {@link Constants}). This + * parameter also indicates if the class is deprecated. + * @param name the internal name of the class (see {@link Type#getInternalName + * getInternalName}). + * @param superName the internal of name of the super class (see {@link + * Type#getInternalName getInternalName}). For interfaces, the super + * class is {@link Object}. May be null, but only for the {@link + * Object java.lang.Object} class. + * @param interfaces the internal names of the class's interfaces (see {@link + * Type#getInternalName getInternalName}). May be null. + * @param sourceFile the name of the source file from which this class was + * compiled. May be null. + */ + + void visit ( + int access, + String name, + String superName, + String[] interfaces, + String sourceFile); + + /** + * Visits information about an inner class. This inner class is not + * necessarily a member of the class being visited. + * + * @param name the internal name of an inner class (see {@link + * Type#getInternalName getInternalName}). + * @param outerName the internal name of the class to which the inner class + * belongs (see {@link Type#getInternalName getInternalName}). May be + * null. + * @param innerName the (simple) name of the inner class inside its enclosing + * class. May be null for anonymous inner classes. + * @param access the access flags of the inner class as originally declared + * in the enclosing class. + */ + + void visitInnerClass ( + String name, + String outerName, + String innerName, + int access); + + /** + * Visits a field of the class. + * + * @param access the field's access flags (see {@link Constants}). This + * parameter also indicates if the field is synthetic and/or deprecated. + * @param name the field's name. + * @param desc the field's descriptor (see {@link Type Type}). + * @param value the field's initial value. This parameter, which may be + * null if the field does not have an initial value, must be an + * {@link java.lang.Integer Integer}, a {@link java.lang.Float Float}, a + * {@link java.lang.Long Long}, a {@link java.lang.Double Double} or a + * {@link String String}. This parameter is only used for static + * fields. Its value is ignored for non static fields, which must be + * initialized through bytecode instructions in constructors or methods. + * @param attrs the non standard method attributes, linked together by their + * next field. May be null. + */ + + void visitField ( + int access, + String name, + String desc, + Object value, + Attribute attrs); + + /** + * Visits a method of the class. This method must return a new + * {@link CodeVisitor CodeVisitor} instance (or null) each time it + * is called, i.e., it should not return a previously returned visitor. + * + * @param access the method's access flags (see {@link Constants}). This + * parameter also indicates if the method is synthetic and/or deprecated. + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type Type}). + * @param exceptions the internal names of the method's exception + * classes (see {@link Type#getInternalName getInternalName}). May be + * null. + * @param attrs the non standard method attributes, linked together by their + * next field. May be null. + * @return an object to visit the byte code of the method, or null if + * this class visitor is not interested in visiting the code of this + * method. + */ + + CodeVisitor visitMethod ( + int access, + String name, + String desc, + String[] exceptions, + Attribute attrs); + + /** + * Visits a non standard attribute of the class. This method must visit only + * the first attribute in the given attribute list. + * + * @param attr a non standard class attribute. Must not be null. + */ + + void visitAttribute (Attribute attr); + + /** + * Visits the end of the class. This method, which is the last one to be + * called, is used to inform the visitor that all the fields and methods of + * the class have been visited. + */ + + void visitEnd (); +} diff --git a/org/objectweb/asm/ClassWriter.java b/org/objectweb/asm/ClassWriter.java new file mode 100644 index 0000000000..98334ec7e9 --- /dev/null +++ b/org/objectweb/asm/ClassWriter.java @@ -0,0 +1,1033 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +/** + * A {@link ClassVisitor ClassVisitor} that generates Java class files. More + * precisely this visitor generates a byte array conforming to the Java class + * file format. It can be used alone, to generate a Java class "from scratch", + * or with one or more {@link ClassReader ClassReader} and adapter class + * visitor to generate a modified class from one or more existing Java classes. + */ + +public class ClassWriter implements ClassVisitor { + + /** + * The type of CONSTANT_Class constant pool items. + */ + + final static int CLASS = 7; + + /** + * The type of CONSTANT_Fieldref constant pool items. + */ + + final static int FIELD = 9; + + /** + * The type of CONSTANT_Methodref constant pool items. + */ + + final static int METH = 10; + + /** + * The type of CONSTANT_InterfaceMethodref constant pool items. + */ + + final static int IMETH = 11; + + /** + * The type of CONSTANT_String constant pool items. + */ + + final static int STR = 8; + + /** + * The type of CONSTANT_Integer constant pool items. + */ + + final static int INT = 3; + + /** + * The type of CONSTANT_Float constant pool items. + */ + + final static int FLOAT = 4; + + /** + * The type of CONSTANT_Long constant pool items. + */ + + final static int LONG = 5; + + /** + * The type of CONSTANT_Double constant pool items. + */ + + final static int DOUBLE = 6; + + /** + * The type of CONSTANT_NameAndType constant pool items. + */ + + final static int NAME_TYPE = 12; + + /** + * The type of CONSTANT_Utf8 constant pool items. + */ + + final static int UTF8 = 1; + + /** + * Minor and major version numbers of the class to be generated. + */ + + private int version; + + /** + * Index of the next item to be added in the constant pool. + */ + + private short index; + + /** + * The constant pool of this class. + */ + + private ByteVector pool; + + /** + * The constant pool's hash table data. + */ + + private Item[] items; + + /** + * The threshold of the constant pool's hash table. + */ + + private int threshold; + + /** + * The access flags of this class. + */ + + private int access; + + /** + * The constant pool item that contains the internal name of this class. + */ + + private int name; + + /** + * The constant pool item that contains the internal name of the super class + * of this class. + */ + + private int superName; + + /** + * Number of interfaces implemented or extended by this class or interface. + */ + + private int interfaceCount; + + /** + * The interfaces implemented or extended by this class or interface. More + * precisely, this array contains the indexes of the constant pool items + * that contain the internal names of these interfaces. + */ + + private int[] interfaces; + + /** + * The index of the constant pool item that contains the name of the source + * file from which this class was compiled. + */ + + private int sourceFile; + + /** + * Number of fields of this class. + */ + + private int fieldCount; + + /** + * The fields of this class. + */ + + private ByteVector fields; + + /** + * true if the maximum stack size and number of local variables must + * be automatically computed. + */ + + private boolean computeMaxs; + + /** + * The methods of this class. These methods are stored in a linked list of + * {@link CodeWriter CodeWriter} objects, linked to each other by their {@link + * CodeWriter#next} field. This field stores the first element of this list. + */ + + CodeWriter firstMethod; + + /** + * The methods of this class. These methods are stored in a linked list of + * {@link CodeWriter CodeWriter} objects, linked to each other by their {@link + * CodeWriter#next} field. This field stores the last element of this list. + */ + + CodeWriter lastMethod; + + /** + * The number of entries in the InnerClasses attribute. + */ + + private int innerClassesCount; + + /** + * The InnerClasses attribute. + */ + + private ByteVector innerClasses; + + /** + * The non standard attributes of the class. + */ + + private Attribute attrs; + + /** + * A reusable key used to look for items in the hash {@link #items items}. + */ + + Item key; + + /** + * A reusable key used to look for items in the hash {@link #items items}. + */ + + Item key2; + + /** + * A reusable key used to look for items in the hash {@link #items items}. + */ + + Item key3; + + /** + * The type of instructions without any label. + */ + + final static int NOARG_INSN = 0; + + /** + * The type of instructions with an signed byte label. + */ + + final static int SBYTE_INSN = 1; + + /** + * The type of instructions with an signed short label. + */ + + final static int SHORT_INSN = 2; + + /** + * The type of instructions with a local variable index label. + */ + + final static int VAR_INSN = 3; + + /** + * The type of instructions with an implicit local variable index label. + */ + + final static int IMPLVAR_INSN = 4; + + /** + * The type of instructions with a type descriptor argument. + */ + + final static int TYPE_INSN = 5; + + /** + * The type of field and method invocations instructions. + */ + + final static int FIELDORMETH_INSN = 6; + + /** + * The type of the INVOKEINTERFACE instruction. + */ + + final static int ITFMETH_INSN = 7; + + /** + * The type of instructions with a 2 bytes bytecode offset label. + */ + + final static int LABEL_INSN = 8; + + /** + * The type of instructions with a 4 bytes bytecode offset label. + */ + + final static int LABELW_INSN = 9; + + /** + * The type of the LDC instruction. + */ + + final static int LDC_INSN = 10; + + /** + * The type of the LDC_W and LDC2_W instructions. + */ + + final static int LDCW_INSN = 11; + + /** + * The type of the IINC instruction. + */ + + final static int IINC_INSN = 12; + + /** + * The type of the TABLESWITCH instruction. + */ + + final static int TABL_INSN = 13; + + /** + * The type of the LOOKUPSWITCH instruction. + */ + + final static int LOOK_INSN = 14; + + /** + * The type of the MULTIANEWARRAY instruction. + */ + + final static int MANA_INSN = 15; + + /** + * The type of the WIDE instruction. + */ + + final static int WIDE_INSN = 16; + + /** + * The instruction types of all JVM opcodes. + */ + + static byte[] TYPE; + + // -------------------------------------------------------------------------- + // Static initializer + // -------------------------------------------------------------------------- + + /** + * Computes the instruction types of JVM opcodes. + */ + + static { + int i; + byte[] b = new byte[220]; + String s = + "AAAAAAAAAAAAAAAABCKLLDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADDDDDEEEEEEEEE" + + "EEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAA" + + "AAAAAAAAAAAAAAAAAIIIIIIIIIIIIIIIIDNOAAAAAAGGGGGGGHAFBFAAFFAAQPIIJJII" + + "IIIIIIIIIIIIIIII"; + for (i = 0; i < b.length; ++i) { + b[i] = (byte)(s.charAt(i) - 'A'); + } + TYPE = b; + + /* code to generate the above string + + // SBYTE_INSN instructions + b[Constants.NEWARRAY] = SBYTE_INSN; + b[Constants.BIPUSH] = SBYTE_INSN; + + // SHORT_INSN instructions + b[Constants.SIPUSH] = SHORT_INSN; + + // (IMPL)VAR_INSN instructions + b[Constants.RET] = VAR_INSN; + for (i = Constants.ILOAD; i <= Constants.ALOAD; ++i) { + b[i] = VAR_INSN; + } + for (i = Constants.ISTORE; i <= Constants.ASTORE; ++i) { + b[i] = VAR_INSN; + } + for (i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3 + b[i] = IMPLVAR_INSN; + } + for (i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3 + b[i] = IMPLVAR_INSN; + } + + // TYPE_INSN instructions + b[Constants.NEW] = TYPE_INSN; + b[Constants.ANEWARRAY] = TYPE_INSN; + b[Constants.CHECKCAST] = TYPE_INSN; + b[Constants.INSTANCEOF] = TYPE_INSN; + + // (Set)FIELDORMETH_INSN instructions + for (i = Constants.GETSTATIC; i <= Constants.INVOKESTATIC; ++i) { + b[i] = FIELDORMETH_INSN; + } + b[Constants.INVOKEINTERFACE] = ITFMETH_INSN; + + // LABEL(W)_INSN instructions + for (i = Constants.IFEQ; i <= Constants.JSR; ++i) { + b[i] = LABEL_INSN; + } + b[Constants.IFNULL] = LABEL_INSN; + b[Constants.IFNONNULL] = LABEL_INSN; + b[200] = LABELW_INSN; // GOTO_W + b[201] = LABELW_INSN; // JSR_W + // temporary opcodes used internally by ASM - see Label and CodeWriter + for (i = 202; i < 220; ++i) { + b[i] = LABEL_INSN; + } + + // LDC(_W) instructions + b[Constants.LDC] = LDC_INSN; + b[19] = LDCW_INSN; // LDC_W + b[20] = LDCW_INSN; // LDC2_W + + // special instructions + b[Constants.IINC] = IINC_INSN; + b[Constants.TABLESWITCH] = TABL_INSN; + b[Constants.LOOKUPSWITCH] = LOOK_INSN; + b[Constants.MULTIANEWARRAY] = MANA_INSN; + b[196] = WIDE_INSN; // WIDE + + for (i = 0; i < b.length; ++i) { + System.err.print((char)('A' + b[i])); + } + System.err.println(); + */ + } + + // -------------------------------------------------------------------------- + // Constructor + // -------------------------------------------------------------------------- + + /** + * Constructs a new {@link ClassWriter ClassWriter} object. + * + * @param computeMaxs true if the maximum stack size and the maximum + * number of local variables must be automatically computed. If this flag + * is true, then the arguments of the {@link + * CodeVisitor#visitMaxs visitMaxs} method of the {@link CodeVisitor + * CodeVisitor} returned by the {@link #visitMethod visitMethod} method + * will be ignored, and computed automatically from the signature and + * the bytecode of each method. + */ + + public ClassWriter (final boolean computeMaxs) { + this(computeMaxs, 45, 3); + } + + /** + * Constructs a new {@link ClassWriter ClassWriter} object. + * + * @param computeMaxs true if the maximum stack size and the maximum + * number of local variables must be automatically computed. If this flag + * is true, then the arguments of the {@link + * CodeVisitor#visitMaxs visitMaxs} method of the {@link CodeVisitor + * CodeVisitor} returned by the {@link #visitMethod visitMethod} method + * will be ignored, and computed automatically from the signature and + * the bytecode of each method. + * @param major the major version of the class to be generated. + * @param minor the minor version of the class to be generated. + */ + + public ClassWriter (final boolean computeMaxs, final int major, final int minor) { + index = 1; + pool = new ByteVector(); + items = new Item[64]; + threshold = (int)(0.75d*items.length); + key = new Item(); + key2 = new Item(); + key3 = new Item(); + this.computeMaxs = computeMaxs; + this.version = minor << 16 | major; + } + + // -------------------------------------------------------------------------- + // Implementation of the ClassVisitor interface + // -------------------------------------------------------------------------- + + public void visit ( + final int access, + final String name, + final String superName, + final String[] interfaces, + final String sourceFile) + { + this.access = access; + this.name = newClass(name); + this.superName = superName == null ? 0 : newClass(superName); + if (interfaces != null && interfaces.length > 0) { + interfaceCount = interfaces.length; + this.interfaces = new int[interfaceCount]; + for (int i = 0; i < interfaceCount; ++i) { + this.interfaces[i] = newClass(interfaces[i]); + } + } + if (sourceFile != null) { + newUTF8("SourceFile"); + this.sourceFile = newUTF8(sourceFile); + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + newUTF8("Deprecated"); + } + } + + public void visitInnerClass ( + final String name, + final String outerName, + final String innerName, + final int access) + { + if (innerClasses == null) { + newUTF8("InnerClasses"); + innerClasses = new ByteVector(); + } + ++innerClassesCount; + innerClasses.putShort(name == null ? 0 : newClass(name)); + innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); + innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); + innerClasses.putShort(access); + } + + public void visitField ( + final int access, + final String name, + final String desc, + final Object value, + final Attribute attrs) + { + ++fieldCount; + if (fields == null) { + fields = new ByteVector(); + } + fields.putShort(access).putShort(newUTF8(name)).putShort(newUTF8(desc)); + int attributeCount = 0; + if (value != null) { + ++attributeCount; + } + if ((access & Constants.ACC_SYNTHETIC) != 0) { + ++attributeCount; + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + ++attributeCount; + } + if (attrs != null) { + attributeCount += attrs.getCount(); + } + fields.putShort(attributeCount); + if (value != null) { + fields.putShort(newUTF8("ConstantValue")); + fields.putInt(2).putShort(newCst(value).index); + } + if ((access & Constants.ACC_SYNTHETIC) != 0) { + fields.putShort(newUTF8("Synthetic")).putInt(0); + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + fields.putShort(newUTF8("Deprecated")).putInt(0); + } + if (attrs != null) { + attrs.getSize(this, null, 0, -1, -1); + attrs.put(this, null, 0, -1, -1, fields); + } + } + + public CodeVisitor visitMethod ( + final int access, + final String name, + final String desc, + final String[] exceptions, + final Attribute attrs) + { + CodeWriter cw = new CodeWriter(this, computeMaxs); + cw.init(access, name, desc, exceptions, attrs); + return cw; + } + + public void visitAttribute (final Attribute attr) { + attr.next = attrs; + attrs = attr; + } + + public void visitEnd () { + } + + // -------------------------------------------------------------------------- + // Other public methods + // -------------------------------------------------------------------------- + + /** + * Returns the bytecode of the class that was build with this class writer. + * + * @return the bytecode of the class that was build with this class writer. + */ + + public byte[] toByteArray () { + // computes the real size of the bytecode of this class + int size = 24 + 2*interfaceCount; + if (fields != null) { + size += fields.length; + } + int nbMethods = 0; + CodeWriter cb = firstMethod; + while (cb != null) { + ++nbMethods; + size += cb.getSize(); + cb = cb.next; + } + int attributeCount = 0; + if (sourceFile != 0) { + ++attributeCount; + size += 8; + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + ++attributeCount; + size += 6; + } + if (innerClasses != null) { + ++attributeCount; + size += 8 + innerClasses.length; + } + if (attrs != null) { + attributeCount += attrs.getCount(); + size += attrs.getSize(this, null, 0, -1, -1); + } + size += pool.length; + // allocates a byte vector of this size, in order to avoid unnecessary + // arraycopy operations in the ByteVector.enlarge() method + ByteVector out = new ByteVector(size); + out.putInt(0xCAFEBABE).putInt(version); + out.putShort(index).putByteArray(pool.data, 0, pool.length); + out.putShort(access).putShort(name).putShort(superName); + out.putShort(interfaceCount); + for (int i = 0; i < interfaceCount; ++i) { + out.putShort(interfaces[i]); + } + out.putShort(fieldCount); + if (fields != null) { + out.putByteArray(fields.data, 0, fields.length); + } + out.putShort(nbMethods); + cb = firstMethod; + while (cb != null) { + cb.put(out); + cb = cb.next; + } + out.putShort(attributeCount); + if (sourceFile != 0) { + out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + out.putShort(newUTF8("Deprecated")).putInt(0); + } + if (innerClasses != null) { + out.putShort(newUTF8("InnerClasses")); + out.putInt(innerClasses.length + 2).putShort(innerClassesCount); + out.putByteArray(innerClasses.data, 0, innerClasses.length); + } + if (attrs != null) { + attrs.put(this, null, 0, -1, -1, out); + } + return out.data; + } + + // -------------------------------------------------------------------------- + // Utility methods: constant pool management + // -------------------------------------------------------------------------- + + /** + * Adds a number or string constant to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * + * @param cst the value of the constant to be added to the constant pool. This + * parameter must be an {@link java.lang.Integer Integer}, a {@link + * java.lang.Float Float}, a {@link java.lang.Long Long}, a {@link + java.lang.Double Double} or a {@link String String}. + * @return a new or already existing constant item with the given value. + */ + + Item newCst (final Object cst) { + if (cst instanceof Integer) { + int val = ((Integer)cst).intValue(); + return newInteger(val); + } else if (cst instanceof Float) { + float val = ((Float)cst).floatValue(); + return newFloat(val); + } else if (cst instanceof Long) { + long val = ((Long)cst).longValue(); + return newLong(val); + } else if (cst instanceof Double) { + double val = ((Double)cst).doubleValue(); + return newDouble(val); + } else if (cst instanceof String) { + return newString((String)cst); + } else { + throw new IllegalArgumentException("value " + cst); + } + } + + /** + * Adds a number or string constant to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param cst the value of the constant to be added to the constant pool. This + * parameter must be an {@link java.lang.Integer Integer}, a {@link + * java.lang.Float Float}, a {@link java.lang.Long Long}, a {@link + java.lang.Double Double} or a {@link String String}. + * @return the index of a new or already existing constant item with the given + * value. + */ + + public int newConst (final Object cst) { + return newCst(cst).index; + } + + /** + * Adds an UTF8 string to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param value the String value. + * @return the index of a new or already existing UTF8 item. + */ + + public int newUTF8 (final String value) { + key.set(UTF8, value, null, null); + Item result = get(key); + if (result == null) { + pool.putByte(UTF8).putUTF8(value); + result = new Item(index++, key); + put(result); + } + return result.index; + } + + /** + * Adds a class reference to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param value the internal name of the class. + * @return the index of a new or already existing class reference item. + */ + + public int newClass (final String value) { + key2.set(CLASS, value, null, null); + Item result = get(key2); + if (result == null) { + pool.put12(CLASS, newUTF8(value)); + result = new Item(index++, key2); + put(result); + } + return result.index; + } + + /** + * Adds a field reference to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param owner the internal name of the field's owner class. + * @param name the field's name. + * @param desc the field's descriptor. + * @return the index of a new or already existing field reference item. + */ + + public int newField ( + final String owner, + final String name, + final String desc) + { + key3.set(FIELD, owner, name, desc); + Item result = get(key3); + if (result == null) { + put122(FIELD, newClass(owner), newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result.index; + } + + /** + * Adds a method reference to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. + * + * @param owner the internal name of the method's owner class. + * @param name the method's name. + * @param desc the method's descriptor. + * @param itf true if owner is an interface. + * @return a new or already existing method reference item. + */ + + Item newMethodItem ( + final String owner, + final String name, + final String desc, + final boolean itf) + { + key3.set(itf ? IMETH : METH, owner, name, desc); + Item result = get(key3); + if (result == null) { + put122(itf ? IMETH : METH, newClass(owner), newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result; + } + + /** + * Adds a method reference to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param owner the internal name of the method's owner class. + * @param name the method's name. + * @param desc the method's descriptor. + * @param itf true if owner is an interface. + * @return the index of a new or already existing method reference item. + */ + + public int newMethod ( + final String owner, + final String name, + final String desc, + final boolean itf) + { + return newMethodItem(owner, name, desc, itf).index; + } + + /** + * Adds an integer to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value the int value. + * @return a new or already existing int item. + */ + + private Item newInteger (final int value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(INT).putInt(value); + result = new Item(index++, key); + put(result); + } + return result; + } + + /** + * Adds a float to the constant pool of the class being build. Does nothing if + * the constant pool already contains a similar item. + * + * @param value the float value. + * @return a new or already existing float item. + */ + + private Item newFloat (final float value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(FLOAT).putInt(Float.floatToIntBits(value)); + result = new Item(index++, key); + put(result); + } + return result; + } + + /** + * Adds a long to the constant pool of the class being build. Does nothing if + * the constant pool already contains a similar item. + * + * @param value the long value. + * @return a new or already existing long item. + */ + + private Item newLong (final long value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(LONG).putLong(value); + result = new Item(index, key); + put(result); + index += 2; + } + return result; + } + + /** + * Adds a double to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value the double value. + * @return a new or already existing double item. + */ + + private Item newDouble (final double value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(DOUBLE).putLong(Double.doubleToLongBits(value)); + result = new Item(index, key); + put(result); + index += 2; + } + return result; + } + + /** + * Adds a string to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value the String value. + * @return a new or already existing string item. + */ + + private Item newString (final String value) { + key2.set(STR, value, null, null); + Item result = get(key2); + if (result == null) { + pool.put12(STR, newUTF8(value)); + result = new Item(index++, key2); + put(result); + } + return result; + } + + /** + * Adds a name and type to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. + * + * @param name a name. + * @param desc a type descriptor. + * @return the index of a new or already existing name and type item. + */ + + private int newNameType (final String name, final String desc) { + key2.set(NAME_TYPE, name, desc, null); + Item result = get(key2); + if (result == null) { + put122(NAME_TYPE, newUTF8(name), newUTF8(desc)); + result = new Item(index++, key2); + put(result); + } + return result.index; + } + + /** + * Returns the constant pool's hash table item which is equal to the given + * item. + * + * @param key a constant pool item. + * @return the constant pool's hash table item which is equal to the given + * item, or null if there is no such item. + */ + + private Item get (final Item key) { + int h = key.hashCode; + Item i = items[h % items.length]; + while (i != null) { + if (i.hashCode == h && key.isEqualTo(i)) { + return i; + } + i = i.next; + } + return null; + } + + /** + * Puts the given item in the constant pool's hash table. The hash table + * must not already contains this item. + * + * @param i the item to be added to the constant pool's hash table. + */ + + private void put (final Item i) { + if (index > threshold) { + Item[] newItems = new Item[items.length * 2 + 1]; + for (int l = items.length - 1; l >= 0; --l) { + Item j = items[l]; + while (j != null) { + int index = j.hashCode % newItems.length; + Item k = j.next; + j.next = newItems[index]; + newItems[index] = j; + j = k; + } + } + items = newItems; + threshold = (int)(items.length * 0.75); + } + int index = i.hashCode % items.length; + i.next = items[index]; + items[index] = i; + } + + /** + * Puts one byte and two shorts into the constant pool. + * + * @param b a byte. + * @param s1 a short. + * @param s2 another short. + */ + + private void put122 (final int b, final int s1, final int s2) { + pool.put12(b, s1).putShort(s2); + } +} diff --git a/org/objectweb/asm/CodeAdapter.java b/org/objectweb/asm/CodeAdapter.java new file mode 100644 index 0000000000..ab398d76a9 --- /dev/null +++ b/org/objectweb/asm/CodeAdapter.java @@ -0,0 +1,163 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +/** + * An empty {@link CodeVisitor CodeVisitor} that delegates to another {@link + * CodeVisitor CodeVisitor}. This class can be used as a super class to quickly + * implement usefull code adapter classes, just by overriding the necessary + * methods. + */ + +public class CodeAdapter implements CodeVisitor { + + /** + * The {@link CodeVisitor CodeVisitor} to which this adapter delegates calls. + */ + + protected CodeVisitor cv; + + /** + * Constructs a new {@link CodeAdapter CodeAdapter} object. + * + * @param cv the code visitor to which this adapter must delegate calls. + */ + + public CodeAdapter (final CodeVisitor cv) { + this.cv = cv; + } + + public void visitInsn (final int opcode) { + cv.visitInsn(opcode); + } + + public void visitIntInsn (final int opcode, final int operand) { + cv.visitIntInsn(opcode, operand); + } + + public void visitVarInsn (final int opcode, final int var) { + cv.visitVarInsn(opcode, var); + } + + public void visitTypeInsn (final int opcode, final String desc) { + cv.visitTypeInsn(opcode, desc); + } + + public void visitFieldInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + cv.visitFieldInsn(opcode, owner, name, desc); + } + + public void visitMethodInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + cv.visitMethodInsn(opcode, owner, name, desc); + } + + public void visitJumpInsn (final int opcode, final Label label) { + cv.visitJumpInsn(opcode, label); + } + + public void visitLabel (final Label label) { + cv.visitLabel(label); + } + + public void visitLdcInsn (final Object cst) { + cv.visitLdcInsn(cst); + } + + public void visitIincInsn (final int var, final int increment) { + cv.visitIincInsn(var, increment); + } + + public void visitTableSwitchInsn ( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + cv.visitTableSwitchInsn(min, max, dflt, labels); + } + + public void visitLookupSwitchInsn ( + final Label dflt, + final int keys[], + final Label labels[]) + { + cv.visitLookupSwitchInsn(dflt, keys, labels); + } + + public void visitMultiANewArrayInsn (final String desc, final int dims) { + cv.visitMultiANewArrayInsn(desc, dims); + } + + public void visitTryCatchBlock ( + final Label start, + final Label end, + final Label handler, + final String type) + { + cv.visitTryCatchBlock(start, end, handler, type); + } + + public void visitMaxs (final int maxStack, final int maxLocals) { + cv.visitMaxs(maxStack, maxLocals); + } + + public void visitLocalVariable ( + final String name, + final String desc, + final Label start, + final Label end, + final int index) + { + cv.visitLocalVariable(name, desc, start, end, index); + } + + public void visitLineNumber (final int line, final Label start) { + cv.visitLineNumber(line, start); + } + + public void visitAttribute (final Attribute attr) { + cv.visitAttribute(attr); + } +} diff --git a/org/objectweb/asm/CodeVisitor.java b/org/objectweb/asm/CodeVisitor.java new file mode 100644 index 0000000000..b8aa3c719f --- /dev/null +++ b/org/objectweb/asm/CodeVisitor.java @@ -0,0 +1,310 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +/** + * A visitor to visit the bytecode instructions of a Java method. The methods + * of this visitor must be called in the sequential order of the bytecode + * instructions of the visited code. The {@link #visitMaxs visitMaxs} method + * must be called after all the instructions have been visited. The {@link + * #visitTryCatchBlock visitTryCatchBlock}, {@link #visitLocalVariable + * visitLocalVariable} and {@link #visitLineNumber visitLineNumber} methods may + * be called in any order, at any time (provided the labels passed as arguments + * have already been visited with {@link #visitLabel visitLabel}). + */ + +public interface CodeVisitor { + + /** + * Visits a zero operand instruction. + * + * @param opcode the opcode of the instruction to be visited. This opcode is + * either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, + * ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, + * FCONST_2, DCONST_0, DCONST_1, + * + * IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, + * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, + * SASTORE, + * + * POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, + * + * IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, + * DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, + * FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, + * LOR, IXOR, LXOR, + * + * I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, + * I2S, + * + * LCMP, FCMPL, FCMPG, DCMPL, DCMPG, + * + * IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, RETURN, + * + * ARRAYLENGTH, + * + * ATHROW, + * + * MONITORENTER, or MONITOREXIT. + */ + + void visitInsn (int opcode); + + /** + * Visits an instruction with a single int operand. + * + * @param opcode the opcode of the instruction to be visited. This opcode is + * either BIPUSH, SIPUSH or NEWARRAY. + * @param operand the operand of the instruction to be visited. + */ + + void visitIntInsn (int opcode, int operand); + + /** + * Visits a local variable instruction. A local variable instruction is an + * instruction that loads or stores the value of a local variable. + * + * @param opcode the opcode of the local variable instruction to be visited. + * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, + * LSTORE, FSTORE, DSTORE, ASTORE or RET. + * @param var the operand of the instruction to be visited. This operand is + * the index of a local variable. + */ + + void visitVarInsn (int opcode, int var); + + /** + * Visits a type instruction. A type instruction is an instruction that + * takes a type descriptor as parameter. + * + * @param opcode the opcode of the type instruction to be visited. This opcode + * is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. + * @param desc the operand of the instruction to be visited. This operand is + * must be a fully qualified class name in internal form, or the type + * descriptor of an array type (see {@link Type Type}). + */ + + void visitTypeInsn (int opcode, String desc); + + /** + * Visits a field instruction. A field instruction is an instruction that + * loads or stores the value of a field of an object. + * + * @param opcode the opcode of the type instruction to be visited. This opcode + * is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. + * @param owner the internal name of the field's owner class (see {@link + * Type#getInternalName getInternalName}). + * @param name the field's name. + * @param desc the field's descriptor (see {@link Type Type}). + */ + + void visitFieldInsn (int opcode, String owner, String name, String desc); + + /** + * Visits a method instruction. A method instruction is an instruction that + * invokes a method. + * + * @param opcode the opcode of the type instruction to be visited. This opcode + * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner the internal name of the method's owner class (see {@link + * Type#getInternalName getInternalName}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type Type}). + */ + + void visitMethodInsn (int opcode, String owner, String name, String desc); + + /** + * Visits a jump instruction. A jump instruction is an instruction that may + * jump to another instruction. + * + * @param opcode the opcode of the type instruction to be visited. This opcode + * is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, + * IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, + * GOTO, JSR, IFNULL or IFNONNULL. + * @param label the operand of the instruction to be visited. This operand is + * a label that designates the instruction to which the jump instruction + * may jump. + */ + + void visitJumpInsn (int opcode, Label label); + + /** + * Visits a label. A label designates the instruction that will be visited + * just after it. + * + * @param label a {@link Label Label} object. + */ + + void visitLabel (Label label); + + // ------------------------------------------------------------------------- + // Special instructions + // ------------------------------------------------------------------------- + + /** + * Visits a LDC instruction. + * + * @param cst the constant to be loaded on the stack. This parameter must be + * a non null {@link java.lang.Integer Integer}, a {@link java.lang.Float + * Float}, a {@link java.lang.Long Long}, a {@link java.lang.Double + * Double} or a {@link String String}. + */ + + void visitLdcInsn (Object cst); + + /** + * Visits an IINC instruction. + * + * @param var index of the local variable to be incremented. + * @param increment amount to increment the local variable by. + */ + + void visitIincInsn (int var, int increment); + + /** + * Visits a TABLESWITCH instruction. + * + * @param min the minimum key value. + * @param max the maximum key value. + * @param dflt beginning of the default handler block. + * @param labels beginnings of the handler blocks. labels[i] is the + * beginning of the handler block for the min + i key. + */ + + void visitTableSwitchInsn (int min, int max, Label dflt, Label labels[]); + + /** + * Visits a LOOKUPSWITCH instruction. + * + * @param dflt beginning of the default handler block. + * @param keys the values of the keys. + * @param labels beginnings of the handler blocks. labels[i] is the + * beginning of the handler block for the keys[i] key. + */ + + void visitLookupSwitchInsn (Label dflt, int keys[], Label labels[]); + + /** + * Visits a MULTIANEWARRAY instruction. + * + * @param desc an array type descriptor (see {@link Type Type}). + * @param dims number of dimensions of the array to allocate. + */ + + void visitMultiANewArrayInsn (String desc, int dims); + + // ------------------------------------------------------------------------- + // Exceptions table entries, max stack size and max locals + // ------------------------------------------------------------------------- + + /** + * Visits a try catch block. + * + * @param start beginning of the exception handler's scope (inclusive). + * @param end end of the exception handler's scope (exclusive). + * @param handler beginning of the exception handler's code. + * @param type internal name of the type of exceptions handled by the handler, + * or null to catch any exceptions (for "finally" blocks). + * @throws IllegalArgumentException if one of the labels has not already been + * visited by this visitor (by the {@link #visitLabel visitLabel} + * method). + */ + + void visitTryCatchBlock (Label start, Label end, Label handler, String type); + + /** + * Visits the maximum stack size and the maximum number of local variables of + * the method. + * + * @param maxStack maximum stack size of the method. + * @param maxLocals maximum number of local variables for the method. + */ + + void visitMaxs (int maxStack, int maxLocals); + + // ------------------------------------------------------------------------- + // Debug information + // ------------------------------------------------------------------------- + + /** + * Visits a local variable declaration. + * + * @param name the name of a local variable. + * @param desc the type descriptor of this local variable. + * @param start the first instruction corresponding to the scope of this + * local variable (inclusive). + * @param end the last instruction corresponding to the scope of this + * local variable (exclusive). + * @param index the local variable's index. + * @throws IllegalArgumentException if one of the labels has not already been + * visited by this visitor (by the {@link #visitLabel visitLabel} + * method). + */ + + void visitLocalVariable ( + String name, + String desc, + Label start, + Label end, + int index); + + /** + * Visits a line number declaration. + * + * @param line a line number. This number refers to the source file + * from which the class was compiled. + * @param start the first instruction corresponding to this line number. + * @throws IllegalArgumentException if start has not already been + * visited by this visitor (by the {@link #visitLabel visitLabel} + * method). + */ + + void visitLineNumber (int line, Label start); + + // ------------------------------------------------------------------------- + // Non standard attributes + // ------------------------------------------------------------------------- + + /** + * Visits a non standard attribute of the code. This method must visit only + * the first attribute in the given attribute list. + * + * @param attr a non standard code attribute. Must not be null. + */ + + void visitAttribute (Attribute attr); +} diff --git a/org/objectweb/asm/CodeWriter.java b/org/objectweb/asm/CodeWriter.java new file mode 100644 index 0000000000..7df47ba17a --- /dev/null +++ b/org/objectweb/asm/CodeWriter.java @@ -0,0 +1,1826 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +/** + * A {@link CodeVisitor CodeVisitor} that generates Java bytecode instructions. + * Each visit method of this class appends the bytecode corresponding to the + * visited instruction to a byte vector, in the order these methods are called. + */ + +public class CodeWriter implements CodeVisitor { + + /** + * true if preconditions must be checked at runtime or not. + */ + + final static boolean CHECK = false; + + /** + * Next code writer (see {@link ClassWriter#firstMethod firstMethod}). + */ + + CodeWriter next; + + /** + * The class writer to which this method must be added. + */ + + private ClassWriter cw; + + /** + * The index of the constant pool item that contains the name of this method. + */ + + private int name; + + /** + * The index of the constant pool item that contains the descriptor of this + * method. + */ + + private int desc; + + /** + * Access flags of this method. + */ + + private int access; + + /** + * Maximum stack size of this method. + */ + + private int maxStack; + + /** + * Maximum number of local variables for this method. + */ + + private int maxLocals; + + /** + * The bytecode of this method. + */ + + private ByteVector code = new ByteVector(); + + /** + * Number of entries in the catch table of this method. + */ + + private int catchCount; + + /** + * The catch table of this method. + */ + + private ByteVector catchTable; + + /** + * Number of exceptions that can be thrown by this method. + */ + + private int exceptionCount; + + /** + * The exceptions that can be thrown by this method. More + * precisely, this array contains the indexes of the constant pool items + * that contain the internal names of these exception classes. + */ + + private int[] exceptions; + + /** + * The non standard attributes of the method. + */ + + private Attribute attrs; + + /** + * Number of entries in the LocalVariableTable attribute. + */ + + private int localVarCount; + + /** + * The LocalVariableTable attribute. + */ + + private ByteVector localVar; + + /** + * Number of entries in the LineNumberTable attribute. + */ + + private int lineNumberCount; + + /** + * The LineNumberTable attribute. + */ + + private ByteVector lineNumber; + + /** + * The non standard attributes of the method's code. + */ + + private Attribute cattrs; + + /** + * Indicates if some jump instructions are too small and need to be resized. + */ + + private boolean resize; + + // -------------------------------------------------------------------------- + // Fields for the control flow graph analysis algorithm (used to compute the + // maximum stack size). A control flow graph contains one node per "basic + // block", and one edge per "jump" from one basic block to another. Each node + // (i.e., each basic block) is represented by the Label object that + // corresponds to the first instruction of this basic block. Each node also + // stores the list of its successors in the graph, as a linked list of Edge + // objects. + // -------------------------------------------------------------------------- + + /** + * true if the maximum stack size and number of local variables must + * be automatically computed. + */ + + private final boolean computeMaxs; + + /** + * The (relative) stack size after the last visited instruction. This size is + * relative to the beginning of the current basic block, i.e., the true stack + * size after the last visited instruction is equal to the {@link + * Label#beginStackSize beginStackSize} of the current basic block plus + * stackSize. + */ + + private int stackSize; + + /** + * The (relative) maximum stack size after the last visited instruction. This + * size is relative to the beginning of the current basic block, i.e., the + * true maximum stack size after the last visited instruction is equal to the + * {@link Label#beginStackSize beginStackSize} of the current basic block plus + * stackSize. + */ + + private int maxStackSize; + + /** + * The current basic block. This block is the basic block to which the next + * instruction to be visited must be added. + */ + + private Label currentBlock; + + /** + * The basic block stack used by the control flow analysis algorithm. This + * stack is represented by a linked list of {@link Label Label} objects, + * linked to each other by their {@link Label#next} field. This stack must + * not be confused with the JVM stack used to execute the JVM instructions! + */ + + private Label blockStack; + + /** + * The stack size variation corresponding to each JVM instruction. This stack + * variation is equal to the size of the values produced by an instruction, + * minus the size of the values consumed by this instruction. + */ + + private final static int[] SIZE; + + // -------------------------------------------------------------------------- + // Fields to optimize the creation of {@link Edge Edge} objects by using a + // pool of reusable objects. The (shared) pool is a linked list of Edge + // objects, linked to each other by their {@link Edge#poolNext} field. Each + // time a CodeWriter needs to allocate an Edge, it removes the first Edge + // of the pool and adds it to a private list of Edge objects. After the end + // of the control flow analysis algorithm, the Edge objects in the private + // list of the CodeWriter are added back to the pool (by appending this + // private list to the pool list; in order to do this in constant time, both + // head and tail of the private list are stored in this CodeWriter). + // -------------------------------------------------------------------------- + + /** + * The head of the list of {@link Edge Edge} objects used by this {@link + * CodeWriter CodeWriter}. These objects, linked to each other by their + * {@link Edge#poolNext} field, are added back to the shared pool at the + * end of the control flow analysis algorithm. + */ + + private Edge head; + + /** + * The tail of the list of {@link Edge Edge} objects used by this {@link + * CodeWriter CodeWriter}. These objects, linked to each other by their + * {@link Edge#poolNext} field, are added back to the shared pool at the + * end of the control flow analysis algorithm. + */ + + private Edge tail; + + /** + * The shared pool of {@link Edge Edge} objects. This pool is a linked list + * of Edge objects, linked to each other by their {@link Edge#poolNext} field. + */ + + private static Edge pool; + + // -------------------------------------------------------------------------- + // Static initializer + // -------------------------------------------------------------------------- + + /** + * Computes the stack size variation corresponding to each JVM instruction. + */ + + static { + int i; + int[] b = new int[202]; + String s = + "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDDCDCDEEEEEEEEE" + + "EEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCDCDCEEEEDDDDDDDCDCDCEFEF" + + "DDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFEDDDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"; + for (i = 0; i < b.length; ++i) { + b[i] = s.charAt(i) - 'E'; + } + SIZE = b; + + /* code to generate the above string + + int NA = 0; // not applicable (unused opcode or variable size opcode) + + b = new int[] { + 0, //NOP, // visitInsn + 1, //ACONST_NULL, // - + 1, //ICONST_M1, // - + 1, //ICONST_0, // - + 1, //ICONST_1, // - + 1, //ICONST_2, // - + 1, //ICONST_3, // - + 1, //ICONST_4, // - + 1, //ICONST_5, // - + 2, //LCONST_0, // - + 2, //LCONST_1, // - + 1, //FCONST_0, // - + 1, //FCONST_1, // - + 1, //FCONST_2, // - + 2, //DCONST_0, // - + 2, //DCONST_1, // - + 1, //BIPUSH, // visitIntInsn + 1, //SIPUSH, // - + 1, //LDC, // visitLdcInsn + NA, //LDC_W, // - + NA, //LDC2_W, // - + 1, //ILOAD, // visitVarInsn + 2, //LLOAD, // - + 1, //FLOAD, // - + 2, //DLOAD, // - + 1, //ALOAD, // - + NA, //ILOAD_0, // - + NA, //ILOAD_1, // - + NA, //ILOAD_2, // - + NA, //ILOAD_3, // - + NA, //LLOAD_0, // - + NA, //LLOAD_1, // - + NA, //LLOAD_2, // - + NA, //LLOAD_3, // - + NA, //FLOAD_0, // - + NA, //FLOAD_1, // - + NA, //FLOAD_2, // - + NA, //FLOAD_3, // - + NA, //DLOAD_0, // - + NA, //DLOAD_1, // - + NA, //DLOAD_2, // - + NA, //DLOAD_3, // - + NA, //ALOAD_0, // - + NA, //ALOAD_1, // - + NA, //ALOAD_2, // - + NA, //ALOAD_3, // - + -1, //IALOAD, // visitInsn + 0, //LALOAD, // - + -1, //FALOAD, // - + 0, //DALOAD, // - + -1, //AALOAD, // - + -1, //BALOAD, // - + -1, //CALOAD, // - + -1, //SALOAD, // - + -1, //ISTORE, // visitVarInsn + -2, //LSTORE, // - + -1, //FSTORE, // - + -2, //DSTORE, // - + -1, //ASTORE, // - + NA, //ISTORE_0, // - + NA, //ISTORE_1, // - + NA, //ISTORE_2, // - + NA, //ISTORE_3, // - + NA, //LSTORE_0, // - + NA, //LSTORE_1, // - + NA, //LSTORE_2, // - + NA, //LSTORE_3, // - + NA, //FSTORE_0, // - + NA, //FSTORE_1, // - + NA, //FSTORE_2, // - + NA, //FSTORE_3, // - + NA, //DSTORE_0, // - + NA, //DSTORE_1, // - + NA, //DSTORE_2, // - + NA, //DSTORE_3, // - + NA, //ASTORE_0, // - + NA, //ASTORE_1, // - + NA, //ASTORE_2, // - + NA, //ASTORE_3, // - + -3, //IASTORE, // visitInsn + -4, //LASTORE, // - + -3, //FASTORE, // - + -4, //DASTORE, // - + -3, //AASTORE, // - + -3, //BASTORE, // - + -3, //CASTORE, // - + -3, //SASTORE, // - + -1, //POP, // - + -2, //POP2, // - + 1, //DUP, // - + 1, //DUP_X1, // - + 1, //DUP_X2, // - + 2, //DUP2, // - + 2, //DUP2_X1, // - + 2, //DUP2_X2, // - + 0, //SWAP, // - + -1, //IADD, // - + -2, //LADD, // - + -1, //FADD, // - + -2, //DADD, // - + -1, //ISUB, // - + -2, //LSUB, // - + -1, //FSUB, // - + -2, //DSUB, // - + -1, //IMUL, // - + -2, //LMUL, // - + -1, //FMUL, // - + -2, //DMUL, // - + -1, //IDIV, // - + -2, //LDIV, // - + -1, //FDIV, // - + -2, //DDIV, // - + -1, //IREM, // - + -2, //LREM, // - + -1, //FREM, // - + -2, //DREM, // - + 0, //INEG, // - + 0, //LNEG, // - + 0, //FNEG, // - + 0, //DNEG, // - + -1, //ISHL, // - + -1, //LSHL, // - + -1, //ISHR, // - + -1, //LSHR, // - + -1, //IUSHR, // - + -1, //LUSHR, // - + -1, //IAND, // - + -2, //LAND, // - + -1, //IOR, // - + -2, //LOR, // - + -1, //IXOR, // - + -2, //LXOR, // - + 0, //IINC, // visitIincInsn + 1, //I2L, // visitInsn + 0, //I2F, // - + 1, //I2D, // - + -1, //L2I, // - + -1, //L2F, // - + 0, //L2D, // - + 0, //F2I, // - + 1, //F2L, // - + 1, //F2D, // - + -1, //D2I, // - + 0, //D2L, // - + -1, //D2F, // - + 0, //I2B, // - + 0, //I2C, // - + 0, //I2S, // - + -3, //LCMP, // - + -1, //FCMPL, // - + -1, //FCMPG, // - + -3, //DCMPL, // - + -3, //DCMPG, // - + -1, //IFEQ, // visitJumpInsn + -1, //IFNE, // - + -1, //IFLT, // - + -1, //IFGE, // - + -1, //IFGT, // - + -1, //IFLE, // - + -2, //IF_ICMPEQ, // - + -2, //IF_ICMPNE, // - + -2, //IF_ICMPLT, // - + -2, //IF_ICMPGE, // - + -2, //IF_ICMPGT, // - + -2, //IF_ICMPLE, // - + -2, //IF_ACMPEQ, // - + -2, //IF_ACMPNE, // - + 0, //GOTO, // - + 1, //JSR, // - + 0, //RET, // visitVarInsn + -1, //TABLESWITCH, // visiTableSwitchInsn + -1, //LOOKUPSWITCH, // visitLookupSwitch + -1, //IRETURN, // visitInsn + -2, //LRETURN, // - + -1, //FRETURN, // - + -2, //DRETURN, // - + -1, //ARETURN, // - + 0, //RETURN, // - + NA, //GETSTATIC, // visitFieldInsn + NA, //PUTSTATIC, // - + NA, //GETFIELD, // - + NA, //PUTFIELD, // - + NA, //INVOKEVIRTUAL, // visitMethodInsn + NA, //INVOKESPECIAL, // - + NA, //INVOKESTATIC, // - + NA, //INVOKEINTERFACE, // - + NA, //UNUSED, // NOT VISITED + 1, //NEW, // visitTypeInsn + 0, //NEWARRAY, // visitIntInsn + 0, //ANEWARRAY, // visitTypeInsn + 0, //ARRAYLENGTH, // visitInsn + NA, //ATHROW, // - + 0, //CHECKCAST, // visitTypeInsn + 0, //INSTANCEOF, // - + -1, //MONITORENTER, // visitInsn + -1, //MONITOREXIT, // - + NA, //WIDE, // NOT VISITED + NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn + -1, //IFNULL, // visitJumpInsn + -1, //IFNONNULL, // - + NA, //GOTO_W, // - + NA, //JSR_W, // - + }; + for (i = 0; i < b.length; ++i) { + System.err.print((char)('E' + b[i])); + } + System.err.println(); + */ + } + + // -------------------------------------------------------------------------- + // Constructor + // -------------------------------------------------------------------------- + + /** + * Constructs a CodeWriter. + * + * @param cw the class writer in which the method must be added. + * @param computeMaxs true if the maximum stack size and number of + * local variables must be automatically computed. + */ + + protected CodeWriter (final ClassWriter cw, final boolean computeMaxs) { + if (cw.firstMethod == null) { + cw.firstMethod = this; + cw.lastMethod = this; + } else { + cw.lastMethod.next = this; + cw.lastMethod = this; + } + this.cw = cw; + this.computeMaxs = computeMaxs; + if (computeMaxs) { + // pushes the first block onto the stack of blocks to be visited + currentBlock = new Label(); + currentBlock.pushed = true; + blockStack = currentBlock; + } + } + + /** + * Initializes this CodeWriter to define the bytecode of the specified method. + * + * @param access the method's access flags (see {@link Constants}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type Type}). + * @param exceptions the internal names of the method's exceptions. May be + * null. + * @param attrs the non standard attributes of the method. + */ + + protected void init ( + final int access, + final String name, + final String desc, + final String[] exceptions, + final Attribute attrs) + { + this.access = access; + this.name = cw.newUTF8(name); + this.desc = cw.newUTF8(desc); + if (exceptions != null && exceptions.length > 0) { + exceptionCount = exceptions.length; + this.exceptions = new int[exceptionCount]; + for (int i = 0; i < exceptionCount; ++i) { + this.exceptions[i] = cw.newClass(exceptions[i]); + } + } + this.attrs = attrs; + if (computeMaxs) { + // updates maxLocals + int size = getArgumentsAndReturnSizes(desc) >> 2; + if ((access & Constants.ACC_STATIC) != 0) { + --size; + } + if (size > maxLocals) { + maxLocals = size; + } + } + } + + // -------------------------------------------------------------------------- + // Implementation of the CodeVisitor interface + // -------------------------------------------------------------------------- + + public void visitInsn (final int opcode) { + if (computeMaxs) { + // updates current and max stack sizes + int size = stackSize + SIZE[opcode]; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + // if opcode == ATHROW or xRETURN, ends current block (no successor) + if ((opcode >= Constants.IRETURN && opcode <= Constants.RETURN) || + opcode == Constants.ATHROW) + { + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + currentBlock = null; + } + } + } + // adds the instruction to the bytecode of the method + code.putByte(opcode); + } + + public void visitIntInsn (final int opcode, final int operand) { + if (computeMaxs && opcode != Constants.NEWARRAY) { + // updates current and max stack sizes only if opcode == NEWARRAY + // (stack size variation = 0 for BIPUSH or SIPUSH) + int size = stackSize + 1; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // adds the instruction to the bytecode of the method + if (opcode == Constants.SIPUSH) { + code.put12(opcode, operand); + } else { // BIPUSH or NEWARRAY + code.put11(opcode, operand); + } + } + + public void visitVarInsn (final int opcode, final int var) { + if (computeMaxs) { + // updates current and max stack sizes + if (opcode == Constants.RET) { + // no stack change, but end of current block (no successor) + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + currentBlock = null; + } + } else { // xLOAD or xSTORE + int size = stackSize + SIZE[opcode]; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // updates max locals + int n; + if (opcode == Constants.LLOAD || opcode == Constants.DLOAD || + opcode == Constants.LSTORE || opcode == Constants.DSTORE) + { + n = var + 2; + } else { + n = var + 1; + } + if (n > maxLocals) { + maxLocals = n; + } + } + // adds the instruction to the bytecode of the method + if (var < 4 && opcode != Constants.RET) { + int opt; + if (opcode < Constants.ISTORE) { + opt = 26 /*ILOAD_0*/ + ((opcode - Constants.ILOAD) << 2) + var; + } else { + opt = 59 /*ISTORE_0*/ + ((opcode - Constants.ISTORE) << 2) + var; + } + code.putByte(opt); + } else if (var >= 256) { + code.putByte(196 /*WIDE*/).put12(opcode, var); + } else { + code.put11(opcode, var); + } + } + + public void visitTypeInsn (final int opcode, final String desc) { + if (computeMaxs && opcode == Constants.NEW) { + // updates current and max stack sizes only if opcode == NEW + // (stack size variation = 0 for ANEWARRAY, CHECKCAST, INSTANCEOF) + int size = stackSize + 1; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // adds the instruction to the bytecode of the method + code.put12(opcode, cw.newClass(desc)); + } + + public void visitFieldInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + if (computeMaxs) { + int size; + // computes the stack size variation + char c = desc.charAt(0); + switch (opcode) { + case Constants.GETSTATIC: + size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); + break; + case Constants.PUTSTATIC: + size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); + break; + case Constants.GETFIELD: + size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); + break; + //case Constants.PUTFIELD: + default: + size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); + break; + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // adds the instruction to the bytecode of the method + code.put12(opcode, cw.newField(owner, name, desc)); + } + + public void visitMethodInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + boolean itf = opcode == Constants.INVOKEINTERFACE; + Item i = cw.newMethodItem(owner, name, desc, itf); + int argSize = i.intVal; + if (computeMaxs) { + // computes the stack size variation. In order not to recompute several + // times this variation for the same Item, we use the intVal field of + // this item to store this variation, once it has been computed. More + // precisely this intVal field stores the sizes of the arguments and of + // the return value corresponding to desc. + if (argSize == 0) { + // the above sizes have not been computed yet, so we compute them... + argSize = getArgumentsAndReturnSizes(desc); + // ... and we save them in order not to recompute them in the future + i.intVal = argSize; + } + int size; + if (opcode == Constants.INVOKESTATIC) { + size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; + } else { + size = stackSize - (argSize >> 2) + (argSize & 0x03); + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // adds the instruction to the bytecode of the method + if (itf) { + if (!computeMaxs) { + if (argSize == 0) { + argSize = getArgumentsAndReturnSizes(desc); + i.intVal = argSize; + } + } + code.put12(Constants.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); + } else { + code.put12(opcode, i.index); + } + } + + public void visitJumpInsn (final int opcode, final Label label) { + if (CHECK) { + if (label.owner == null) { + label.owner = this; + } else if (label.owner != this) { + throw new IllegalArgumentException(); + } + } + if (computeMaxs) { + if (opcode == Constants.GOTO) { + // no stack change, but end of current block (with one new successor) + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + addSuccessor(stackSize, label); + currentBlock = null; + } + } else if (opcode == Constants.JSR) { + if (currentBlock != null) { + addSuccessor(stackSize + 1, label); + } + } else { + // updates current stack size (max stack size unchanged because stack + // size variation always negative in this case) + stackSize += SIZE[opcode]; + if (currentBlock != null) { + addSuccessor(stackSize, label); + } + } + } + // adds the instruction to the bytecode of the method + if (label.resolved && label.position - code.length < Short.MIN_VALUE) { + // case of a backward jump with an offset < -32768. In this case we + // automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx + // with IFNOTxxx GOTO_W , where IFNOTxxx is the "opposite" opcode + // of IFxxx (i.e., IFNE for IFEQ) and where designates the + // instruction just after the GOTO_W. + if (opcode == Constants.GOTO) { + code.putByte(200); // GOTO_W + } else if (opcode == Constants.JSR) { + code.putByte(201); // JSR_W + } else { + code.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1); + code.putShort(8); // jump offset + code.putByte(200); // GOTO_W + } + label.put(this, code, code.length - 1, true); + } else { + // case of a backward jump with an offset >= -32768, or of a forward jump + // with, of course, an unknown offset. In these cases we store the offset + // in 2 bytes (which will be increased in resizeInstructions, if needed). + code.putByte(opcode); + label.put(this, code, code.length - 1, false); + } + } + + public void visitLabel (final Label label) { + if (CHECK) { + if (label.owner == null) { + label.owner = this; + } else if (label.owner != this) { + throw new IllegalArgumentException(); + } + } + if (computeMaxs) { + if (currentBlock != null) { + // ends current block (with one new successor) + currentBlock.maxStackSize = maxStackSize; + addSuccessor(stackSize, label); + } + // begins a new current block, + // resets the relative current and max stack sizes + currentBlock = label; + stackSize = 0; + maxStackSize = 0; + } + // resolves previous forward references to label, if any + resize |= label.resolve(this, code.length, code.data); + } + + public void visitLdcInsn (final Object cst) { + Item i = cw.newCst(cst); + if (computeMaxs) { + int size; + // computes the stack size variation + if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { + size = stackSize + 2; + } else { + size = stackSize + 1; + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // adds the instruction to the bytecode of the method + int index = i.index; + if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { + code.put12(20 /*LDC2_W*/, index); + } else if (index >= 256) { + code.put12(19 /*LDC_W*/, index); + } else { + code.put11(Constants.LDC, index); + } + } + + public void visitIincInsn (final int var, final int increment) { + if (computeMaxs) { + // updates max locals only (no stack change) + int n = var + 1; + if (n > maxLocals) { + maxLocals = n; + } + } + // adds the instruction to the bytecode of the method + if ((var > 255) || (increment > 127) || (increment < -128)) { + code.putByte(196 /*WIDE*/).put12(Constants.IINC, var).putShort(increment); + } else { + code.putByte(Constants.IINC).put11(var, increment); + } + } + + public void visitTableSwitchInsn ( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + if (computeMaxs) { + // updates current stack size (max stack size unchanged) + --stackSize; + // ends current block (with many new successors) + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + addSuccessor(stackSize, dflt); + for (int i = 0; i < labels.length; ++i) { + addSuccessor(stackSize, labels[i]); + } + currentBlock = null; + } + } + // adds the instruction to the bytecode of the method + int source = code.length; + code.putByte(Constants.TABLESWITCH); + while (code.length % 4 != 0) { + code.putByte(0); + } + dflt.put(this, code, source, true); + code.putInt(min).putInt(max); + for (int i = 0; i < labels.length; ++i) { + labels[i].put(this, code, source, true); + } + } + + public void visitLookupSwitchInsn ( + final Label dflt, + final int keys[], + final Label labels[]) + { + if (computeMaxs) { + // updates current stack size (max stack size unchanged) + --stackSize; + // ends current block (with many new successors) + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + addSuccessor(stackSize, dflt); + for (int i = 0; i < labels.length; ++i) { + addSuccessor(stackSize, labels[i]); + } + currentBlock = null; + } + } + // adds the instruction to the bytecode of the method + int source = code.length; + code.putByte(Constants.LOOKUPSWITCH); + while (code.length % 4 != 0) { + code.putByte(0); + } + dflt.put(this, code, source, true); + code.putInt(labels.length); + for (int i = 0; i < labels.length; ++i) { + code.putInt(keys[i]); + labels[i].put(this, code, source, true); + } + } + + public void visitMultiANewArrayInsn (final String desc, final int dims) { + if (computeMaxs) { + // updates current stack size (max stack size unchanged because stack + // size variation always negative or null) + stackSize += 1 - dims; + } + // adds the instruction to the bytecode of the method + code.put12(Constants.MULTIANEWARRAY, cw.newClass(desc)).putByte(dims); + } + + public void visitTryCatchBlock ( + final Label start, + final Label end, + final Label handler, + final String type) + { + if (CHECK) { + if (start.owner != this || end.owner != this || handler.owner != this) { + throw new IllegalArgumentException(); + } + if (!start.resolved || !end.resolved || !handler.resolved) { + throw new IllegalArgumentException(); + } + } + if (computeMaxs) { + // pushes handler block onto the stack of blocks to be visited + if (!handler.pushed) { + handler.beginStackSize = 1; + handler.pushed = true; + handler.next = blockStack; + blockStack = handler; + } + } + ++catchCount; + if (catchTable == null) { + catchTable = new ByteVector(); + } + catchTable.putShort(start.position); + catchTable.putShort(end.position); + catchTable.putShort(handler.position); + catchTable.putShort(type != null ? cw.newClass(type) : 0); + } + + public void visitMaxs (final int maxStack, final int maxLocals) { + if (computeMaxs) { + // true (non relative) max stack size + int max = 0; + // control flow analysis algorithm: while the block stack is not empty, + // pop a block from this stack, update the max stack size, compute + // the true (non relative) begin stack size of the successors of this + // block, and push these successors onto the stack (unless they have + // already been pushed onto the stack). Note: by hypothesis, the {@link + // Label#beginStackSize} of the blocks in the block stack are the true + // (non relative) beginning stack sizes of these blocks. + Label stack = blockStack; + while (stack != null) { + // pops a block from the stack + Label l = stack; + stack = stack.next; + // computes the true (non relative) max stack size of this block + int start = l.beginStackSize; + int blockMax = start + l.maxStackSize; + // updates the global max stack size + if (blockMax > max) { + max = blockMax; + } + // analyses the successors of the block + Edge b = l.successors; + while (b != null) { + l = b.successor; + // if this successor has not already been pushed onto the stack... + if (!l.pushed) { + // computes the true beginning stack size of this successor block + l.beginStackSize = start + b.stackSize; + // pushes this successor onto the stack + l.pushed = true; + l.next = stack; + stack = l; + } + b = b.next; + } + } + this.maxStack = max; + // releases all the Edge objects used by this CodeWriter + synchronized (SIZE) { + // appends the [head ... tail] list at the beginning of the pool list + if (tail != null) { + tail.poolNext = pool; + pool = head; + } + } + } else { + this.maxStack = maxStack; + this.maxLocals = maxLocals; + } + } + + public void visitLocalVariable ( + final String name, + final String desc, + final Label start, + final Label end, + final int index) + { + if (CHECK) { + if (start.owner != this || !start.resolved) { + throw new IllegalArgumentException(); + } + if (end.owner != this || !end.resolved) { + throw new IllegalArgumentException(); + } + } + if (localVar == null) { + cw.newUTF8("LocalVariableTable"); + localVar = new ByteVector(); + } + ++localVarCount; + localVar.putShort(start.position); + localVar.putShort(end.position - start.position); + localVar.putShort(cw.newUTF8(name)); + localVar.putShort(cw.newUTF8(desc)); + localVar.putShort(index); + } + + public void visitLineNumber (final int line, final Label start) { + if (CHECK) { + if (start.owner != this || !start.resolved) { + throw new IllegalArgumentException(); + } + } + if (lineNumber == null) { + cw.newUTF8("LineNumberTable"); + lineNumber = new ByteVector(); + } + ++lineNumberCount; + lineNumber.putShort(start.position); + lineNumber.putShort(line); + } + + public void visitAttribute (final Attribute attr) { + attr.next = cattrs; + cattrs = attr; + } + + // -------------------------------------------------------------------------- + // Utility methods: control flow analysis algorithm + // -------------------------------------------------------------------------- + + /** + * Computes the size of the arguments and of the return value of a method. + * + * @param desc the descriptor of a method. + * @return the size of the arguments of the method (plus one for the implicit + * this argument), argSize, and the size of its return value, retSize, + * packed into a single int i = (argSize << 2) | retSize + * (argSize is therefore equal to i >> 2, and retSize to + * i & 0x03). + */ + + private static int getArgumentsAndReturnSizes (final String desc) { + int n = 1; + int c = 1; + while (true) { + char car = desc.charAt(c++); + if (car == ')') { + car = desc.charAt(c); + return n << 2 | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); + } else if (car == 'L') { + while (desc.charAt(c++) != ';') { + } + n += 1; + } else if (car == '[') { + while ((car = desc.charAt(c)) == '[') { + ++c; + } + if (car == 'D' || car == 'J') { + n -= 1; + } + } else if (car == 'D' || car == 'J') { + n += 2; + } else { + n += 1; + } + } + } + + /** + * Adds a successor to the {@link #currentBlock currentBlock} block. + * + * @param stackSize the current (relative) stack size in the current block. + * @param successor the successor block to be added to the current block. + */ + + private void addSuccessor (final int stackSize, final Label successor) { + Edge b; + // creates a new Edge object or reuses one from the shared pool + synchronized (SIZE) { + if (pool == null) { + b = new Edge(); + } else { + b = pool; + // removes b from the pool + pool = pool.poolNext; + } + } + // adds the previous Edge to the list of Edges used by this CodeWriter + if (tail == null) { + tail = b; + } + b.poolNext = head; + head = b; + // initializes the previous Edge object... + b.stackSize = stackSize; + b.successor = successor; + // ...and adds it to the successor list of the currentBlock block + b.next = currentBlock.successors; + currentBlock.successors = b; + } + + // -------------------------------------------------------------------------- + // Utility methods: dump bytecode array + // -------------------------------------------------------------------------- + + /** + * Returns the size of the bytecode of this method. + * + * @return the size of the bytecode of this method. + */ + + final int getSize () { + if (resize) { + // replaces the temporary jump opcodes introduced by Label.resolve. + resizeInstructions(new int[0], new int[0], 0); + } + int size = 8; + if (code.length > 0) { + cw.newUTF8("Code"); + size += 18 + code.length + 8 * catchCount; + if (localVar != null) { + size += 8 + localVar.length; + } + if (lineNumber != null) { + size += 8 + lineNumber.length; + } + if (cattrs != null) { + size += cattrs.getSize(cw, code.data, code.length, maxStack, maxLocals); + } + } + if (exceptionCount > 0) { + cw.newUTF8("Exceptions"); + size += 8 + 2 * exceptionCount; + } + if ((access & Constants.ACC_SYNTHETIC) != 0) { + cw.newUTF8("Synthetic"); + size += 6; + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + cw.newUTF8("Deprecated"); + size += 6; + } + if (attrs != null) { + size += attrs.getSize(cw, null, 0, -1, -1); + } + return size; + } + + /** + * Puts the bytecode of this method in the given byte vector. + * + * @param out the byte vector into which the bytecode of this method must be + * copied. + */ + + final void put (final ByteVector out) { + out.putShort(access).putShort(name).putShort(desc); + int attributeCount = 0; + if (code.length > 0) { + ++attributeCount; + } + if (exceptionCount > 0) { + ++attributeCount; + } + if ((access & Constants.ACC_SYNTHETIC) != 0) { + ++attributeCount; + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + ++attributeCount; + } + if (attrs != null) { + attributeCount += attrs.getCount(); + } + out.putShort(attributeCount); + if (code.length > 0) { + int size = 12 + code.length + 8 * catchCount; + if (localVar != null) { + size += 8 + localVar.length; + } + if (lineNumber != null) { + size += 8 + lineNumber.length; + } + if (cattrs != null) { + size += cattrs.getSize(cw, code.data, code.length, maxStack, maxLocals); + } + out.putShort(cw.newUTF8("Code")).putInt(size); + out.putShort(maxStack).putShort(maxLocals); + out.putInt(code.length).putByteArray(code.data, 0, code.length); + out.putShort(catchCount); + if (catchCount > 0) { + out.putByteArray(catchTable.data, 0, catchTable.length); + } + attributeCount = 0; + if (localVar != null) { + ++attributeCount; + } + if (lineNumber != null) { + ++attributeCount; + } + if (cattrs != null) { + attributeCount += cattrs.getCount(); + } + out.putShort(attributeCount); + if (localVar != null) { + out.putShort(cw.newUTF8("LocalVariableTable")); + out.putInt(localVar.length + 2).putShort(localVarCount); + out.putByteArray(localVar.data, 0, localVar.length); + } + if (lineNumber != null) { + out.putShort(cw.newUTF8("LineNumberTable")); + out.putInt(lineNumber.length + 2).putShort(lineNumberCount); + out.putByteArray(lineNumber.data, 0, lineNumber.length); + } + if (cattrs != null) { + cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); + } + } + if (exceptionCount > 0) { + out.putShort(cw.newUTF8("Exceptions")).putInt(2 * exceptionCount + 2); + out.putShort(exceptionCount); + for (int i = 0; i < exceptionCount; ++i) { + out.putShort(exceptions[i]); + } + } + if ((access & Constants.ACC_SYNTHETIC) != 0) { + out.putShort(cw.newUTF8("Synthetic")).putInt(0); + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + out.putShort(cw.newUTF8("Deprecated")).putInt(0); + } + if (attrs != null) { + attrs.put(cw, null, 0, -1, -1, out); + } + } + + // -------------------------------------------------------------------------- + // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) + // -------------------------------------------------------------------------- + + /** + * Resizes the designated instructions, while keeping jump offsets and + * instruction addresses consistent. This may require to resize other existing + * instructions, or even to introduce new instructions: for example, + * increasing the size of an instruction by 2 at the middle of a method can + * increases the offset of an IFEQ instruction from 32766 to 32768, in which + * case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W 32765. This, in turn, + * may require to increase the size of another jump instruction, and so on... + * All these operations are handled automatically by this method. + *

+ * This method must be called after all the method that is being built has + * been visited. In particular, the {@link Label Label} objects used to + * construct the method are no longer valid after this method has been called. + * + * @param indexes current positions of the instructions to be resized. Each + * instruction must be designated by the index of its last byte, + * plus one (or, in other words, by the index of the first byte of + * the next instruction). + * @param sizes the number of bytes to be added to the above + * instructions. More precisely, for each i < len, + * sizes[i] bytes will be added at the end of the instruction + * designated by indexes[i] or, if sizes[i] is + * negative, the last |sizes[i]| bytes of the instruction + * will be removed (the instruction size must not become negative + * or null). The gaps introduced by this method must be filled in + * "manually" in the array returned by the {@link #getCode getCode} + * method. + * @param len the number of instruction to be resized. Must be smaller than or + * equal to indexes.length and sizes.length. + * @return the indexes array, which now contains the new positions of + * the resized instructions (designated as above). + */ + + protected int[] resizeInstructions ( + final int[] indexes, + final int[] sizes, + final int len) + { + byte[] b = code.data; // bytecode of the method + int u, v, label; // indexes in b + int i, j; // loop indexes + + // 1st step: + // As explained above, resizing an instruction may require to resize another + // one, which may require to resize yet another one, and so on. The first + // step of the algorithm consists in finding all the instructions that + // need to be resized, without modifying the code. This is done by the + // following "fix point" algorithm: + // - parse the code to find the jump instructions whose offset will need + // more than 2 bytes to be stored (the future offset is computed from the + // current offset and from the number of bytes that will be inserted or + // removed between the source and target instructions). For each such + // instruction, adds an entry in (a copy of) the indexes and sizes arrays + // (if this has not already been done in a previous iteration!) + // - if at least one entry has been added during the previous step, go back + // to the beginning, otherwise stop. + // In fact the real algorithm is complicated by the fact that the size of + // TABLESWITCH and LOOKUPSWITCH instructions depends on their position in + // the bytecode (because of padding). In order to ensure the convergence of + // the algorithm, the number of bytes to be added or removed from these + // instructions is over estimated during the previous loop, and computed + // exactly only after the loop is finished (this requires another pass to + // parse the bytecode of the method). + + int[] allIndexes = new int[len]; // copy of indexes + int[] allSizes = new int[len]; // copy of sizes + boolean[] resize; // instructions to be resized + int newOffset; // future offset of a jump instruction + + System.arraycopy(indexes, 0, allIndexes, 0, len); + System.arraycopy(sizes, 0, allSizes, 0, len); + resize = new boolean[code.length]; + + int state = 3; // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done + do { + if (state == 3) { + state = 2; + } + u = 0; + while (u < b.length) { + int opcode = b[u] & 0xFF; // opcode of current instruction + int insert = 0; // bytes to be added after this instruction + + switch (ClassWriter.TYPE[opcode]) { + case ClassWriter.NOARG_INSN: + case ClassWriter.IMPLVAR_INSN: + u += 1; + break; + case ClassWriter.LABEL_INSN: + if (opcode > 201) { + // converts temporary opcodes 202 to 217 (inclusive), 218 and 219 + // to IFEQ ... JSR (inclusive), IFNULL and IFNONNULL + opcode = opcode < 218 ? opcode - 49 : opcode - 20; + label = u + readUnsignedShort(b, u + 1); + } else { + label = u + readShort(b, u + 1); + } + newOffset = getNewOffset(allIndexes, allSizes, u, label); + if (newOffset < Short.MIN_VALUE || newOffset > Short.MAX_VALUE) { + if (!resize[u]) { + if (opcode == Constants.GOTO || opcode == Constants.JSR) { + // two additional bytes will be required to replace this + // GOTO or JSR instruction with a GOTO_W or a JSR_W + insert = 2; + } else { + // five additional bytes will be required to replace this + // IFxxx instruction with IFNOTxxx GOTO_W , where + // IFNOTxxx is the "opposite" opcode of IFxxx (i.e., IFNE for + // IFEQ) and where designates the instruction just after + // the GOTO_W. + insert = 5; + } + resize[u] = true; + } + } + u += 3; + break; + case ClassWriter.LABELW_INSN: + u += 5; + break; + case ClassWriter.TABL_INSN: + if (state == 1) { + // true number of bytes to be added (or removed) from this + // instruction = (future number of padding bytes - current number + // of padding byte) - previously over estimated variation = + // = ((3 - newOffset%4) - (3 - u%4)) - u%4 + // = (-newOffset%4 + u%4) - u%4 + // = -(newOffset & 3) + newOffset = getNewOffset(allIndexes, allSizes, 0, u); + insert = -(newOffset & 3); + } else if (!resize[u]) { + // over estimation of the number of bytes to be added to this + // instruction = 3 - current number of padding bytes = 3 - (3 - + // u%4) = u%4 = u & 3 + insert = u & 3; + resize[u] = true; + } + // skips instruction + u = u + 4 - (u & 3); + u += 4*(readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12; + break; + case ClassWriter.LOOK_INSN: + if (state == 1) { + // like TABL_INSN + newOffset = getNewOffset(allIndexes, allSizes, 0, u); + insert = -(newOffset & 3); + } else if (!resize[u]) { + // like TABL_INSN + insert = u & 3; + resize[u] = true; + } + // skips instruction + u = u + 4 - (u & 3); + u += 8*readInt(b, u + 4) + 8; + break; + case ClassWriter.WIDE_INSN: + opcode = b[u + 1] & 0xFF; + if (opcode == Constants.IINC) { + u += 6; + } else { + u += 4; + } + break; + case ClassWriter.VAR_INSN: + case ClassWriter.SBYTE_INSN: + case ClassWriter.LDC_INSN: + u += 2; + break; + case ClassWriter.SHORT_INSN: + case ClassWriter.LDCW_INSN: + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.TYPE_INSN: + case ClassWriter.IINC_INSN: + u += 3; + break; + case ClassWriter.ITFMETH_INSN: + u += 5; + break; + // case ClassWriter.MANA_INSN: + default: + u += 4; + break; + } + if (insert != 0) { + // adds a new (u, insert) entry in the allIndexes and allSizes arrays + int[] newIndexes = new int[allIndexes.length + 1]; + int[] newSizes = new int[allSizes.length + 1]; + System.arraycopy(allIndexes, 0, newIndexes, 0, allIndexes.length); + System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length); + newIndexes[allIndexes.length] = u; + newSizes[allSizes.length] = insert; + allIndexes = newIndexes; + allSizes = newSizes; + if (insert > 0) { + state = 3; + } + } + } + if (state < 3) { + --state; + } + } while (state != 0); + + // 2nd step: + // copies the bytecode of the method into a new bytevector, updates the + // offsets, and inserts (or removes) bytes as requested. + + ByteVector newCode = new ByteVector(code.length); + + u = 0; + while (u < code.length) { + for (i = allIndexes.length - 1; i >= 0; --i) { + if (allIndexes[i] == u) { + if (i < len) { + if (sizes[i] > 0) { + newCode.putByteArray(null, 0, sizes[i]); + } else { + newCode.length += sizes[i]; + } + indexes[i] = newCode.length; + } + } + } + int opcode = b[u] & 0xFF; + switch (ClassWriter.TYPE[opcode]) { + case ClassWriter.NOARG_INSN: + case ClassWriter.IMPLVAR_INSN: + newCode.putByte(opcode); + u += 1; + break; + case ClassWriter.LABEL_INSN: + if (opcode > 201) { + // changes temporary opcodes 202 to 217 (inclusive), 218 and 219 + // to IFEQ ... JSR (inclusive), IFNULL and IFNONNULL + opcode = opcode < 218 ? opcode - 49 : opcode - 20; + label = u + readUnsignedShort(b, u + 1); + } else { + label = u + readShort(b, u + 1); + } + newOffset = getNewOffset(allIndexes, allSizes, u, label); + if (resize[u]) { + // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx with + // IFNOTxxx GOTO_W , where IFNOTxxx is the "opposite" opcode + // of IFxxx (i.e., IFNE for IFEQ) and where designates the + // instruction just after the GOTO_W. + if (opcode == Constants.GOTO) { + newCode.putByte(200); // GOTO_W + } else if (opcode == Constants.JSR) { + newCode.putByte(201); // JSR_W + } else { + newCode.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1); + newCode.putShort(8); // jump offset + newCode.putByte(200); // GOTO_W + newOffset -= 3; // newOffset now computed from start of GOTO_W + } + newCode.putInt(newOffset); + } else { + newCode.putByte(opcode); + newCode.putShort(newOffset); + } + u += 3; + break; + case ClassWriter.LABELW_INSN: + label = u + readInt(b, u + 1); + newOffset = getNewOffset(allIndexes, allSizes, u, label); + newCode.putByte(opcode); + newCode.putInt(newOffset); + u += 5; + break; + case ClassWriter.TABL_INSN: + // skips 0 to 3 padding bytes + v = u; + u = u + 4 - (v & 3); + // reads and copies instruction + int source = newCode.length; + newCode.putByte(Constants.TABLESWITCH); + while (newCode.length % 4 != 0) { + newCode.putByte(0); + } + label = v + readInt(b, u); u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.putInt(newOffset); + j = readInt(b, u); u += 4; + newCode.putInt(j); + j = readInt(b, u) - j + 1; u += 4; + newCode.putInt(readInt(b, u - 4)); + for ( ; j > 0; --j) { + label = v + readInt(b, u); u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.putInt(newOffset); + } + break; + case ClassWriter.LOOK_INSN: + // skips 0 to 3 padding bytes + v = u; + u = u + 4 - (v & 3); + // reads and copies instruction + source = newCode.length; + newCode.putByte(Constants.LOOKUPSWITCH); + while (newCode.length % 4 != 0) { + newCode.putByte(0); + } + label = v + readInt(b, u); u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.putInt(newOffset); + j = readInt(b, u); u += 4; + newCode.putInt(j); + for ( ; j > 0; --j) { + newCode.putInt(readInt(b, u)); u += 4; + label = v + readInt(b, u); u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.putInt(newOffset); + } + break; + case ClassWriter.WIDE_INSN: + opcode = b[u + 1] & 0xFF; + if (opcode == Constants.IINC) { + newCode.putByteArray(b, u, 6); + u += 6; + } else { + newCode.putByteArray(b, u, 4); + u += 4; + } + break; + case ClassWriter.VAR_INSN: + case ClassWriter.SBYTE_INSN: + case ClassWriter.LDC_INSN: + newCode.putByteArray(b, u, 2); + u += 2; + break; + case ClassWriter.SHORT_INSN: + case ClassWriter.LDCW_INSN: + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.TYPE_INSN: + case ClassWriter.IINC_INSN: + newCode.putByteArray(b, u, 3); + u += 3; + break; + case ClassWriter.ITFMETH_INSN: + newCode.putByteArray(b, u, 5); + u += 5; + break; + // case MANA_INSN: + default: + newCode.putByteArray(b, u, 4); + u += 4; + break; + } + } + + // updates the instructions addresses in the + // catch, local var and line number tables + if (catchTable != null) { + b = catchTable.data; + u = 0; + while (u < catchTable.length) { + writeShort(b, u, getNewOffset( + allIndexes, allSizes, 0, readUnsignedShort(b, u))); + writeShort(b, u + 2, getNewOffset( + allIndexes, allSizes, 0, readUnsignedShort(b, u + 2))); + writeShort(b, u + 4, getNewOffset( + allIndexes, allSizes, 0, readUnsignedShort(b, u + 4))); + u += 8; + } + } + if (localVar != null) { + b = localVar.data; + u = 0; + while (u < localVar.length) { + label = readUnsignedShort(b, u); + newOffset = getNewOffset(allIndexes, allSizes, 0, label); + writeShort(b, u, newOffset); + label += readUnsignedShort(b, u + 2); + newOffset = getNewOffset(allIndexes, allSizes, 0, label) - newOffset; + writeShort(b, u, newOffset); + u += 10; + } + } + if (lineNumber != null) { + b = lineNumber.data; + u = 0; + while (u < lineNumber.length) { + writeShort(b, u, getNewOffset( + allIndexes, allSizes, 0, readUnsignedShort(b, u))); + u += 4; + } + } + // updates the labels of the other attributes + while (cattrs != null) { + Label[] labels = cattrs.getLabels(); + if (labels != null) { + for (i = labels.length - 1; i >= 0; --i) { + if (!labels[i].resized) { + labels[i].position = + getNewOffset(allIndexes, allSizes, 0, labels[i].position); + labels[i].resized = true; + } + } + } + } + + // replaces old bytecodes with new ones + code = newCode; + + // returns the positions of the resized instructions + return indexes; + } + + /** + * Reads an unsigned short value in the given byte array. + * + * @param b a byte array. + * @param index the start index of the value to be read. + * @return the read value. + */ + + static int readUnsignedShort (final byte[] b, final int index) { + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); + } + + /** + * Reads a signed short value in the given byte array. + * + * @param b a byte array. + * @param index the start index of the value to be read. + * @return the read value. + */ + + static short readShort (final byte[] b, final int index) { + return (short)(((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); + } + + /** + * Reads a signed int value in the given byte array. + * + * @param b a byte array. + * @param index the start index of the value to be read. + * @return the read value. + */ + + static int readInt (final byte[] b, final int index) { + return ((b[index] & 0xFF) << 24) | + ((b[index + 1] & 0xFF) << 16) | + ((b[index + 2] & 0xFF) << 8) | + (b[index + 3] & 0xFF); + } + + /** + * Writes a short value in the given byte array. + * + * @param b a byte array. + * @param index where the first byte of the short value must be written. + * @param s the value to be written in the given byte array. + */ + + static void writeShort (final byte[] b, final int index, final int s) { + b[index] = (byte)(s >>> 8); + b[index + 1] = (byte)s; + } + + /** + * Computes the future value of a bytecode offset. + *

+ * Note: it is possible to have several entries for the same instruction + * in the indexes and sizes: two entries (index=a,size=b) + * and (index=a,size=b') are equivalent to a single entry (index=a,size=b+b'). + * + * @param indexes current positions of the instructions to be resized. Each + * instruction must be designated by the index of its last byte, + * plus one (or, in other words, by the index of the first byte of + * the next instruction). + * @param sizes the number of bytes to be added to the above + * instructions. More precisely, for each i < len, + * sizes[i] bytes will be added at the end of the instruction + * designated by indexes[i] or, if sizes[i] is + * negative, the last |sizes[i]| bytes of the instruction + * will be removed (the instruction size must not become negative + * or null). + * @param begin index of the first byte of the source instruction. + * @param end index of the first byte of the target instruction. + * @return the future value of the given bytecode offset. + */ + + static int getNewOffset ( + final int[] indexes, + final int[] sizes, + final int begin, + final int end) + { + int offset = end - begin; + for (int i = 0; i < indexes.length; ++i) { + if (begin < indexes[i] && indexes[i] <= end) { // forward jump + offset += sizes[i]; + } else if (end < indexes[i] && indexes[i] <= begin) { // backward jump + offset -= sizes[i]; + } + } + return offset; + } + + /** + * Returns the current size of the bytecode of this method. This size just + * includes the size of the bytecode instructions: it does not include the + * size of the Exceptions, LocalVariableTable, LineNumberTable, Synthetic + * and Deprecated attributes, if present. + * + * @return the current size of the bytecode of this method. + */ + + protected int getCodeSize () { + return code.length; + } + + /** + * Returns the current bytecode of this method. This bytecode only contains + * the instructions: it does not include the Exceptions, LocalVariableTable, + * LineNumberTable, Synthetic and Deprecated attributes, if present. + * + * @return the current bytecode of this method. The bytecode is contained + * between the index 0 (inclusive) and the index {@link #getCodeSize + * getCodeSize} (exclusive). + */ + + protected byte[] getCode () { + return code.data; + } +} diff --git a/org/objectweb/asm/Constants.java b/org/objectweb/asm/Constants.java new file mode 100644 index 0000000000..0c1988b75d --- /dev/null +++ b/org/objectweb/asm/Constants.java @@ -0,0 +1,288 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +/** + * Defines the JVM opcodes, access flags and array type codes. This interface + * does not define all the JVM opcodes because some opcodes are automatically + * handled. For example, the xLOAD and xSTORE opcodes are automatically replaced + * by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and xSTORE_n + * opcodes are therefore not defined in this interface. Likewise for LDC, + * automatically replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and + * JSR_W. + */ + +public interface Constants { + + // access flags + + int ACC_PUBLIC = 1; + int ACC_PRIVATE = 2; + int ACC_PROTECTED = 4; + int ACC_STATIC = 8; + int ACC_FINAL = 16; + int ACC_SYNCHRONIZED = 32; + int ACC_VOLATILE = 64; // fields + int ACC_BRIDGE = 64; // methods + int ACC_VARARGS = 128; // methods + int ACC_TRANSIENT = 128; // fields + int ACC_NATIVE = 256; // methods + int ACC_ENUM = 256; // classes and fields + // TODO find out what is the value for ACC_ANNOTATION constant + + int ACC_INTERFACE = 512; + int ACC_ABSTRACT = 1024; + int ACC_STRICT = 2048; + int ACC_SUPER = 32; + + int ACC_SYNTHETIC = 65536; + int ACC_DEPRECATED = 131072; + + // types for NEWARRAY + + int T_BOOLEAN = 4; + int T_CHAR = 5; + int T_FLOAT = 6; + int T_DOUBLE = 7; + int T_BYTE = 8; + int T_SHORT = 9; + int T_INT = 10; + int T_LONG = 11; + + // opcodes // visit method (- = idem) + + int NOP = 0; // visitInsn + int ACONST_NULL = 1; // - + int ICONST_M1 = 2; // - + int ICONST_0 = 3; // - + int ICONST_1 = 4; // - + int ICONST_2 = 5; // - + int ICONST_3 = 6; // - + int ICONST_4 = 7; // - + int ICONST_5 = 8; // - + int LCONST_0 = 9; // - + int LCONST_1 = 10; // - + int FCONST_0 = 11; // - + int FCONST_1 = 12; // - + int FCONST_2 = 13; // - + int DCONST_0 = 14; // - + int DCONST_1 = 15; // - + int BIPUSH = 16; // visitIntInsn + int SIPUSH = 17; // - + int LDC = 18; // visitLdcInsn + //int LDC_W = 19; // - + //int LDC2_W = 20; // - + int ILOAD = 21; // visitVarInsn + int LLOAD = 22; // - + int FLOAD = 23; // - + int DLOAD = 24; // - + int ALOAD = 25; // - + //int ILOAD_0 = 26; // - + //int ILOAD_1 = 27; // - + //int ILOAD_2 = 28; // - + //int ILOAD_3 = 29; // - + //int LLOAD_0 = 30; // - + //int LLOAD_1 = 31; // - + //int LLOAD_2 = 32; // - + //int LLOAD_3 = 33; // - + //int FLOAD_0 = 34; // - + //int FLOAD_1 = 35; // - + //int FLOAD_2 = 36; // - + //int FLOAD_3 = 37; // - + //int DLOAD_0 = 38; // - + //int DLOAD_1 = 39; // - + //int DLOAD_2 = 40; // - + //int DLOAD_3 = 41; // - + //int ALOAD_0 = 42; // - + //int ALOAD_1 = 43; // - + //int ALOAD_2 = 44; // - + //int ALOAD_3 = 45; // - + int IALOAD = 46; // visitInsn + int LALOAD = 47; // - + int FALOAD = 48; // - + int DALOAD = 49; // - + int AALOAD = 50; // - + int BALOAD = 51; // - + int CALOAD = 52; // - + int SALOAD = 53; // - + int ISTORE = 54; // visitVarInsn + int LSTORE = 55; // - + int FSTORE = 56; // - + int DSTORE = 57; // - + int ASTORE = 58; // - + //int ISTORE_0 = 59; // - + //int ISTORE_1 = 60; // - + //int ISTORE_2 = 61; // - + //int ISTORE_3 = 62; // - + //int LSTORE_0 = 63; // - + //int LSTORE_1 = 64; // - + //int LSTORE_2 = 65; // - + //int LSTORE_3 = 66; // - + //int FSTORE_0 = 67; // - + //int FSTORE_1 = 68; // - + //int FSTORE_2 = 69; // - + //int FSTORE_3 = 70; // - + //int DSTORE_0 = 71; // - + //int DSTORE_1 = 72; // - + //int DSTORE_2 = 73; // - + //int DSTORE_3 = 74; // - + //int ASTORE_0 = 75; // - + //int ASTORE_1 = 76; // - + //int ASTORE_2 = 77; // - + //int ASTORE_3 = 78; // - + int IASTORE = 79; // visitInsn + int LASTORE = 80; // - + int FASTORE = 81; // - + int DASTORE = 82; // - + int AASTORE = 83; // - + int BASTORE = 84; // - + int CASTORE = 85; // - + int SASTORE = 86; // - + int POP = 87; // - + int POP2 = 88; // - + int DUP = 89; // - + int DUP_X1 = 90; // - + int DUP_X2 = 91; // - + int DUP2 = 92; // - + int DUP2_X1 = 93; // - + int DUP2_X2 = 94; // - + int SWAP = 95; // - + int IADD = 96; // - + int LADD = 97; // - + int FADD = 98; // - + int DADD = 99; // - + int ISUB = 100; // - + int LSUB = 101; // - + int FSUB = 102; // - + int DSUB = 103; // - + int IMUL = 104; // - + int LMUL = 105; // - + int FMUL = 106; // - + int DMUL = 107; // - + int IDIV = 108; // - + int LDIV = 109; // - + int FDIV = 110; // - + int DDIV = 111; // - + int IREM = 112; // - + int LREM = 113; // - + int FREM = 114; // - + int DREM = 115; // - + int INEG = 116; // - + int LNEG = 117; // - + int FNEG = 118; // - + int DNEG = 119; // - + int ISHL = 120; // - + int LSHL = 121; // - + int ISHR = 122; // - + int LSHR = 123; // - + int IUSHR = 124; // - + int LUSHR = 125; // - + int IAND = 126; // - + int LAND = 127; // - + int IOR = 128; // - + int LOR = 129; // - + int IXOR = 130; // - + int LXOR = 131; // - + int IINC = 132; // visitIincInsn + int I2L = 133; // visitInsn + int I2F = 134; // - + int I2D = 135; // - + int L2I = 136; // - + int L2F = 137; // - + int L2D = 138; // - + int F2I = 139; // - + int F2L = 140; // - + int F2D = 141; // - + int D2I = 142; // - + int D2L = 143; // - + int D2F = 144; // - + int I2B = 145; // - + int I2C = 146; // - + int I2S = 147; // - + int LCMP = 148; // - + int FCMPL = 149; // - + int FCMPG = 150; // - + int DCMPL = 151; // - + int DCMPG = 152; // - + int IFEQ = 153; // visitJumpInsn + int IFNE = 154; // - + int IFLT = 155; // - + int IFGE = 156; // - + int IFGT = 157; // - + int IFLE = 158; // - + int IF_ICMPEQ = 159; // - + int IF_ICMPNE = 160; // - + int IF_ICMPLT = 161; // - + int IF_ICMPGE = 162; // - + int IF_ICMPGT = 163; // - + int IF_ICMPLE = 164; // - + int IF_ACMPEQ = 165; // - + int IF_ACMPNE = 166; // - + int GOTO = 167; // - + int JSR = 168; // - + int RET = 169; // visitVarInsn + int TABLESWITCH = 170; // visiTableSwitchInsn + int LOOKUPSWITCH = 171; // visitLookupSwitch + int IRETURN = 172; // visitInsn + int LRETURN = 173; // - + int FRETURN = 174; // - + int DRETURN = 175; // - + int ARETURN = 176; // - + int RETURN = 177; // - + int GETSTATIC = 178; // visitFieldInsn + int PUTSTATIC = 179; // - + int GETFIELD = 180; // - + int PUTFIELD = 181; // - + int INVOKEVIRTUAL = 182; // visitMethodInsn + int INVOKESPECIAL = 183; // - + int INVOKESTATIC = 184; // - + int INVOKEINTERFACE = 185; // - + //int UNUSED = 186; // NOT VISITED + int NEW = 187; // visitTypeInsn + int NEWARRAY = 188; // visitIntInsn + int ANEWARRAY = 189; // visitTypeInsn + int ARRAYLENGTH = 190; // visitInsn + int ATHROW = 191; // - + int CHECKCAST = 192; // visitTypeInsn + int INSTANCEOF = 193; // - + int MONITORENTER = 194; // visitInsn + int MONITOREXIT = 195; // - + //int WIDE = 196; // NOT VISITED + int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn + int IFNULL = 198; // visitJumpInsn + int IFNONNULL = 199; // - + //int GOTO_W = 200; // - + //int JSR_W = 201; // - +} diff --git a/org/objectweb/asm/Edge.java b/org/objectweb/asm/Edge.java new file mode 100644 index 0000000000..cf012b2037 --- /dev/null +++ b/org/objectweb/asm/Edge.java @@ -0,0 +1,70 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +/** + * An edge in the control flow graph of a method body. See {@link Label Label}. + */ + +class Edge { + + /** + * The (relative) stack size in the basic block from which this edge + * originates. This size is equal to the stack size at the "jump" instruction + * to which this edge corresponds, relatively to the stack size at the + * beginning of the originating basic block. + */ + + int stackSize; + + /** + * The successor block of the basic block from which this edge originates. + */ + + Label successor; + + /** + * The next edge in the list of successors of the originating basic block. + * See {@link Label#successors successors}. + */ + + Edge next; + + /** + * The next available edge in the pool. See {@link CodeWriter#pool pool}. + */ + + Edge poolNext; +} diff --git a/org/objectweb/asm/Item.java b/org/objectweb/asm/Item.java new file mode 100644 index 0000000000..6d603c47c8 --- /dev/null +++ b/org/objectweb/asm/Item.java @@ -0,0 +1,266 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +/** + * A constant pool item. Constant pool items can be created with the 'newXXX' + * methods in the {@link ClassWriter} class. + */ + +final class Item { + + /** + * Index of this item in the constant pool. + */ + + short index; + + /** + * Type of this constant pool item. A single class is used to represent all + * constant pool item types, in order to minimize the bytecode size of this + * package. The value of this field is one of the constants defined in the + * {@link ClassWriter ClassWriter} class. + */ + + int type; + + /** + * Value of this item, for a {@link ClassWriter#INT INT} item. + */ + + int intVal; + + /** + * Value of this item, for a {@link ClassWriter#LONG LONG} item. + */ + + long longVal; + + /** + * Value of this item, for a {@link ClassWriter#FLOAT FLOAT} item. + */ + + float floatVal; + + /** + * Value of this item, for a {@link ClassWriter#DOUBLE DOUBLE} item. + */ + + double doubleVal; + + /** + * First part of the value of this item, for items that do not hold a + * primitive value. + */ + + String strVal1; + + /** + * Second part of the value of this item, for items that do not hold a + * primitive value. + */ + + String strVal2; + + /** + * Third part of the value of this item, for items that do not hold a + * primitive value. + */ + + String strVal3; + + /** + * The hash code value of this constant pool item. + */ + + int hashCode; + + /** + * Link to another constant pool item, used for collision lists in the + * constant pool's hash table. + */ + + Item next; + + /** + * Constructs an uninitialized {@link Item Item} object. + */ + + Item () { + } + + /** + * Constructs a copy of the given item. + * + * @param index index of the item to be constructed. + * @param i the item that must be copied into the item to be constructed. + */ + + Item (final short index, final Item i) { + this.index = index; + type = i.type; + intVal = i.intVal; + longVal = i.longVal; + floatVal = i.floatVal; + doubleVal = i.doubleVal; + strVal1 = i.strVal1; + strVal2 = i.strVal2; + strVal3 = i.strVal3; + hashCode = i.hashCode; + } + + /** + * Sets this item to an {@link ClassWriter#INT INT} item. + * + * @param intVal the value of this item. + */ + + void set (final int intVal) { + this.type = ClassWriter.INT; + this.intVal = intVal; + this.hashCode = 0x7FFFFFFF & (type + intVal); + } + + /** + * Sets this item to a {@link ClassWriter#LONG LONG} item. + * + * @param longVal the value of this item. + */ + + void set (final long longVal) { + this.type = ClassWriter.LONG; + this.longVal = longVal; + this.hashCode = 0x7FFFFFFF & (type + (int)longVal); + } + + /** + * Sets this item to a {@link ClassWriter#FLOAT FLOAT} item. + * + * @param floatVal the value of this item. + */ + + void set (final float floatVal) { + this.type = ClassWriter.FLOAT; + this.floatVal = floatVal; + this.hashCode = 0x7FFFFFFF & (type + (int)floatVal); + } + + /** + * Sets this item to a {@link ClassWriter#DOUBLE DOUBLE} item. + * + * @param doubleVal the value of this item. + */ + + void set (final double doubleVal) { + this.type = ClassWriter.DOUBLE; + this.doubleVal = doubleVal; + this.hashCode = 0x7FFFFFFF & (type + (int)doubleVal); + } + + /** + * Sets this item to an item that do not hold a primitive value. + * + * @param type the type of this item. + * @param strVal1 first part of the value of this item. + * @param strVal2 second part of the value of this item. + * @param strVal3 third part of the value of this item. + */ + + void set ( + final int type, + final String strVal1, + final String strVal2, + final String strVal3) + { + this.type = type; + this.strVal1 = strVal1; + this.strVal2 = strVal2; + this.strVal3 = strVal3; + switch (type) { + case ClassWriter.UTF8: + case ClassWriter.STR: + case ClassWriter.CLASS: + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); + return; + case ClassWriter.NAME_TYPE: + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()*strVal2.hashCode()); + return; + //case ClassWriter.FIELD: + //case ClassWriter.METH: + //case ClassWriter.IMETH: + default: + hashCode = 0x7FFFFFFF & (type + + strVal1.hashCode()*strVal2.hashCode()*strVal3.hashCode()); + } + } + + /** + * Indicates if the given item is equal to this one. + * + * @param i the item to be compared to this one. + * @return true if the given item if equal to this one, + * false otherwise. + */ + + boolean isEqualTo (final Item i) { + if (i.type == type) { + switch (type) { + case ClassWriter.INT: + return i.intVal == intVal; + case ClassWriter.LONG: + return i.longVal == longVal; + case ClassWriter.FLOAT: + return i.floatVal == floatVal; + case ClassWriter.DOUBLE: + return i.doubleVal == doubleVal; + case ClassWriter.UTF8: + case ClassWriter.STR: + case ClassWriter.CLASS: + return i.strVal1.equals(strVal1); + case ClassWriter.NAME_TYPE: + return i.strVal1.equals(strVal1) && + i.strVal2.equals(strVal2); + //case ClassWriter.FIELD: + //case ClassWriter.METH: + //case ClassWriter.IMETH: + default: + return i.strVal1.equals(strVal1) && + i.strVal2.equals(strVal2) && + i.strVal3.equals(strVal3); + } + } + return false; + } +} diff --git a/org/objectweb/asm/Label.java b/org/objectweb/asm/Label.java new file mode 100644 index 0000000000..b657d6c4e1 --- /dev/null +++ b/org/objectweb/asm/Label.java @@ -0,0 +1,327 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm; + +/** + * A label represents a position in the bytecode of a method. Labels are used + * for jump, goto, and switch instructions, and for try catch blocks. + */ + +public class Label { + + /** + * The code writer to which this label belongs, or null if unknown. + */ + + CodeWriter owner; + + /** + * Indicates if the position of this label is known. + */ + + boolean resolved; + + /** + * The position of this label in the code, if known. + */ + + int position; + + /** + * If the label position has been updated, after instruction resizing. + */ + + boolean resized; + + /** + * Number of forward references to this label, times two. + */ + + private int referenceCount; + + /** + * Informations about forward references. Each forward reference is described + * by two consecutive integers in this array: the first one is the position + * of the first byte of the bytecode instruction that contains the forward + * reference, while the second is the position of the first byte of the + * forward reference itself. In fact the sign of the first integer indicates + * if this reference uses 2 or 4 bytes, and its absolute value gives the + * position of the bytecode instruction. + */ + + private int[] srcAndRefPositions; + + // -------------------------------------------------------------------------- + // Fields for the control flow graph analysis algorithm (used to compute the + // maximum stack size). A control flow graph contains one node per "basic + // block", and one edge per "jump" from one basic block to another. Each node + // (i.e., each basic block) is represented by the Label object that + // corresponds to the first instruction of this basic block. Each node also + // stores the list of it successors in the graph, as a linked list of Edge + // objects. + // -------------------------------------------------------------------------- + + /** + * The stack size at the beginning of this basic block. + * This size is initially unknown. It is computed by the control flow + * analysis algorithm (see {@link CodeWriter#visitMaxs visitMaxs}). + */ + + int beginStackSize; + + /** + * The (relative) maximum stack size corresponding to this basic block. This + * size is relative to the stack size at the beginning of the basic block, + * i.e., the true maximum stack size is equal to {@link #beginStackSize + * beginStackSize} + {@link #maxStackSize maxStackSize}. + */ + + int maxStackSize; + + /** + * The successors of this node in the control flow graph. These successors + * are stored in a linked list of {@link Edge Edge} objects, linked to each + * other by their {@link Edge#next} field. + */ + + Edge successors; + + /** + * The next basic block in the basic block stack. + * See {@link CodeWriter#visitMaxs visitMaxs}. + */ + + Label next; + + /** + * true if this basic block has been pushed in the basic block stack. + * See {@link CodeWriter#visitMaxs visitMaxs}. + */ + + boolean pushed; + + // -------------------------------------------------------------------------- + // Constructor + // -------------------------------------------------------------------------- + + /** + * Constructs a new label. + */ + + public Label () { + } + + // -------------------------------------------------------------------------- + // Methods to compute offsets and to manage forward references + // -------------------------------------------------------------------------- + + /** + * Returns the offset corresponding to this label. This offset is computed + * from the start of the method's bytecode. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @return the offset corresponding to this label. + * @throws IllegalStateException if this label is not resolved yet. + */ + + public int getOffset () { + if (!resolved) { + throw new IllegalStateException( + "Label offset position has not been resolved yet"); + } + return position; + } + + /** + * Puts a reference to this label in the bytecode of a method. If the position + * of the label is known, the offset is computed and written directly. + * Otherwise, a null offset is written and a new forward reference is declared + * for this label. + * + * @param owner the code writer that calls this method. + * @param out the bytecode of the method. + * @param source the position of first byte of the bytecode instruction that + * contains this label. + * @param wideOffset true if the reference must be stored in 4 bytes, + * or false if it must be stored with 2 bytes. + * @throws IllegalArgumentException if this label has not been created by the + * given code writer. + */ + + void put ( + final CodeWriter owner, + final ByteVector out, + final int source, + final boolean wideOffset) + { + if (CodeWriter.CHECK) { + if (this.owner == null) { + this.owner = owner; + } else if (this.owner != owner) { + throw new IllegalArgumentException(); + } + } + if (resolved) { + if (wideOffset) { + out.putInt(position - source); + } else { + out.putShort(position - source); + } + } else { + if (wideOffset) { + addReference(-1 - source, out.length); + out.putInt(-1); + } else { + addReference(source, out.length); + out.putShort(-1); + } + } + } + + /** + * Adds a forward reference to this label. This method must be called only for + * a true forward reference, i.e. only if this label is not resolved yet. For + * backward references, the offset of the reference can be, and must be, + * computed and stored directly. + * + * @param sourcePosition the position of the referencing instruction. This + * position will be used to compute the offset of this forward reference. + * @param referencePosition the position where the offset for this forward + * reference must be stored. + */ + + private void addReference ( + final int sourcePosition, + final int referencePosition) + { + if (srcAndRefPositions == null) { + srcAndRefPositions = new int[6]; + } + if (referenceCount >= srcAndRefPositions.length) { + int[] a = new int[srcAndRefPositions.length + 6]; + System.arraycopy(srcAndRefPositions, 0, a, 0, srcAndRefPositions.length); + srcAndRefPositions = a; + } + srcAndRefPositions[referenceCount++] = sourcePosition; + srcAndRefPositions[referenceCount++] = referencePosition; + } + + /** + * Resolves all forward references to this label. This method must be called + * when this label is added to the bytecode of the method, i.e. when its + * position becomes known. This method fills in the blanks that where left in + * the bytecode by each forward reference previously added to this label. + * + * @param owner the code writer that calls this method. + * @param position the position of this label in the bytecode. + * @param data the bytecode of the method. + * @return true if a blank that was left for this label was to small + * to store the offset. In such a case the corresponding jump instruction + * is replaced with a pseudo instruction (using unused opcodes) using an + * unsigned two bytes offset. These pseudo instructions will need to be + * replaced with true instructions with wider offsets (4 bytes instead of + * 2). This is done in {@link CodeWriter#resizeInstructions}. + * @throws IllegalArgumentException if this label has already been resolved, + * or if it has not been created by the given code writer. + */ + + boolean resolve ( + final CodeWriter owner, + final int position, + final byte[] data) + { + if (CodeWriter.CHECK) { + if (this.owner == null) { + this.owner = owner; + } + if (resolved || this.owner != owner) { + throw new IllegalArgumentException(); + } + } + boolean needUpdate = false; + this.resolved = true; + this.position = position; + int i = 0; + while (i < referenceCount) { + int source = srcAndRefPositions[i++]; + int reference = srcAndRefPositions[i++]; + int offset; + if (source >= 0) { + offset = position - source; + if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) { + // changes the opcode of the jump instruction, in order to be able to + // find it later (see resizeInstructions in CodeWriter). These + // temporary opcodes are similar to jump instruction opcodes, except + // that the 2 bytes offset is unsigned (and can therefore represent + // values from 0 to 65535, which is sufficient since the size of a + // method is limited to 65535 bytes). + int opcode = data[reference - 1] & 0xFF; + if (opcode <= Constants.JSR) { + // changes IFEQ ... JSR to opcodes 202 to 217 (inclusive) + data[reference - 1] = (byte)(opcode + 49); + } else { + // changes IFNULL and IFNONNULL to opcodes 218 and 219 (inclusive) + data[reference - 1] = (byte)(opcode + 20); + } + needUpdate = true; + } + data[reference++] = (byte)(offset >>> 8); + data[reference] = (byte)offset; + } else { + offset = position + source + 1; + data[reference++] = (byte)(offset >>> 24); + data[reference++] = (byte)(offset >>> 16); + data[reference++] = (byte)(offset >>> 8); + data[reference] = (byte)offset; + } + } + return needUpdate; + } + + // -------------------------------------------------------------------------- + // Overriden Object methods + // -------------------------------------------------------------------------- + + /** + * Returns a string representation of this label. + * + * @return a string representation of this label. + */ + + public String toString () { + return "L" + System.identityHashCode(this); + } +} diff --git a/org/objectweb/asm/Type.java b/org/objectweb/asm/Type.java new file mode 100644 index 0000000000..dbcf8b5c9a --- /dev/null +++ b/org/objectweb/asm/Type.java @@ -0,0 +1,763 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + * with contributions from: Chris Nokleberg + */ + +package org.objectweb.asm; + +import java.lang.reflect.Method; + +/** + * A Java type. This class can be used to make it easier to manipulate type + * and method descriptors. + */ + +public class Type { + + /** + * The sort of the void type. See {@link #getSort getSort}. + */ + + public final static int VOID = 0; + + /** + * The sort of the boolean type. See {@link #getSort getSort}. + */ + + public final static int BOOLEAN = 1; + + /** + * The sort of the char type. See {@link #getSort getSort}. + */ + + public final static int CHAR = 2; + + /** + * The sort of the byte type. See {@link #getSort getSort}. + */ + + public final static int BYTE = 3; + + /** + * The sort of the short type. See {@link #getSort getSort}. + */ + + public final static int SHORT = 4; + + /** + * The sort of the int type. See {@link #getSort getSort}. + */ + + public final static int INT = 5; + + /** + * The sort of the float type. See {@link #getSort getSort}. + */ + + public final static int FLOAT = 6; + + /** + * The sort of the long type. See {@link #getSort getSort}. + */ + + public final static int LONG = 7; + + /** + * The sort of the double type. See {@link #getSort getSort}. + */ + + public final static int DOUBLE = 8; + + /** + * The sort of array reference types. See {@link #getSort getSort}. + */ + + public final static int ARRAY = 9; + + /** + * The sort of object reference type. See {@link #getSort getSort}. + */ + + public final static int OBJECT = 10; + + /** + * The void type. + */ + + public final static Type VOID_TYPE = new Type(VOID); + + /** + * The boolean type. + */ + + public final static Type BOOLEAN_TYPE = new Type(BOOLEAN); + + /** + * The char type. + */ + + public final static Type CHAR_TYPE = new Type(CHAR); + + /** + * The byte type. + */ + + public final static Type BYTE_TYPE = new Type(BYTE); + + /** + * The short type. + */ + + public final static Type SHORT_TYPE = new Type(SHORT); + + /** + * The int type. + */ + + public final static Type INT_TYPE = new Type(INT); + + /** + * The float type. + */ + + public final static Type FLOAT_TYPE = new Type(FLOAT); + + /** + * The long type. + */ + + public final static Type LONG_TYPE = new Type(LONG); + + /** + * The double type. + */ + + public final static Type DOUBLE_TYPE = new Type(DOUBLE); + + // -------------------------------------------------------------------------- + // Fields + // -------------------------------------------------------------------------- + + /** + * The sort of this Java type. + */ + + private final int sort; + + /** + * A buffer containing the descriptor of this Java type. + * This field is only used for reference types. + */ + + private char[] buf; + + /** + * The offset of the descriptor of this Java type in {@link #buf buf}. + * This field is only used for reference types. + */ + + private int off; + + /** + * The length of the descriptor of this Java type. + */ + + private int len; + + // -------------------------------------------------------------------------- + // Constructors + // -------------------------------------------------------------------------- + + /** + * Constructs a primitive type. + * + * @param sort the sort of the primitive type to be constructed. + */ + + private Type (final int sort) { + this.sort = sort; + this.len = 1; + } + + /** + * Constructs a reference type. + * + * @param sort the sort of the reference type to be constructed. + * @param buf a buffer containing the descriptor of the previous type. + * @param off the offset of this descriptor in the previous buffer. + * @param len the length of this descriptor. + */ + + private Type ( + final int sort, + final char[] buf, + final int off, + final int len) + { + this.sort = sort; + this.buf = buf; + this.off = off; + this.len = len; + } + + /** + * Returns the Java type corresponding to the given type descriptor. + * + * @param typeDescriptor a type descriptor. + * @return the Java type corresponding to the given type descriptor. + */ + + public static Type getType (final String typeDescriptor) { + return getType(typeDescriptor.toCharArray(), 0); + } + + /** + * Returns the Java type corresponding to the given class. + * + * @param c a class. + * @return the Java type corresponding to the given class. + */ + + public static Type getType (final Class c) { + if (c.isPrimitive()) { + if (c == Integer.TYPE) { + return INT_TYPE; + } else if (c == Void.TYPE) { + return VOID_TYPE; + } else if (c == Boolean.TYPE) { + return BOOLEAN_TYPE; + } else if (c == Byte.TYPE) { + return BYTE_TYPE; + } else if (c == Character.TYPE) { + return CHAR_TYPE; + } else if (c == Short.TYPE) { + return SHORT_TYPE; + } else if (c == Double.TYPE) { + return DOUBLE_TYPE; + } else if (c == Float.TYPE) { + return FLOAT_TYPE; + } else /*if (c == Long.TYPE)*/ { + return LONG_TYPE; + } + } else { + return getType(getDescriptor(c)); + } + } + + /** + * Returns the Java types corresponding to the argument types of the given + * method descriptor. + * + * @param methodDescriptor a method descriptor. + * @return the Java types corresponding to the argument types of the given + * method descriptor. + */ + + public static Type[] getArgumentTypes (final String methodDescriptor) { + char[] buf = methodDescriptor.toCharArray(); + int off = 1; + int size = 0; + while (true) { + char car = buf[off++]; + if (car == ')') { + break; + } else if (car == 'L') { + while (buf[off++] != ';') { + } + ++size; + } else if (car != '[') { + ++size; + } + } + Type[] args = new Type[size]; + off = 1; + size = 0; + while (buf[off] != ')') { + args[size] = getType(buf, off); + off += args[size].len; + size += 1; + } + return args; + } + + /** + * Returns the Java types corresponding to the argument types of the given + * method. + * + * @param method a method. + * @return the Java types corresponding to the argument types of the given + * method. + */ + + public static Type[] getArgumentTypes (final Method method) { + Class[] classes = method.getParameterTypes(); + Type[] types = new Type[classes.length]; + for (int i = classes.length - 1; i >= 0; --i) { + types[i] = getType(classes[i]); + } + return types; + } + + /** + * Returns the Java type corresponding to the return type of the given + * method descriptor. + * + * @param methodDescriptor a method descriptor. + * @return the Java type corresponding to the return type of the given + * method descriptor. + */ + + public static Type getReturnType (final String methodDescriptor) { + char[] buf = methodDescriptor.toCharArray(); + return getType(buf, methodDescriptor.indexOf(')') + 1); + } + + /** + * Returns the Java type corresponding to the return type of the given + * method. + * + * @param method a method. + * @return the Java type corresponding to the return type of the given + * method. + */ + + public static Type getReturnType (final Method method) { + return getType(method.getReturnType()); + } + + /** + * Returns the Java type corresponding to the given type descriptor. + * + * @param buf a buffer containing a type descriptor. + * @param off the offset of this descriptor in the previous buffer. + * @return the Java type corresponding to the given type descriptor. + */ + + private static Type getType (final char[] buf, final int off) { + int len; + switch (buf[off]) { + case 'V': return VOID_TYPE; + case 'Z': return BOOLEAN_TYPE; + case 'C': return CHAR_TYPE; + case 'B': return BYTE_TYPE; + case 'S': return SHORT_TYPE; + case 'I': return INT_TYPE; + case 'F': return FLOAT_TYPE; + case 'J': return LONG_TYPE; + case 'D': return DOUBLE_TYPE; + case '[': + len = 1; + while (buf[off + len] == '[') { + ++len; + } + if (buf[off + len] == 'L') { + ++len; + while (buf[off + len] != ';') { + ++len; + } + } + return new Type(ARRAY, buf, off, len + 1); + //case 'L': + default: + len = 1; + while (buf[off + len] != ';') { + ++len; + } + return new Type(OBJECT, buf, off, len + 1); + } + } + + // -------------------------------------------------------------------------- + // Accessors + // -------------------------------------------------------------------------- + + /** + * Returns the sort of this Java type. + * + * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, {@link #CHAR CHAR}, + * {@link #BYTE BYTE}, {@link #SHORT SHORT}, {@link #INT INT}, {@link + * #FLOAT FLOAT}, {@link #LONG LONG}, {@link #DOUBLE DOUBLE}, {@link + * #ARRAY ARRAY} or {@link #OBJECT OBJECT}. + */ + + public int getSort () { + return sort; + } + + /** + * Returns the number of dimensions of this array type. + * This method should only be used for an array type. + * + * @return the number of dimensions of this array type. + */ + + public int getDimensions () { + int i = 1; + while (buf[off + i] == '[') { + ++i; + } + return i; + } + + /** + * Returns the type of the elements of this array type. + * This method should only be used for an array type. + * + * @return Returns the type of the elements of this array type. + */ + + public Type getElementType () { + return getType(buf, off + getDimensions()); + } + + /** + * Returns the name of the class corresponding to this object type. + * This method should only be used for an object type. + * + * @return the fully qualified name of the class corresponding to this object + * type. + */ + + public String getClassName () { + return new String(buf, off + 1, len - 2).replace('/', '.'); + } + + /** + * Returns the internal name of the class corresponding to this object type. + * The internal name of a class is its fully qualified name, where '.' are + * replaced by '/'. * This method should only be used for an object type. + * + * @return the internal name of the class corresponding to this object type. + */ + + public String getInternalName () { + return new String(buf, off + 1, len - 2); + } + + // -------------------------------------------------------------------------- + // Conversion to type descriptors + // -------------------------------------------------------------------------- + + /** + * Returns the descriptor corresponding to this Java type. + * + * @return the descriptor corresponding to this Java type. + */ + + public String getDescriptor () { + StringBuffer buf = new StringBuffer(); + getDescriptor(buf); + return buf.toString(); + } + + /** + * Returns the descriptor corresponding to the given argument and return + * types. + * + * @param returnType the return type of the method. + * @param argumentTypes the argument types of the method. + * @return the descriptor corresponding to the given argument and return + * types. + */ + + public static String getMethodDescriptor ( + final Type returnType, + final Type[] argumentTypes) + { + StringBuffer buf = new StringBuffer(); + buf.append('('); + for (int i = 0; i < argumentTypes.length; ++i) { + argumentTypes[i].getDescriptor(buf); + } + buf.append(')'); + returnType.getDescriptor(buf); + return buf.toString(); + } + + /** + * Appends the descriptor corresponding to this Java type to the given string + * buffer. + * + * @param buf the string buffer to which the descriptor must be appended. + */ + + private void getDescriptor (final StringBuffer buf) { + switch (sort) { + case VOID: buf.append('V'); return; + case BOOLEAN: buf.append('Z'); return; + case CHAR: buf.append('C'); return; + case BYTE: buf.append('B'); return; + case SHORT: buf.append('S'); return; + case INT: buf.append('I'); return; + case FLOAT: buf.append('F'); return; + case LONG: buf.append('J'); return; + case DOUBLE: buf.append('D'); return; + //case ARRAY: + //case OBJECT: + default: buf.append(this.buf, off, len); + } + } + + // -------------------------------------------------------------------------- + // Direct conversion from classes to type descriptors, + // without intermediate Type objects + // -------------------------------------------------------------------------- + + /** + * Returns the internal name of the given class. The internal name of a class + * is its fully qualified name, where '.' are replaced by '/'. + * + * @param c an object class. + * @return the internal name of the given class. + */ + + public static String getInternalName (final Class c) { + return c.getName().replace('.', '/'); + } + + /** + * Returns the descriptor corresponding to the given Java type. + * + * @param c an object class, a primitive class or an array class. + * @return the descriptor corresponding to the given class. + */ + + public static String getDescriptor (final Class c) { + StringBuffer buf = new StringBuffer(); + getDescriptor(buf, c); + return buf.toString(); + } + + /** + * Returns the descriptor corresponding to the given method. + * + * @param m a {@link Method Method} object. + * @return the descriptor of the given method. + */ + + public static String getMethodDescriptor (final Method m) { + Class[] parameters = m.getParameterTypes(); + StringBuffer buf = new StringBuffer(); + buf.append('('); + for (int i = 0; i < parameters.length; ++i) { + getDescriptor(buf, parameters[i]); + } + buf.append(')'); + getDescriptor(buf, m.getReturnType()); + return buf.toString(); + } + + /** + * Appends the descriptor of the given class to the given string buffer. + * + * @param buf the string buffer to which the descriptor must be appended. + * @param c the class whose descriptor must be computed. + */ + + private static void getDescriptor (final StringBuffer buf, final Class c) { + Class d = c; + while (true) { + if (d.isPrimitive()) { + char car; + if (d == Integer.TYPE) { + car = 'I'; + } else if (d == Void.TYPE) { + car = 'V'; + } else if (d == Boolean.TYPE) { + car = 'Z'; + } else if (d == Byte.TYPE) { + car = 'B'; + } else if (d == Character.TYPE) { + car = 'C'; + } else if (d == Short.TYPE) { + car = 'S'; + } else if (d == Double.TYPE) { + car = 'D'; + } else if (d == Float.TYPE) { + car = 'F'; + } else /*if (d == Long.TYPE)*/ { + car = 'J'; + } + buf.append(car); + return; + } else if (d.isArray()) { + buf.append('['); + d = d.getComponentType(); + } else { + buf.append('L'); + String name = d.getName(); + int len = name.length(); + for (int i = 0; i < len; ++i) { + char car = name.charAt(i); + buf.append(car == '.' ? '/' : car); + } + buf.append(';'); + return; + } + } + } + + // -------------------------------------------------------------------------- + // Corresponding size and opcodes + // -------------------------------------------------------------------------- + + /** + * Returns the size of values of this type. + * + * @return the size of values of this type, i.e., 2 for long and + * double, and 1 otherwise. + */ + + public int getSize () { + return (sort == LONG || sort == DOUBLE ? 2 : 1); + } + + /** + * Returns a JVM instruction opcode adapted to this Java type. + * + * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, + * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, + * ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. + * @return an opcode that is similar to the given opcode, but adapted to this + * Java type. For example, if this type is float and + * opcode is IRETURN, this method returns FRETURN. + */ + + public int getOpcode (final int opcode) { + if (opcode == Constants.IALOAD || opcode == Constants.IASTORE) { + switch (sort) { + case BOOLEAN: + case BYTE: + return opcode + 5; + case CHAR: + return opcode + 6; + case SHORT: + return opcode + 7; + case INT: + return opcode; + case FLOAT: + return opcode + 2; + case LONG: + return opcode + 1; + case DOUBLE: + return opcode + 3; + //case ARRAY: + //case OBJECT: + default: + return opcode + 4; + } + } else { + switch (sort) { + case VOID: + return opcode + 5; + case BOOLEAN: + case CHAR: + case BYTE: + case SHORT: + case INT: + return opcode; + case FLOAT: + return opcode + 2; + case LONG: + return opcode + 1; + case DOUBLE: + return opcode + 3; + //case ARRAY: + //case OBJECT: + default: + return opcode + 4; + } + } + } + + // -------------------------------------------------------------------------- + // Equals, hashCode and toString + // -------------------------------------------------------------------------- + + /** + * Tests if the given object is equal to this type. + * + * @param o the object to be compared to this type. + * @return true if the given object is equal to this type. + */ + + public boolean equals (final Object o) { + if (this == o) { + return true; + } + if (o == null || !(o instanceof Type)) { + return false; + } + Type t = (Type)o; + if (sort != t.sort) { + return false; + } + if (sort == Type.OBJECT || sort == Type.ARRAY) { + if (len != t.len) { + return false; + } + for (int i = off, j = t.off, end = i + len; i < end; i++, j++) { + if (buf[i] != t.buf[j]) { + return false; + } + } + } + return true; + } + + /** + * Returns a hash code value for this type. + * + * @return a hash code value for this type. + */ + + public int hashCode () { + int hc = 13 * sort; + if (sort == Type.OBJECT || sort == Type.ARRAY) { + for (int i = off, end = i + len; i < end; i++) { + hc = 17 * (hc + buf[i]); + } + } + return hc; + } + + /** + * Returns a string representation of this type. + * + * @return the descriptor of this type. + */ + + public String toString () { + return getDescriptor(); + } +} diff --git a/org/objectweb/asm/attrs/Annotation.java b/org/objectweb/asm/attrs/Annotation.java new file mode 100644 index 0000000000..454e41319c --- /dev/null +++ b/org/objectweb/asm/attrs/Annotation.java @@ -0,0 +1,335 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import java.util.LinkedList; +import java.util.List; + +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; + +/** + * Annotation data contains an annotated type and its array of the member-value + * pairs. Structure is in the following format: + *

+ *   annotation {
+ *     u2 type_index;
+ *     u2 num_member_value_pairs;
+ *     {
+ *       u2 member_name_index;
+ *       member_value value;
+ *     } member_value_pairs[num_member_value_pairs];
+ *   }
+ * 
+ * The items of the annotation structure are as follows: + *
+ *
type_index
+ *
The value of the type_index item must be a valid index into the constant_pool + * table. The constant_pool entry at that index must be a CONSTANT_Class_info + * structure representing the annotation interface corresponding to the + * annotation represented by this annotation structure.
+ *
num_member_value_pairs
+ *
The value of the num_member_value_pairs item gives the number of member-value + * pairs in the annotation represented by this annotation structure. Note that a + * maximum of 65535 member-value pairs may be contained in a single annotation.
+ *
member_value_pairs
+ *
Each value of the member_value_pairs table represents a single member-value + * pair in the annotation represented by this annotation structure. + * Each member_value_pairs entry contains the following two items: + *
member_name_index
+ *
The value of the member_name_index item must be a valid index into the + * constant_pool table. The constant_pool entry at that index must be a + * CONSTANT_Utf8_info structure representing the name of the annotation type + * member corresponding to this member_value_pairs entry.
+ *
value
+ *
The value item represents the value in the member-value pair represented by + * this member_value_pairs entry.
+ *
+ * + * + * + * @see JSR 175 : A Metadata + * Facility for the Java Programming Language + * + * @author Eugene Kuleshov + */ + +public class Annotation { + + public String type; + + public List memberValues = new LinkedList(); + + public void add (String name, Object value) { + memberValues.add(new Object[]{name, value}); + } + + /** + * Reads annotation data structures. + * + * @param cr the class that contains the attribute to be read. + * @param off index of the first byte of the data structure. + * @param buf buffer to be used to call {@link ClassReader#readUTF8 readUTF8}, + * {@link ClassReader#readClass readClass} or {@link + * ClassReader#readConst readConst}. + * + * @return offset position in bytecode after reading annotation + */ + + public int read (ClassReader cr, int off, char[] buf) { + type = cr.readClass(off, buf); + int numMemberValuePairs = cr.readUnsignedShort(off + 2); + off += 4; + for (int i = 0; i < numMemberValuePairs; i++) { + String memberName = cr.readUTF8(off, buf); + AnnotationMemberValue value = new AnnotationMemberValue(); + off = value.read(cr, off + 2, buf); + memberValues.add(new Object[]{memberName, value}); + } + return off; + } + + /** + * Writes annotation data structures. + * + * @param bv the byte array form to store data structures. + * @param cw the class to which this attribute must be added. This parameter + * can be used to add to the constant pool of this class the items that + * corresponds to this attribute. + */ + + public void write (ByteVector bv, ClassWriter cw) { + bv.putShort(cw.newClass(type)); + bv.putShort(memberValues.size()); + for (int i = 0; i < memberValues.size(); i++) { + Object[] value = (Object[])memberValues.get(i); + bv.putShort(cw.newUTF8((String)value[0])); + ((AnnotationMemberValue)value[1]).write(bv, cw); + } + } + + public void dump (StringBuffer buf, String varName) { + buf.append("Annotation ").append(varName).append(" = new Annotation();\n"); + buf.append(varName).append(".type = \"").append(type).append("\";\n"); + if (memberValues.size() > 0) { + buf.append("{\n"); + for (int i = 0; i < memberValues.size(); i++) { + Object[] values = (Object[])memberValues.get(i); + String val = varName + "val" + i; + ((AnnotationMemberValue)values[1]).dump(buf, val); + buf.append(varName).append(".add( \"") + .append(values[0]).append("\", ").append(val).append(");\n"); + } + buf.append("}\n"); + } + } + + /** + * Utility method to read List of annotations. Each element of annotations + * List will have Annotation instance. + * + * @param annotations the List to store parameters annotations. + * @param cr the class that contains the attribute to be read. + * @param off index of the first byte of the data structure. + * @param buf buffer to be used to call {@link ClassReader#readUTF8 readUTF8}, + * {@link ClassReader#readClass readClass} or {@link + * ClassReader#readConst readConst}. + * + * @return offset position in bytecode after reading annotations + */ + + public static int readAnnotations ( + List annotations, ClassReader cr, int off, char[] buf) { + int size = cr.readUnsignedShort(off); + off += 2; + for (int i = 0; i < size; i++) { + Annotation ann = new Annotation(); + off = ann.read(cr, off, buf); + annotations.add(ann); + } + return off; + } + + /** + * Utility method to read List of parameters annotations. + * + * @param parameters the List to store parameters annotations. + * Each element of the parameters List will have List of Annotation + * instances. + * @param cr the class that contains the attribute to be read. + * @param off index of the first byte of the data structure. + * @param buf buffer to be used to call {@link ClassReader#readUTF8 readUTF8}, + * {@link ClassReader#readClass readClass} or {@link + * ClassReader#readConst readConst}. + */ + + public static void readParameterAnnotations ( + List parameters, ClassReader cr, int off, char[] buf) { + int numParameters = cr.b[off++] & 0xff; + for (int i = 0; i < numParameters; i++) { + List annotations = new LinkedList(); + off = Annotation.readAnnotations(annotations, cr, off, buf); + parameters.add(annotations); + } + } + + /** + * Utility method to write List of annotations. + * + * @param bv the byte array form to store data structures. + * @param annotations the List of annotations to write. + * Elements should be instances of the Annotation class. + * @param cw the class to which this attribute must be added. This parameter + * can be used to add to the constant pool of this class the items that + * corresponds to this attribute. + * + * @return the byte array form with saved annotations. + */ + + public static ByteVector writeAnnotations (ByteVector bv, + List annotations, ClassWriter cw) { + bv.putShort(annotations.size()); + for (int i = 0; i < annotations.size(); i++) { + ((Annotation)annotations.get(i)).write(bv, cw); + } + return bv; + } + + /** + * Utility method to write List of parameters annotations. + * + * @param bv the byte array form to store data structures. + * @param parametars the List of parametars to write. Elements should be + * instances of the List that contains instances of the Annotation class. + * @param cw the class to which this attribute must be added. This parameter + * can be used to add to the constant pool of this class the items that + * corresponds to this attribute. + * + * @return the byte array form with saved annotations. + */ + + public static ByteVector writeParametersAnnotations (ByteVector bv, + List parameters, + ClassWriter cw) { + bv.putByte(parameters.size()); + for (int i = 0; i < parameters.size(); i++) { + writeAnnotations(bv, (List)parameters.get(i), cw); + } + return bv; + } + + public static void dumpAnnotations (StringBuffer buf, + String varName, List annotations) { + if (annotations.size() > 0) { + buf.append("{\n"); + for (int i = 0; i < annotations.size(); i++) { + String val = varName + "ann" + i; + ((Annotation)annotations.get(i)).dump(buf, val); + buf.append(varName).append(".add( ").append(val).append(");\n"); + } + buf.append("}\n"); + } + } + + public static void dumpParameterAnnotations (StringBuffer buf, + String varName, + List parameters) { + // TODO implement method Annotation.dumpParameterAnnotations + if (parameters.size() > 0) { + buf.append("{\n"); + for (int i = 0; i < parameters.size(); i++) { + String val = varName + "param" + i; + dumpAnnotations(buf, val, (List)parameters.get(i)); + buf.append(varName).append(".add( ").append(val).append(");\n"); + } + buf.append("}\n"); + } + } + + /** + * Returns annotation values in the format described in JSR-175 for Java + * source code. + */ + + public static String stringAnnotations (List annotations) { + StringBuffer sb = new StringBuffer(); + if (annotations.size() > 0) { + for (int i = 0; i < annotations.size(); i++) { + sb.append('\n').append(annotations.get(i)); + } + } + return sb.toString(); + } + + /** + * Returns parameter annotation values in the format described in JSR-175 + * for Java source code. + */ + + public static String stringParameterAnnotations (List parameters) { + StringBuffer sb = new StringBuffer(); + String sep = ""; + for (int i = 0; i < parameters.size(); i++) { + sb.append(sep).append(stringAnnotations((List)parameters.get(i))); + sep = ", "; + } + return sb.toString(); + } + + /** + * Returns value in the format described in JSR-175 for Java source code. + */ + + public String toString () { + StringBuffer sb = new StringBuffer("@").append(type); + // shorthand syntax for marker annotation + if (memberValues.size() > 0) { + sb.append(" ( "); + String sep = ""; + for (int i = 0; i < memberValues.size(); i++) { + Object[] value = (Object[])memberValues.get(i); + // using shorthand syntax for single-member annotation + if (memberValues.size() > 1) { + sb.append(sep).append(value[0]).append(" = "); + } + sb.append(value[1]); + sep = ", "; + } + sb.append(" )"); + } + return sb.toString(); + } +} diff --git a/org/objectweb/asm/attrs/AnnotationDefaultAttribute.java b/org/objectweb/asm/attrs/AnnotationDefaultAttribute.java new file mode 100644 index 0000000000..40fd398ec7 --- /dev/null +++ b/org/objectweb/asm/attrs/AnnotationDefaultAttribute.java @@ -0,0 +1,120 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; + +import java.util.Map; + +/** + * The AnnotationDefault attribute is a variable length attribute in the + * attributes table of certain method_info structures, namely those representing + * members of annotation types. The AnnotationDefault attribute records the + * default value for the member represented by the method_info structure. Each + * method_info structures representing a member of an annotation types may contain + * at most one AnnotationDefault attribute. The JVM must make this default value + * available so it can be applied by appropriate reflective APIs. + *

+ * The AnnotationDefault attribute has the following format: + *

+ *    AnnotationDefault_attribute {
+ *      u2 attribute_name_index;
+ *      u4 attribute_length;
+ *      member_value default_value;
+ *    }
+ * 
+ * The items of the AnnotationDefault structure are as follows: + *
+ *
attribute_name_index
+ *
The value of the attribute_name_index item must be a valid index into the + * constant_pool table. The constant_pool entry at that index must be a + * CONSTANT_Utf8_info structure representing the string "AnnotationDefault".
+ *
attribute_length
+ *
The value of the attribute_length item indicates the length of the attribute, + * excluding the initial six bytes. The value of the attribute_length item is + * thus dependent on the default value.
+ *
default_value
+ *
The default_value item represents the default value of the annotation type + * {@link org.objectweb.asm.attrs.AnnotationMemberValue member} whose default + * value is represented by this AnnotationDefault attribute.
+ *
+ * + * @see JSR 175 : A Metadata + * Facility for the Java Programming Language + * + * @author Eugene Kuleshov + */ + +public class AnnotationDefaultAttribute extends Attribute implements Dumpable { + + public AnnotationMemberValue defaultValue; + + public AnnotationDefaultAttribute () { + super("AnnotationDefault"); + } + + protected Attribute read (ClassReader cr, int off, + int len, char[] buf, int codeOff, Label[] labels) { + AnnotationDefaultAttribute ann = new AnnotationDefaultAttribute(); + ann.defaultValue = new AnnotationMemberValue(); + ann.defaultValue.read(cr, off, buf); + return ann; + } + + protected ByteVector write (ClassWriter cw, byte[] code, + int len, int maxStack, int maxLocals) { + return defaultValue.write(new ByteVector(), cw); + } + + public void dump (StringBuffer buf, String varName, Map labelNames) { + buf.append("AnnotationDefaultAttribute ").append(varName) + .append(" = new AnnotationDefaultAttribute();\n"); + defaultValue.dump(buf, varName + "Val"); + buf.append(varName).append(".defaultValue = ") + .append(varName).append("Val;\n"); + } + + /** + * Returns value in the format described in JSR-175 for Java source code. + */ + + public String toString () { + return "default " + defaultValue; + } +} diff --git a/org/objectweb/asm/attrs/AnnotationMemberValue.java b/org/objectweb/asm/attrs/AnnotationMemberValue.java new file mode 100644 index 0000000000..1d0115cfd6 --- /dev/null +++ b/org/objectweb/asm/attrs/AnnotationMemberValue.java @@ -0,0 +1,466 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Type; + +/** + * The member_value structure is a discriminated union representing the value of a + * member-value pair. It is used to represent values in all class file attributes + * that describe annotations ( RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, + * RuntimeVisibleParameterAnnotations, and RuntimeInvisibleParameterAnnotations). + *

+ * The member_value structure has the following format: + *

+ *   member_value {
+ *     u1 tag;
+ *     union {
+ *       u2   const_value_index;
+ *       {
+ *         u2   type_name_index;
+ *         u2   const_name_index;
+ *       } enum_const_value;
+ *       u2   class_info_index;
+ *       annotation annotation_value;
+ *       {
+ *         u2    num_values;
+ *         member_value values[num_values];
+ *       } array_value;
+ *     } value;
+ *   }
+ * 
+ * The items of the member_value structure are as follows: + *
+ *
tag
+ *
The tag item indicates the member type of this member-value pair. The letters + * 'B', 'C', 'D', 'F', 'I', 'J', 'S', and 'Z' indicate a primitive type. These + * letters are interpreted as BaseType characters (Table 4.2). The other legal + * values for tag are listed with their interpretations in this table: + *
+ *     tag  value Member Type
+ *     's'  String
+ *     'e'  enum constant
+ *     'c'  class
+ *     '@'  annotation type
+ *     '['  array
+ *   
+ *
+ *
value
+ *
The value item represents the value of this annotation element. This item is + * a union. The tag item, above, determines which item of the union is to be used: + *
+ *
const_value_index
+ *
The const_value_index item is used if the tag item is one of 'B', 'C', 'D', + * 'F', 'I', 'J', 'S', 'Z', or 's'. The value of the const_value_index item must + * be a valid index into the constant_pool table. The constant_pool entry at + * that index must be of the correct entry type for the field type designated by + * the tag item, as specified in table 4.6, with one exception: if the tag is + * 's', the the value of the const_value_index item must be the index of a + * CONSTANT_Utf8 structure, rather than a CONSTANT_String.
+ *
enum_const_value
+ *
The enum_const_value item is used if the tag item is 'e'. The + * enum_const_value item consists of the following two items: + *
+ *
type_name_index
+ *
The value of the type_name_index item must be a valid index into the + * constant_pool table. The constant_pool entry at that index must be a + * CONSTANT_Utf8_info structure representing the binary name (JLS 13.1) of the + * type of the enum constant represented by this member_value structure.
+ *
const_name_index
+ *
The value of the const_name_index item must be a valid index into the + * constant_pool table. The constant_pool entry at that index must be a + * CONSTANT_Utf8_info structure representing the simple name of the enum + * constant represented by this member_value structure.
+ *
+ *
+ *
class_info_index
+ *
The class_info_index item is used if the tag item is 'c'. The + * class_info_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Class_info + * structure representing the class represented by this member_value + * structure.
+ *
annotation_value
+ *
The annotation_value item is used if the tag item is '@'. The member_value + * structure represents a "nested" {@link org.objectweb.asm.attrs.Annotation annotation}.
+ *
array_value
+ *
The array_value item is used if the tag item is '['. The array_value item + * consists of the following two items: + *
+ *
num_values
+ *
The value of the num_values item gives the number of elements in the + * array-typed value represented by this member_value structure. Note that a + * maximum of 65535 elements are permitted in an array-typed member value.
+ *
values
+ *
Each element of the values table gives the value of an element of the + * array-typed value represented by this {@link AnnotationMemberValue member_value structure}.
+ *
+ *
+ *
+ *
+ *
+ * + * @see JSR 175 : A Metadata + * Facility for the Java Programming Language + * + * @author Eugene Kuleshov + */ + +public class AnnotationMemberValue { + + private static final String TAGS = "BCDFIJSZsec@["; + + private static final Class[] TYPES = new Class[]{ + Byte.class, + Character.class, + Double.class, + Float.class, + Integer.class, + Long.class, + Short.class, + Boolean.class, + String.class, + EnumConstValue.class, // should we use java.lang.Enum in the future? + Type.class, + Annotation.class, + AnnotationMemberValue[].class}; + + // private int tag; + + private Object value; + + public AnnotationMemberValue () { + } + + public AnnotationMemberValue (Object value) { + this.value = value; + } + + public int getTag () { + for (int i = 0; i < TYPES.length; i++) { + if (value.getClass().equals(TYPES[i])) return TAGS.charAt(i); + } + return -1; + } + + public Object getValue () { + return value; + } + + /** + * Reads member_value data structures. + * + * @param cr the class that contains the attribute to be read. + * @param off index of the first byte of the data structure. + * @param buf buffer to be used to call {@link ClassReader#readUTF8 readUTF8}, + * {@link ClassReader#readClass readClass} or {@link + * ClassReader#readConst readConst}. + * + * @return offset position in bytecode after reading annotation + */ + + public int read (ClassReader cr, int off, char[] buf) { + int tag = cr.readByte(off++); + switch (tag) { + case 'B': // pointer to CONSTANT_Byte + case 'C': // pointer to CONSTANT_Char + case 'D': // pointer to CONSTANT_Double + case 'F': // pointer to CONSTANT_Float + case 'I': // pointer to CONSTANT_Integer + case 'J': // pointer to CONSTANT_Long + case 'S': // pointer to CONSTANT_Short + case 'Z': // pointer to CONSTANT_Boolean + value = cr.readConst(cr.readUnsignedShort(off), buf); + off += 2; + break; + + case 's': // pointer to CONSTANT_Utf8 + value = cr.readUTF8(off, buf); + off += 2; + break; + + case 'e': // enum_const_value + // TODO verify the data structures + value = new EnumConstValue(cr.readUTF8(off, buf), cr.readUTF8(off + 2, buf)); + off += 4; + break; + + case 'c': // class_info + value = Type.getType(cr.readClass(off, buf)); + off += 2; + break; + + case '@': // annotation_value + value = new Annotation(); + off = ((Annotation)value).read(cr, off, buf); + break; + + case '[': // array_value + int size = cr.readUnsignedShort(off); + off += 2; + AnnotationMemberValue[] v = new AnnotationMemberValue[size]; + value = v; + for (int i = 0; i < size; i++) { + v[i] = new AnnotationMemberValue(); + off = v[i].read(cr, off, buf); + } + break; + } + return off; + } + + /** + * Writes member_value data structures. + * + * @param bv the byte array form to store data structures. + * @param cw the class to which this attribute must be added. This parameter + * can be used to add to the constant pool of this class the items that + * corresponds to this attribute. + */ + + public ByteVector write (ByteVector bv, ClassWriter cw) { + int tag = getTag(); + bv.putByte(tag); + switch (tag) { + case 'B': // pointer to CONSTANT_Byte + case 'C': // pointer to CONSTANT_Char + case 'D': // pointer to CONSTANT_Double + case 'F': // pointer to CONSTANT_Float + case 'I': // pointer to CONSTANT_Integer + case 'J': // pointer to CONSTANT_Long + case 'S': // pointer to CONSTANT_Short + case 'Z': // pointer to CONSTANT_Boolean + bv.putShort(cw.newConst(value)); + break; + + case 's': // pointer to CONSTANT_Utf8 + bv.putShort(cw.newUTF8((String)value)); + break; + + case 'e': // enum_const_value + ((EnumConstValue)value).write(bv, cw); + break; + + case 'c': // class_info + bv.putShort(cw.newClass(((Type)value).getDescriptor())); + break; + + case '@': // annotation_value + ((Annotation)value).write(bv, cw); + break; + + case '[': // array_value + AnnotationMemberValue[] v = (AnnotationMemberValue[])value; + bv.putShort(v.length); + for (int i = 0; i < v.length; i++) { + v[i].write(bv, cw); + } + break; + } + return bv; + } + + public void dump (StringBuffer buf, String valName) { + int tag = getTag(); + String objName = valName.concat("obj"); + switch (tag) { + case 'B': // pointer to CONSTANT_Byte + buf.append("Object ").append(objName) + .append(" = new Byte(").append(value).append(");\n"); + break; + + case 'C': // pointer to CONSTANT_Char + buf.append("Object ").append(objName) + .append(" = new Character((char)").append(value).append(");\n"); + break; + + case 'D': // pointer to CONSTANT_Double + buf.append("Object ").append(objName) + .append(" = new Double((double)").append(value).append(");\n"); + break; + + case 'F': // pointer to CONSTANT_Float + buf.append("Object ").append(objName) + .append(" = new Float((float)").append(value).append(");\n"); + break; + + case 'I': // pointer to CONSTANT_Integer + buf.append("Object ").append(objName) + .append(" = new Integer((int)").append(value).append(");\n"); + break; + + case 'J': // pointer to CONSTANT_Long + buf.append("Object ").append(objName) + .append(" = new Long((long)").append(value).append(");\n"); + break; + + case 'S': // pointer to CONSTANT_Short + buf.append("Object ").append(objName) + .append(" = new Short((short)").append(value).append(");\n"); + break; + + case 'Z': // pointer to CONSTANT_Boolean + buf.append("Object ").append(objName) + .append(" = new Boolean(").append(value).append(");\n"); + break; + + case 's': // pointer to CONSTANT_Utf8 + buf.append("Object ").append(objName) + .append(" = \"").append(value).append("\";\n"); + break; + + case 'e': // enum_const_value + EnumConstValue e = (EnumConstValue)value; + buf.append("Object ").append(objName) + .append(" = new AnnotationMemberValue.EnumConstValue(\"") + .append(e.typeName).append("\", \"").append(e.constName) + .append("\"));\n"); + break; + + case 'c': // class_info + Type t = (Type)value; + buf.append("Object ").append(objName). + append(" = Type.getType(\"" + t.getDescriptor() + "\");\n"); + break; + + case '@': // annotation_value + ((Annotation)value).dump(buf, objName); + break; + + case '[': // array_value + AnnotationMemberValue[] v = (AnnotationMemberValue[])value; + buf.append("AnnotationMemberValue[] ").append(objName) + .append(" = new AnnotationMemberValue[") + .append(v.length).append("]\n;"); + buf.append("{\n"); + buf.append("Object av = null;\n"); + for (int i = 0; i < v.length; i++) { + v[i].dump(buf, objName + i); + buf.append(objName) + .append("[").append(i).append("] = ").append(objName + i); + } + buf.append("};\n"); + break; + } + + buf.append("AnnotationMemberValue ").append(valName); + buf.append(" = new AnnotationMemberValue( ").append(objName).append(");\n"); + } + + /** + * Returns value in the format described in JSR-175 for Java source code. + */ + + public String toString () { + StringBuffer sb = new StringBuffer(); + // TODO + int tag = getTag(); + switch (tag) { + case 's': // pointer to CONSTANT_Utf8 + sb.append('"').append(value).append('"'); + break; + + case 'B': // pointer to CONSTANT_Byte + case 'C': // pointer to CONSTANT_Char + case 'D': // pointer to CONSTANT_Double + case 'F': // pointer to CONSTANT_Float + case 'I': // pointer to CONSTANT_Integer + case 'J': // pointer to CONSTANT_Long + case 'S': // pointer to CONSTANT_Short + case 'Z': // pointer to CONSTANT_Boolean + case 'e': // enum_const_value + sb.append(value); + break; + + case 'c': // class_info + // TODO verify if the following is correct + sb.append(value); + break; + + case '@': // annotation_value + // TODO verify if the following is correct + sb.append(value); + break; + + case '[': // array_value + AnnotationMemberValue[] v = (AnnotationMemberValue[])value; + if (v.length > 0) { + sb.append("{ "); + String sep = ""; + for (int i = 0; i < v.length; i++) { + sb.append(sep).append(v[i].toString()); + sep = ", "; + } + sb.append(" }"); + } + break; + } + return sb.toString(); + } + + /** + * Container class used to store enum_const_value structure. + */ + + public static class EnumConstValue { + + public String typeName; + + public String constName; + + public EnumConstValue (String typeName, String constName) { + this.typeName = typeName; + this.constName = constName; + } + + public void write (ByteVector bv, ClassWriter cw) { + // TODO verify the data structures + bv.putShort(cw.newUTF8(typeName)); + bv.putShort(cw.newUTF8(constName)); + } + + // public boolean equals( Object o) { + // EnumConstValue v = ( EnumConstValue) o; + // return v.constName.equals( ) + // } + + public String toString () { + // TODO verify print enum + return typeName + "." + constName; + } + } +} diff --git a/org/objectweb/asm/attrs/Dumpable.java b/org/objectweb/asm/attrs/Dumpable.java new file mode 100644 index 0000000000..ece8b35064 --- /dev/null +++ b/org/objectweb/asm/attrs/Dumpable.java @@ -0,0 +1,61 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import java.util.Map; + +/** + * Dumpable interface has to be implemented by the Attribute class + * in order to support DumpClassVisitor and DumpCodeVisitor. + * + * Implementation should print the ASM code that generates + * attribute data structures for current attribute state. + * + * @author Eugene Kuleshov + */ + +public interface Dumpable { + + /** + * Dump attribute data into ASM code. + * + * @param buf A buffer used for printing java code. + * @param varName name of the variable in a printed code used to store + * attribute instance. + * @param labelNames map of label instances to their names. + */ + + void dump (StringBuffer buf, String varName, Map labelNames); +} diff --git a/org/objectweb/asm/attrs/EnclosingMethodAttribute.java b/org/objectweb/asm/attrs/EnclosingMethodAttribute.java new file mode 100644 index 0000000000..91dc4617df --- /dev/null +++ b/org/objectweb/asm/attrs/EnclosingMethodAttribute.java @@ -0,0 +1,115 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import java.util.Map; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; + + +/** + * The EnclosingMethod attribute is an optional fixed-length attribute + * in the attributes table of the ClassFile structure. A class must + * have an EnclosingMethod attribute if and only if it is a local + * class or an anonymous class. A class may have no more than one + * EnclosingMethod attribute. + *

+ * The EnclosingMethod attribute has the following format: + *

+ *   EnclosingMethod_attribute {
+ *     u2 attribute_name;
+ *     u4 attribute_length;
+ *     u2 method_descriptor_index;
+ *   }
+ * 
+ * The items of the EnclosingMethod_attribute structure are as follows: + *
+ *
attribute_name_index
+ *
The value of the attribute_name_index item must be a valid index + * into the constant_pool table. The constant_pool entry at that index + * must be a CONSTANT_Utf8_info structure representing the string + * "EnclosingMethod".
+ *
attribute_length
+ *
The value of the attribute_length item is zero.
+ *
method_descriptor_index
+ *
The value of the method_descriptor_index item must be a valid + * index into the constant_pool table. The constant_pool entry at that + * index must be a CONSTANT_Utf8_info structure representing a valid + * method descriptor (JLS 4.4.3). It is the responsibility of the + * Java compiler to ensure that the method identified via the + * method_descriptor_index is indeed the closest lexically enclosing + * method of the class that contains this EnclosingMethod attribute.
+ *
+ * + * @author Eugene Kuleshov + */ + +public class EnclosingMethodAttribute extends Attribute implements Dumpable { + + public String methodDescriptor; + + public EnclosingMethodAttribute () { + super("EnclosingMethod"); + } + + public EnclosingMethodAttribute (String methodDescriptor) { + this(); + this.methodDescriptor = methodDescriptor; + } + + protected Attribute read (ClassReader cr, int off, + int len, char[] buf, int codeOff, Label[] labels) { + return new EnclosingMethodAttribute(cr.readUTF8(off, buf)); + } + + protected ByteVector write (ClassWriter cw, byte[] code, + int len, int maxStack, int maxLocals) { + return new ByteVector().putShort(cw.newUTF8(methodDescriptor)); + } + + public void dump (StringBuffer buf, String varName, Map labelNames) { + buf.append("EnclosingMethodAttribute ").append(varName) + .append(" = new EnclosingMethodAttribute(\"") + .append(methodDescriptor).append("\");\n"); + } + + public String toString () { + return methodDescriptor; + } +} diff --git a/org/objectweb/asm/attrs/RuntimeInvisibleAnnotations.java b/org/objectweb/asm/attrs/RuntimeInvisibleAnnotations.java new file mode 100644 index 0000000000..18791cfe47 --- /dev/null +++ b/org/objectweb/asm/attrs/RuntimeInvisibleAnnotations.java @@ -0,0 +1,133 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; + +/** + * The RuntimeInvisibleAnnotations attribute is similar to the + * RuntimeVisibleAnnotations attribute, except that the annotations represented by + * a RuntimeInvisibleAnnotations attribute must not be made available for return + * by reflective APIs, unless the JVM has been instructed to retain these + * annotations via some implementation-specific mechanism such as a command line + * flag. In the absence of such instructions, the JVM ignores this attribute. + *

+ * The RuntimeInvisibleAnnotations attribute is a variable length attribute in the + * attributes table of the ClassFile, field_info, and method_info structures. The + * RuntimeInvisibleAnnotations attribute records runtime-invisible Java + * programming language annotations on the corresponding class, method, or field. + * Each ClassFile, field_info, and method_info structure may contain at most one + * RuntimeInvisibleAnnotations attribute, which records all the runtime-invisible + * Java programming language annotations on the corresponding program element. + *

+ * The RuntimeInvisibleAnnotations attribute has the following format: + *

+ *   RuntimeInvisibleAnnotations_attribute {
+ *     u2 attribute_name_index;
+ *     u4 attribute_length;
+ *     u2 num_annotations;
+ *     annotation annotations[num_annotations];
+ *   }
+ * 
+ * The items of the RuntimeInvisibleAnnotations structure are as follows: + *
+ *
attribute_name_index
+ *
The value of the attribute_name_index item must be a valid index into the + * constant_pool table. The constant_pool entry at that index must be a + * CONSTANT_Utf8_info structure representing the string + * "RuntimeInvisibleAnnotations".
+ *
attribute_length
+ *
The value of the attribute_length item indicates the length of the + * attribute, excluding the initial six bytes. The value of the + * attribute_length item is thus dependent on the number of runtime-invisible + * annotations represented by the structure, and their values.
+ *
num_annotations
+ *
The value of the num_annotations item gives the number of runtime-invisible + * annotations represented by the structure. Note that a maximum of 65535 + * runtime-invisible Java programming language annotations may be directly + * attached to a program element.
+ *
annotations
+ *
Each value of the annotations table represents a single runtime-invisible + * {@link org.objectweb.asm.attrs.Annotation annotation} on a program element.
+ *
+ * + * @see JSR 175 : A Metadata + * Facility for the Java Programming Language + * + * @author Eugene Kuleshov + */ + +public class RuntimeInvisibleAnnotations extends Attribute implements Dumpable { + + public List annotations = new LinkedList(); + + public RuntimeInvisibleAnnotations () { + super("RuntimeInvisibleAnnotations"); + } + + protected Attribute read (ClassReader cr, int off, + int len, char[] buf, int codeOff, Label[] labels) { + RuntimeInvisibleAnnotations atr = new RuntimeInvisibleAnnotations(); + Annotation.readAnnotations(atr.annotations, cr, off, buf); + return atr; + } + + protected ByteVector write (ClassWriter cw, byte[] code, + int len, int maxStack, int maxLocals) { + return Annotation.writeAnnotations(new ByteVector(), annotations, cw); + } + + public void dump (StringBuffer buf, String varName, Map labelNames) { + buf.append("RuntimeInvisibleAnnotations ").append(varName) + .append(" = new RuntimeInvisibleAnnotations();\n"); + Annotation.dumpAnnotations(buf, varName, annotations); + } + + /** + * Returns value in the format described in JSR-175 for Java source code. + */ + + public String toString () { + return Annotation.stringAnnotations(annotations); + } +} diff --git a/org/objectweb/asm/attrs/RuntimeInvisibleParameterAnnotations.java b/org/objectweb/asm/attrs/RuntimeInvisibleParameterAnnotations.java new file mode 100644 index 0000000000..1a0a002ad4 --- /dev/null +++ b/org/objectweb/asm/attrs/RuntimeInvisibleParameterAnnotations.java @@ -0,0 +1,152 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; + + +/** + * The RuntimeInvisibleParameterAnnotations attribute is similar to the + * RuntimeVisibleParameterAnnotations attribute, except that the annotations + * represented by a RuntimeInvisibleParameterAnnotations attribute must not be + * made available for return by reflective APIs, unless the JVM has specifically + * been instructed to retain these annotations via some implementation-specific + * mechanism such as a command line flag. In the absence of such instructions, the + * JVM ignores this attribute. + *

+ * The RuntimeInvisibleParameterAnnotations attribute is a variable length + * attribute in the attributes table of the method_info structure. The + * RuntimeInvisibleParameterAnnotations attribute records runtime-invisible Java + * programming language annotations on the parameters of the corresponding method. + * Each method_info structure may contain at most one + * RuntimeInvisibleParameterAnnotations attribute, which records all the + * runtime-invisible Java programming language annotations on the parameters of + * the corresponding method. + *

+ * The RuntimeInvisibleParameterAnnotations attribute has the following format: + *

+ *   RuntimeInvisibleParameterAnnotations_attribute {
+ *     u2 attribute_name_index;
+ *     u4 attribute_length;
+ *     u1 num_parameters;
+ *     {
+ *       u2 num_annotations;
+ *       annotation annotations[num_annotations];
+ *     } parameter_annotations[num_parameters];
+ *   }
+ * 
+ * The items of the RuntimeInvisibleParameterAnnotations structure are as follows: + *
+ *
attribute_name_index
+ *
The value of the attribute_name_index item must be a valid index into the + * constant_pool table. The constant_pool entry at that index must be a + * CONSTANT_Utf8_info structure representing the string + * "RuntimeInvisibleParameterAnnotations".
+ *
attribute_length
+ *
The value of the attribute_length item indicates the length of the attribute, + * excluding the initial six bytes. The value of the attribute_length item is + * thus dependent on the number of parameters, the number of runtime-invisible + * annotations on each parameter, and their values.
+ *
num_parameters
+ *
The value of the num_parameters item gives the number of parameters of the + * method represented by the method_info structure on which the annotation + * occurs. (This duplicates information that could be extracted from the method + * descriptor.)
+ *
parameter_annotations
+ *
Each value of the parameter_annotations table represents all of the + * runtime-invisible annotations on a single parameter. The sequence of values + * in the table corresponds to the sequence of parameters in the method + * signature. Each parameter_annotations entry contains the following two items: + *
+ *
num_annotations
+ *
The value of the num_annotations item indicates the number of + * runtime-invisible annotations on the parameter corresponding to the sequence + * number of this parameter_annotations element.
+ *
annotations
+ *
Each value of the annotations table represents a single runtime-invisible + * {@link Annotation annotation} on the parameter corresponding to the sequence + * number of this parameter_annotations element.
+ *
+ *
+ *
+ * + * @see JSR 175 : A Metadata + * Facility for the Java Programming Language + * + * @author Eugene Kuleshov + */ + +public class RuntimeInvisibleParameterAnnotations + extends Attribute implements Dumpable +{ + + public List parameters = new LinkedList(); + + public RuntimeInvisibleParameterAnnotations () { + super("RuntimeInvisibleParameterAnnotations"); + } + + protected Attribute read (ClassReader cr, int off, + int len, char[] buf, int codeOff, Label[] labels) { + RuntimeInvisibleParameterAnnotations atr = + new RuntimeInvisibleParameterAnnotations(); + Annotation.readParameterAnnotations(atr.parameters, cr, off, buf); + return atr; + } + + protected ByteVector write (ClassWriter cw, byte[] code, + int len, int maxStack, int maxLocals) { + return Annotation.writeParametersAnnotations( + new ByteVector(), parameters, cw); + } + + public void dump (StringBuffer buf, String varName, Map labelNames) { + buf.append("RuntimeInvisibleParameterAnnotations ").append(varName) + .append(" = new RuntimeInvisibleParameterAnnotations();\n"); + Annotation.dumpParameterAnnotations(buf, varName, parameters); + } + + public String toString () { + return Annotation.stringParameterAnnotations(parameters); + } +} diff --git a/org/objectweb/asm/attrs/RuntimeVisibleAnnotations.java b/org/objectweb/asm/attrs/RuntimeVisibleAnnotations.java new file mode 100644 index 0000000000..4a4c249991 --- /dev/null +++ b/org/objectweb/asm/attrs/RuntimeVisibleAnnotations.java @@ -0,0 +1,129 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; + + +/** + * The RuntimeVisibleAnnotations attribute is a variable length attribute in the + * attributes table of the ClassFile, field_info, and method_info structures. The + * RuntimeVisibleAnnotations attribute records runtime-visible Java programming + * language annotations on the corresponding class, method, or field. Each + * ClassFile, field_info, and method_info structure may contain at most one + * RuntimeVisibleAnnotations attribute, which records all the runtime-visible Java + * programming language annotations on the corresponding program element. The JVM + * must make these annotations available so they can be returned by the + * appropriate reflective APIs. + *

+ * The RuntimeVisibleAnnotations attribute has the following format: + *

+ *   RuntimeVisibleAnnotations_attribute {
+ *     u2 attribute_name_index;
+ *     u4 attribute_length;
+ *     u2 num_annotations;
+ *     annotation annotations[num_annotations];
+ *   }
+ * 
+ * The items of the RuntimeVisibleAnnotations structure are as follows: + *
+ *
attribute_name_index
+ *
The value of the attribute_name_index item must be a valid index into the + * constant_pool table. The constant_pool entry at that index must be a + * CONSTANT_Utf8_info structure representing the string + * "RuntimeVisibleAnnotations".
+ *
attribute_length
+ *
The value of the attribute_length item indicates the length of the attribute, + * excluding the initial six bytes. The value of the attribute_length item is + * thus dependent on the number of runtime-visible annotations represented by + * the structure, and their values.
+ *
num_annotations
+ *
The value of the num_annotations item gives the number of runtime-visible + * annotations represented by the structure. Note that a maximum of 65535 + * runtime-visible Java programming language annotations may be directly + * attached to a program element.
+ *
annotations
+ *
Each value of the annotations table represents a single runtime-visible + * {@link Annotation annotation} on a program element.
+ *
+ * + * @see JSR 175 : A Metadata + * Facility for the Java Programming Language + * + * @author Eugene Kuleshov + */ + +public class RuntimeVisibleAnnotations extends Attribute implements Dumpable { + + public List annotations = new LinkedList(); + + public RuntimeVisibleAnnotations () { + super("RuntimeVisibleAnnotations"); + } + + protected Attribute read (ClassReader cr, int off, + int len, char[] buf, int codeOff, Label[] labels) { + RuntimeVisibleAnnotations atr = new RuntimeVisibleAnnotations(); + Annotation.readAnnotations(atr.annotations, cr, off, buf); + return atr; + } + + protected ByteVector write (ClassWriter cw, byte[] code, + int len, int maxStack, int maxLocals) { + return Annotation.writeAnnotations(new ByteVector(), annotations, cw); + } + + public void dump (StringBuffer buf, String varName, Map labelNames) { + buf.append("RuntimeVisibleAnnotations ").append(varName) + .append(" = new RuntimeVisibleAnnotations();\n"); + Annotation.dumpAnnotations(buf, varName, annotations); + } + + /** + * Returns value in the format described in JSR-175 for Java source code. + */ + + public String toString () { + return Annotation.stringAnnotations(annotations); + } +} diff --git a/org/objectweb/asm/attrs/RuntimeVisibleParameterAnnotations.java b/org/objectweb/asm/attrs/RuntimeVisibleParameterAnnotations.java new file mode 100644 index 0000000000..d56b300d91 --- /dev/null +++ b/org/objectweb/asm/attrs/RuntimeVisibleParameterAnnotations.java @@ -0,0 +1,144 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; + +/** + * The RuntimeVisibleParameterAnnotations attribute is a variable length attribute + * in the attributes table of the method_info structure. The + * RuntimeVisibleParameterAnnotations attribute records runtime-visible Java + * programming language annotations on the parameters of the corresponding method. + * Each method_info structure may contain at most one + * RuntimeVisibleParameterAnnotations attribute, which records all the + * runtime-visible Java programming language annotations on the parameters of the + * corresponding method. The JVM must make these annotations available so they can + * be returned by the appropriate reflective APIs. + *

+ * The RuntimeVisibleParameterAnnotations attribute has the following format: + *

+ *   RuntimeVisibleParameterAnnotations_attribute {
+ *     u2 attribute_name_index;
+ *     u4 attribute_length;
+ *     u1 num_parameters;
+ *     {
+ *       u2 num_annotations;
+ *       annotation annotations[num_annotations];
+ *     } parameter_annotations[num_parameters];
+ *   }
+ * 
+ * The items of the RuntimeVisibleParameterAnnotations structure are as follows:
+ * 
+ *
attribute_name_index
+ *
The value of the attribute_name_index item must be a valid index into the + * constant_pool table. The constant_pool entry at that index must be a + * CONSTANT_Utf8_info structure representing the string + * "RuntimeVisibleParameterAnnotations".
+ *
attribute_length
+ *
The value of the attribute_length item indicates the length of the attribute, + * excluding the initial six bytes. The value of the attribute_length item is + * thus dependent on the number of parameters, the number of runtime-visible + * annotations on each parameter, and their values.
+ *
num_parameters
+ *
The value of the num_parameters item gives the number of parameters of the + * method represented by the method_info structure on which the annotation + * occurs. (This duplicates information that could be extracted from the method + * descriptor.)
+ *
parameter_annotations
+ *
Each value of the parameter_annotations table represents all of the + * runtime-visible annotations on a single parameter. The sequence of values in + * the table corresponds to the sequence of parameters in the method signature. + * Each parameter_annotations entry contains the following two items:
+ *
+ *
num_annotations
+ *
The value of the num_annotations item indicates the number of runtime-visible + * annotations on the parameter corresponding to the sequence number of this + * parameter_annotations element.
+ *
annotations
+ *
Each value of the annotations table represents a single runtime-visible + * {@link org.objectweb.asm.attrs.Annotation annotation} on the parameter + * corresponding to the sequence number of this parameter_annotations element.
+ *
+ * + *
+ * + * @see JSR 175 : A Metadata + * Facility for the Java Programming Language + * + * @author Eugene Kuleshov + */ + +public class RuntimeVisibleParameterAnnotations + extends Attribute implements Dumpable +{ + + public List parameters = new LinkedList(); + + public RuntimeVisibleParameterAnnotations () { + super("RuntimeVisibleParameterAnnotations"); + } + + protected Attribute read (ClassReader cr, int off, + int len, char[] buf, int codeOff, Label[] labels) { + RuntimeInvisibleParameterAnnotations atr = + new RuntimeInvisibleParameterAnnotations(); + Annotation.readParameterAnnotations(atr.parameters, cr, off, buf); + return atr; + } + + protected ByteVector write (ClassWriter cw, byte[] code, + int len, int maxStack, int maxLocals) { + return Annotation.writeParametersAnnotations( + new ByteVector(), parameters, cw); + } + + public void dump (StringBuffer buf, String varName, Map labelNames) { + buf.append("RuntimeVisibleParameterAnnotations ").append(varName) + .append(" = new RuntimeVisibleParameterAnnotations();\n"); + Annotation.dumpParameterAnnotations(buf, varName, parameters); + } + + public String toString () { + return Annotation.stringParameterAnnotations(parameters); + } +} diff --git a/org/objectweb/asm/attrs/SignatureAttribute.java b/org/objectweb/asm/attrs/SignatureAttribute.java new file mode 100644 index 0000000000..3a248c6a92 --- /dev/null +++ b/org/objectweb/asm/attrs/SignatureAttribute.java @@ -0,0 +1,129 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import java.util.Map; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; + +/** + * The Signature Attribute introduced in JSR-14 (Adding Generics to the + * Java Programming Language) and also defined in the Java Virtual Machine + * Specification, 3rd edition draft. This atribute is used for classes, + * fields and methods. + *

+ * Classfiles need to carry generic type information in a backwards + * compatible way. This is accomplished by introducing a new "Signature" + * attribute for classes, methods and fields. The structure of this + * attribute is as follows: + *

+ *   "Signature" (u4 attr-length, u2 signature-index)
+ * 
+ * When used as an attribute of a method or field, a signature gives the + * full (possibly generic) type of that method or field. + * When used as a class attribute, a signature indicates the type + * parameters of the class, followed by its supertype, followed by + * all its interfaces. + *

+ * The type syntax in signatures is extended to parameterized types and + * type variables. There is also a new signature syntax for formal type + * parameters. The syntax extensions for signature strings are as follows: + *

+ *   MethodOrFieldSignature ::= TypeSignature
+ *   ClassSignature        ::= ParameterPartOpt super_TypeSignature interface_TypeSignatures
+ *   TypeSignatures        ::= TypeSignatures TypeSignature
+ *                             |
+ *   TypeSignature         ::= ...
+ *                             | ClassTypeSignature
+ *                             | MethodTypeSignature
+ *                             | TypeVariableSignature
+ *   ClassTypeSignature    ::= 'L' Ident TypeArgumentsOpt ';'
+ *                             | ClassTypeSignature '.' Ident ';' TypeArgumentsOpt
+ *   MethodTypeSignature   ::= TypeArgumentsOpt '(' TypeSignatures ')'
+ *                             TypeSignature ThrowsSignatureListOpt
+ *   ThrowsSignatureList   ::= ThrowsSignature ThrowsSignatureList
+ *                             | ThrowsSignature
+ *   ThrowsSignature       ::= '^' TypeSignature
+ *   TypeVariableSignature ::= 'T' Ident ';'
+ *   TypeArguments         ::= '<' TypeSignature TypeSignatures '>'
+ *   ParameterPart         ::= '<' ParameterSignature ParameterSignatures '>'
+ *   ParameterSignatures   ::= ParameterSignatures ParameterSignature
+ *                             |
+ *   ParameterSignature ::= Ident ':' bound_TypeSignature
+ * 
+ * + * @see JSR 14 : Add Generic + * Types To The JavaTM Programming Language + * + * @author Eugene Kuleshov + */ + +public class SignatureAttribute extends Attribute implements Dumpable { + + public String signature; + + public SignatureAttribute () { + super("Signature"); + } + + public SignatureAttribute (String signature) { + this(); + this.signature = signature; + } + + protected Attribute read (ClassReader cr, int off, + int len, char[] buf, int codeOff, Label[] labels) { + return new SignatureAttribute(cr.readUTF8(off, buf)); + } + + protected ByteVector write (ClassWriter cw, byte[] code, + int len, int maxStack, int maxLocals) { + return new ByteVector().putShort(cw.newUTF8(signature)); + } + + public void dump (StringBuffer buf, String varName, Map labelNames) { + buf.append("SignatureAttribute ").append(varName) + .append(" = new SignatureAttribute(\"") + .append(signature).append("\");\n"); + } + + public String toString () { + return signature; + } +} diff --git a/org/objectweb/asm/attrs/SourceDebugExtensionAttribute.java b/org/objectweb/asm/attrs/SourceDebugExtensionAttribute.java new file mode 100644 index 0000000000..487872933d --- /dev/null +++ b/org/objectweb/asm/attrs/SourceDebugExtensionAttribute.java @@ -0,0 +1,189 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; + +import java.util.Map; + +/** + * The SourceDebugExtension attribute is an optional attribute defined in JSR-045 + * in the attributes table of the ClassFile structure. There can be no more than one + * SourceDebugExtension attribute in the attributes table of a given ClassFile + * structure. The SourceDebugExtension attribute has the following format: + *
+ *   SourceDebugExtension_attribute {
+ *     u2 attribute_name_index;
+ *     u4 attribute_length;
+ *     u1 debug_extension[attribute_length];
+ *   }
+ * 
+ * The items of the SourceDebugExtension_attribute structure are as follows: + *
+ *
attribute_name_index
+ *
The value of the attribute_name_index item must be a valid index into the + * constant_pool table. The constant_pool entry at that index must be a + * CONSTANT_Utf8_info structure representing the string "SourceDebugExtension".
+ *
attribute_length
+ *
The value of the attribute_length item indicates the length of + * the attribute, excluding the initial six bytes. The value of the + * attribute_length item is thus the number of bytes in the debug_extension[] + * item.
+ *
debug_extension[]
+ *
The debug_extension array holds a string, which must be in UTF-8 format. + * There is no terminating zero byte. The string in the debug_extension item + * will be interpreted as extended debugging information. The content of this + * string has no semantic effect on the Java Virtual Machine.
+ *
+ * + * @see JSR-045: Debugging + * Support for Other Languages + * + * @author Eugene Kuleshov + */ + +public class SourceDebugExtensionAttribute + extends Attribute implements Dumpable + { + + public String debugExtension; + + public SourceDebugExtensionAttribute () { + super("SourceDebugExtension"); + } + + public SourceDebugExtensionAttribute (String debugExtension) { + this(); + this.debugExtension = debugExtension; + } + + protected Attribute read (ClassReader cr, int off, + int len, char[] buf, int codeOff, Label[] labels) { + return new SourceDebugExtensionAttribute(readUTF8(cr, off, len)); + } + + protected ByteVector write (ClassWriter cw, byte[] code, + int len, int maxStack, int maxLocals) { + byte[] b = putUTF8(debugExtension); + return new ByteVector().putByteArray(b, 0, b.length); + } + + public void dump (StringBuffer buf, String varName, Map labelNames) { + buf.append("SourceDebugExtensionAttribute ").append(varName) + .append(" = new SourceDebugExtensionAttribute(\"") + .append(debugExtension).append("\");\n"); + } + + private String readUTF8 (ClassReader cr, int index, int utfLen) { + int endIndex = index + utfLen; + byte[] b = cr.b; + char[] buf = new char[utfLen]; + int strLen = 0; + int c, d, e; + while (index < endIndex) { + c = b[index++] & 0xFF; + switch (c >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + buf[strLen++] = (char)c; + break; + + case 12: + case 13: + // 110x xxxx 10xx xxxx + d = b[index++]; + buf[strLen++] = (char)(((c & 0x1F) << 6) | (d & 0x3F)); + break; + + default: + // 1110 xxxx 10xx xxxx 10xx xxxx + d = b[index++]; + e = b[index++]; + buf[strLen++] = (char)(((c & 0x0F) << 12) | ((d & 0x3F) << 6) | (e & 0x3F)); + break; + } + } + + return new String(buf, 0, strLen); + } + + private byte[] putUTF8 (String s) { + int charLength = s.length(); + int byteLength = 0; + for (int i = 0; i < charLength; ++i) { + char c = s.charAt(i); + if (c >= '\001' && c <= '\177') { + byteLength++; + } else if (c > '\u07FF') { + byteLength += 3; + } else { + byteLength += 2; + } + } + /*if (byteLength > 65535) { + throw new IllegalArgumentException(); + }*/ + byte[] data = new byte[byteLength]; + for (int i = 0; i < charLength;) { + char c = s.charAt(i); + if (c >= '\001' && c <= '\177') { + data[i++] = (byte)c; + } else if (c > '\u07FF') { + data[i++] = (byte)(0xE0 | c >> 12 & 0xF); + data[i++] = (byte)(0x80 | c >> 6 & 0x3F); + data[i++] = (byte)(0x80 | c & 0x3F); + } else { + data[i++] = (byte)(0xC0 | c >> 6 & 0x1F); + data[i++] = (byte)(0x80 | c & 0x3F); + } + } + return data; + } + + public String toString () { + return debugExtension; + } +} diff --git a/org/objectweb/asm/attrs/StackMapAttribute.java b/org/objectweb/asm/attrs/StackMapAttribute.java new file mode 100644 index 0000000000..471262de35 --- /dev/null +++ b/org/objectweb/asm/attrs/StackMapAttribute.java @@ -0,0 +1,232 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; + +/** + * StackMapAttribute is used by CDLC preverifier and also by javac compiller + * starting from J2SE 1.5. Definition is given in appendix "CLDC Byte Code + * Typechecker Specification" from CDLC 1.1 specification. + *

+ * Note that this implementation does not calculate StackMapFrame structures + * from the method bytecode. If method code is changed or generated from scratch, + * then developer is responsible to prepare a correct StackMapFrame structures. + *

+ * The format of the stack map in the class file is given below. In the following, + *

    + *
  • if the length of the method's byte code1 is 65535 or less, then uoffset + * represents the type u2; otherwise uoffset represents the type u4.
  • + *
  • If the maximum number of local variables for the method is 65535 or less, + * then ulocalvar represents the type u2; otherwise ulocalvar + * represents the type u4.
  • + *
  • If the maximum size of the operand stack is 65535 or less, then ustack + * represents the type u2; otherwise ustack represents the type u4.
  • + *
+ * + *
+ *   stack_map { // attribute StackMap
+ *     u2 attribute_name_index;
+ *     u4 attribute_length
+ *     uoffset number_of_entries;
+ *     stack_map_frame entries[number_of_entries];
+ *   }
+ * 
+ * Each stack map frame has the following format: + *
+ *   stack_map_frame {
+ *     uoffset offset;
+ *     ulocalvar number_of_locals;
+ *     verification_type_info locals[number_of_locals];
+ *     ustack number_of_stack_items;
+ *     verification_type_info stack[number_of_stack_items];
+ *   }
+ * 
+ * The verification_type_info structure consists of a one-byte tag + * followed by zero or more bytes, giving more information about the tag. + * Each verification_type_info structure specifies the verification + * type of one or two locations. + *
+ *   union verification_type_info {
+ *     Top_variable_info;
+ *     Integer_variable_info;
+ *     Float_variable_info;
+ *     Long_variable_info;
+ *     Double_variable_info;
+ *     Null_variable_info;
+ *     UninitializedThis_variable_info;
+ *     Object_variable_info;
+ *     Uninitialized_variable_info;
+ *   }
+ *
+ *   Top_variable_info {
+ *     u1 tag = ITEM_Top; // 0
+ *   }
+ *
+ *   Integer_variable_info {
+ *     u1 tag = ITEM_Integer; // 1
+ *   }
+ *
+ *   Float_variable_info {
+ *     u1 tag = ITEM_Float; // 2
+ *   }
+ *
+ *   Long_variable_info {
+ *     u1 tag = ITEM_Long; // 4
+ *   }
+ *
+ *   Double_variable_info {
+ *     u1 tag = ITEM_Double; // 3
+ *   }
+ *
+ *   Null_variable_info {
+ *     u1 tag = ITEM_Null; // 5
+ *   }
+ *
+ *   UninitializedThis_variable_info {
+ *     u1 tag = ITEM_UninitializedThis; // 6
+ *   }
+ *
+ *   Object_variable_info {
+ *     u1 tag = ITEM_Object; // 7
+ *     u2 cpool_index;
+ *   }
+ *
+ *   Uninitialized_variable_info {
+ *     u1 tag = ITEM_Uninitialized // 8
+ *     uoffset offset;
+ *   }
+ * 
+ * + * @see JSR 139 : Connected + * Limited Device Configuration 1.1 + * + * @author Eugene Kuleshov + */ + +public class StackMapAttribute extends Attribute implements Dumpable { + + static final int MAX_SIZE = 65535; + + public LinkedList frames = new LinkedList(); + + public StackMapAttribute () { + super("StackMap"); + } + + public StackMapFrame getFrame (Label label) { + for (int i = 0; i < frames.size(); i++) { + StackMapFrame frame = (StackMapFrame)frames.get(i); + if (frame.label == label) { + return frame; + } + } + return null; + } + + protected Attribute read (ClassReader cr, int off, int len, + char[] buf, int codeOff, Label[] labels) { + StackMapAttribute attr = new StackMapAttribute(); + // note that this is not the size of Code attribute + int codeSize = cr.readInt(codeOff + 4); + int size = 0; + if (codeSize > MAX_SIZE) { + size = cr.readInt(off); + off += 4; + } else { + size = cr.readShort(off); + off += 2; + } + for (int i = 0; i < size; i++) { + StackMapFrame frame = new StackMapFrame(); + off = frame.read(cr, off, buf, codeOff, labels); + attr.frames.add(frame); + } + return attr; + } + + protected ByteVector write (ClassWriter cw, byte[] code, + int len, int maxStack, int maxLocals) { + ByteVector bv = new ByteVector(); + if (code.length > MAX_SIZE) { + bv.putInt(frames.size()); + } else { + bv.putShort(frames.size()); + } + for (int i = 0; i < frames.size(); i++) { + ((StackMapFrame)frames.get(i)).write(cw, maxStack, maxLocals, bv); + } + return bv; + } + + protected Label[] getLabels () { + HashSet labels = new HashSet(); + for (int i = 0; i < frames.size(); i++) { + ((StackMapFrame)frames.get(i)).getLabels(labels); + } + return (Label[])labels.toArray(new Label[labels.size()]); + } + + public void dump (StringBuffer buf, String varName, Map labelNames) { + buf.append("{\n"); + buf.append("StackMapAttribute ").append(varName).append("Attr"); + buf.append(" = new StackMapAttribute();\n"); + if (frames.size() > 0) { + for (int i = 0; i < frames.size(); i++) { + ((StackMapFrame)frames.get(i)) + .dump(buf, varName + "frame" + i, labelNames); + } + } + buf.append(varName).append(".visitAttribute(").append(varName); + buf.append("Attr);\n}\n"); + } + + public String toString () { + StringBuffer sb = new StringBuffer("StackMap["); + for (int i = 0; i < frames.size(); i++) { + sb.append('\n').append('[').append(frames.get(i)).append(']'); + } + sb.append("\n]"); + return sb.toString(); + } +} diff --git a/org/objectweb/asm/attrs/StackMapFrame.java b/org/objectweb/asm/attrs/StackMapFrame.java new file mode 100644 index 0000000000..28197a0259 --- /dev/null +++ b/org/objectweb/asm/attrs/StackMapFrame.java @@ -0,0 +1,227 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; + +/** + * StackMapFrame is used by {@link StackMapAttribute} to hold state of the stack + * and local variables for a single execution branch. + * + * Note that Long and Double types are represented by two entries in locals + * and stack. Second entry sohould be always of type Top. + * + * @see JSR 139 : Connected + * Limited Device Configuration 1.1 + * + * @author Eugene Kuleshov + */ + +public class StackMapFrame { + + public Label label; + + public List locals = new LinkedList(); + + public List stack = new LinkedList(); + + public int read (ClassReader cr, + int off, char[] buf, int codeOff, Label[] labels) { + int n = cr.readUnsignedShort(off); + off += 2; + if (labels[n] == null) { + labels[n] = new Label(); + } + label = labels[n]; + off = readTypeInfo(cr, off, locals, labels, buf, + cr.readUnsignedShort(codeOff + 2)); // maxLocals + off = readTypeInfo(cr, off, stack, labels, buf, + cr.readUnsignedShort(codeOff)); // maxStack + return off; + } + + public void write (ClassWriter cw, + int maxStack, int maxLocals, ByteVector bv) { + bv.putShort(label.getOffset()); + writeTypeInfo(bv, cw, locals, maxLocals); + writeTypeInfo(bv, cw, stack, maxStack); + } + + public void getLabels (Set labels) { + labels.add(label); + getTypeInfoLabels(labels, locals); + getTypeInfoLabels(labels, stack); + } + + private void getTypeInfoLabels (Set labels, List info) { + for (Iterator it = info.iterator(); it.hasNext();) { + StackMapType typeInfo = (StackMapType)it.next(); + if (typeInfo.getType() == StackMapType.ITEM_Uninitialized) { + labels.add(typeInfo.getLabel()); + } + } + } + + private int readTypeInfo (ClassReader cr, int off, + List info, Label[] labels, char[] buf, int max) { + int n = 0; + if (max > StackMapAttribute.MAX_SIZE) { + n = cr.readInt(off); + off += 4; + } else { + n = cr.readUnsignedShort(off); + off += 2; + } + for (int j = 0; j < n; j++) { + int itemType = cr.readByte(off++); + StackMapType typeInfo = StackMapType.getTypeInfo(itemType); + info.add(typeInfo); + switch (itemType) { + case StackMapType.ITEM_Long: // + case StackMapType.ITEM_Double: // + info.add(StackMapType.getTypeInfo(StackMapType.ITEM_Top)); + break; + + case StackMapType.ITEM_Object: // + typeInfo.setObject(cr.readClass(off, buf)); + off += 2; + break; + + case StackMapType.ITEM_Uninitialized: // + int o = cr.readUnsignedShort(off); + off += 2; + if (labels[o] == null) { + labels[o] = new Label(); + } + typeInfo.setLabel(labels[o]); + break; + } + } + return off; + } + + private void writeTypeInfo (ByteVector bv, + ClassWriter cw, List info, int max) { + if (max > StackMapAttribute.MAX_SIZE) { + bv.putInt(info.size()); + } else { + bv.putShort(info.size()); + } + for (int j = 0; j < info.size(); j++) { + StackMapType typeInfo = (StackMapType)info.get(j); + bv = new ByteVector().putByte(typeInfo.getType()); + switch (typeInfo.getType()) { + case StackMapType.ITEM_Long: // + case StackMapType.ITEM_Double: // + // skip Top in the stack/locals after long/double + j++; + break; + + case StackMapType.ITEM_Object: // + bv.putShort(cw.newClass(typeInfo.getObject())); + break; + + case StackMapType.ITEM_Uninitialized: // + bv.putShort(typeInfo.getLabel().getOffset()); + break; + } + } + } + + public void dump (StringBuffer buf, String varName, Map labelNames) { + declareLabel(buf, labelNames, label); + buf.append(varName).append(".label = ") + .append(labelNames.get(label)).append(";\n"); + + dumpTypeInfo(buf, varName, labelNames, locals); + dumpTypeInfo(buf, varName, labelNames, stack); + } + + private void dumpTypeInfo (StringBuffer buf, + String varName, Map labelNames, List infos) { + if (infos.size() > 0) { + buf.append("{\n"); + for (int i = 0; i < infos.size(); i++) { + StackMapType typeInfo = (StackMapType)infos.get(i); + String localName = varName + "Info" + i; + int type = typeInfo.getType(); + buf.append("StackMapType ").append(localName) + .append(" = new StackMapType( StackMapType.ITEM_") + .append(StackMapType.ITEM_NAMES[type]).append(");\n"); + + switch (type) { + case StackMapType.ITEM_Object: // + buf.append(localName).append(".setObject(") + .append(typeInfo.getObject()).append(");\n"); + break; + + case StackMapType.ITEM_Uninitialized: // + declareLabel(buf, labelNames, typeInfo.getLabel()); + buf.append(localName).append(".setLabel(") + .append(labelNames.get(typeInfo.getLabel())).append(");\n"); + break; + } + buf.append(varName).append(".add(").append(localName).append(");\n"); + } + buf.append("}\n"); + } + } + + public static void declareLabel (StringBuffer buf, Map labelNames, Label l) { + String name = (String)labelNames.get(l); + if (name == null) { + name = "l" + labelNames.size(); + labelNames.put(l, name); + buf.append("Label ").append(name).append(" = new Label();\n"); + } + } + + public String toString () { + StringBuffer sb = new StringBuffer("Frame:L"); + sb.append(System.identityHashCode(label)); + sb.append(" locals").append(locals); + sb.append(" stack").append(stack); + return sb.toString(); + } +} diff --git a/org/objectweb/asm/attrs/StackMapType.java b/org/objectweb/asm/attrs/StackMapType.java new file mode 100644 index 0000000000..78538ade07 --- /dev/null +++ b/org/objectweb/asm/attrs/StackMapType.java @@ -0,0 +1,124 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.attrs; + +import org.objectweb.asm.Label; + +/** + * Verification type info used by {@link StackMapAttribute}. + * + * @see JSR 139 : Connected + * Limited Device Configuration 1.1 + * + * @author Eugene Kuleshov + */ + +public class StackMapType { + + public static final int ITEM_Top = 0; + + public static final int ITEM_Integer = 1; + + public static final int ITEM_Float = 2; + + public static final int ITEM_Double = 3; + + public static final int ITEM_Long = 4; + + public static final int ITEM_Null = 5; + + public static final int ITEM_UninitializedThis = 6; + + public static final int ITEM_Object = 7; + + public static final int ITEM_Uninitialized = 8; + + public static final String[] ITEM_NAMES = { + "Top", + "Integer", + "Float", + "Double", + "Long", + "Null", + "UninitializedThis", + "Object", + "Uninitialized" + }; + + private int type; + + private Label offset; + + private String object; + + private StackMapType (int type) { + this.type = type; + } + + public int getType () { + return type; + } + + public static StackMapType getTypeInfo (int itemType) { + return new StackMapType(itemType); + } + + public void setLabel (Label offset) { + this.offset = offset; + } + + public void setObject (String object) { + this.object = object; + } + + public Label getLabel () { + return offset; + } + + public String getObject () { + return object; + } + + public String toString () { + StringBuffer sb = new StringBuffer(ITEM_NAMES[type]); + if (type == ITEM_Object) { + sb.append(":").append(object); + } + if (type == ITEM_Uninitialized) { + sb.append(":L").append(System.identityHashCode(offset)); + } + return sb.toString(); + } +} diff --git a/org/objectweb/asm/tree/AbstractInsnNode.java b/org/objectweb/asm/tree/AbstractInsnNode.java new file mode 100644 index 0000000000..72439ff3f9 --- /dev/null +++ b/org/objectweb/asm/tree/AbstractInsnNode.java @@ -0,0 +1,78 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.CodeVisitor; + +/** + * A node that represents a bytecode instruction. + */ + +public abstract class AbstractInsnNode { + + /** + * The opcode of this instruction. + */ + + protected int opcode; + + /** + * Constructs a new {@link AbstractInsnNode AbstractInsnNode} object. + * + * @param opcode the opcode of the instruction to be constructed. + */ + + protected AbstractInsnNode (final int opcode) { + this.opcode = opcode; + } + + /** + * Returns the opcode of this instruction. + * + * @return the opcode of this instruction. + */ + + public int getOpcode () { + return opcode; + } + + /** + * Makes the given code visitor visit this instruction. + * + * @param cv a code visitor. + */ + + public abstract void accept (final CodeVisitor cv); +} diff --git a/org/objectweb/asm/tree/ClassNode.java b/org/objectweb/asm/tree/ClassNode.java new file mode 100644 index 0000000000..4f5ca90a99 --- /dev/null +++ b/org/objectweb/asm/tree/ClassNode.java @@ -0,0 +1,186 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Attribute; + +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * A node that represents a class. + */ + +public class ClassNode { + + /** + * The class's access flags (see {@link org.objectweb.asm.Constants}). This + * field also indicates if the class is deprecated. + */ + + public int access; + + /** + * The internal name of the class (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). + */ + + public String name; + + /** + * The internal of name of the super class (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). For interfaces, + * the super class is {@link Object}. May be null, but only for the + * {@link Object java.lang.Object} class. + */ + + public String superName; + + /** + * The internal names of the class's interfaces (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). This list is a + * list of {@link String} objects. + */ + + public final List interfaces; + + /** + * The name of the source file from which this class was compiled. May be + * null. + */ + + public String sourceFile; + + /** + * Informations about the inner classes of this class. This list is a list of + * {@link InnerClassNode InnerClassNode} objects. + */ + + public final List innerClasses; + + /** + * The fields of this class. This list is a list of {@link FieldNode + * FieldNode} objects. + */ + + public final List fields; + + /** + * The methods of this class. This list is a list of {@link MethodNode + * MethodNode} objects. + */ + + public final List methods; + + /** + * The non standard attributes of the class. + */ + + public Attribute attrs; + + /** + * Constructs a new {@link ClassNode ClassNode} object. + * + * @param access the class's access flags (see {@link + * org.objectweb.asm.Constants}). This parameter also indicates if the + * class is deprecated. + * @param name the internal name of the class (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). + * @param superName the internal of name of the super class (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). For + * interfaces, the super class is {@link Object}. + * @param interfaces the internal names of the class's interfaces (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). May be + * null. + * @param sourceFile the name of the source file from which this class was + * compiled. May be null. + */ + + public ClassNode ( + final int access, + final String name, + final String superName, + final String[] interfaces, + final String sourceFile) + { + this.access = access; + this.name = name; + this.superName = superName; + this.interfaces = new ArrayList(); + this.sourceFile = sourceFile; + this.innerClasses = new ArrayList(); + this.fields = new ArrayList(); + this.methods = new ArrayList(); + if (interfaces != null) { + this.interfaces.addAll(Arrays.asList(interfaces)); + } + } + + /** + * Makes the given class visitor visit this class. + * + * @param cv a class visitor. + */ + + public void accept (final ClassVisitor cv) { + // visits header + String[] interfaces = new String[this.interfaces.size()]; + this.interfaces.toArray(interfaces); + cv.visit(access, name, superName, interfaces, sourceFile); + // visits inner classes + int i; + for (i = 0; i < innerClasses.size(); ++i) { + ((InnerClassNode)innerClasses.get(i)).accept(cv); + } + // visits fields + for (i = 0; i < fields.size(); ++i) { + ((FieldNode)fields.get(i)).accept(cv); + } + // visits methods + for (i = 0; i < methods.size(); ++i) { + ((MethodNode)methods.get(i)).accept(cv); + } + // visits attributes + Attribute attrs = this.attrs; + while (attrs != null) { + cv.visitAttribute(attrs); + attrs = attrs.next; + } + // visits end + cv.visitEnd(); + } +} diff --git a/org/objectweb/asm/tree/FieldInsnNode.java b/org/objectweb/asm/tree/FieldInsnNode.java new file mode 100644 index 0000000000..61c257429b --- /dev/null +++ b/org/objectweb/asm/tree/FieldInsnNode.java @@ -0,0 +1,103 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.CodeVisitor; + +/** + * A node that represents a field instruction. A field instruction is an + * instruction that loads or stores the value of a field of an object. + */ + +public class FieldInsnNode extends AbstractInsnNode { + + /** + * The internal name of the field's owner class (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). + */ + + public String owner; + + /** + * The field's name. + */ + + public String name; + + /** + * The field's descriptor (see {@link org.objectweb.asm.Type Type}). + */ + + public String desc; + + /** + * Constructs a new {@link FieldInsnNode FieldInsnNode} object. + * + * @param opcode the opcode of the type instruction to be constructed. This + * opcode must be GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. + * @param owner the internal name of the field's owner class (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). + * @param name the field's name. + * @param desc the field's descriptor (see {@link org.objectweb.asm.Type + * Type}). + */ + + public FieldInsnNode ( + final int opcode, + final String owner, + final String name, + final String desc) + { + super(opcode); + this.owner = owner; + this.name = name; + this.desc = desc; + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be GETSTATIC, + * PUTSTATIC, GETFIELD or PUTFIELD. + */ + + public void setOpcode (final int opcode) { + this.opcode = opcode; + } + + public void accept (final CodeVisitor cv) { + cv.visitFieldInsn(opcode, owner, name, desc); + } +} diff --git a/org/objectweb/asm/tree/FieldNode.java b/org/objectweb/asm/tree/FieldNode.java new file mode 100644 index 0000000000..d9d032581d --- /dev/null +++ b/org/objectweb/asm/tree/FieldNode.java @@ -0,0 +1,120 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Attribute; + +/** + * A node that represents a field. + */ + +public class FieldNode { + + /** + * The field's access flags (see {@link org.objectweb.asm.Constants}). This + * field also indicates if the field is synthetic and/or deprecated. + */ + + public int access; + + /** + * The field's name. + */ + + public String name; + + /** + * The field's descriptor (see {@link org.objectweb.asm.Type Type}). + */ + + public String desc; + + /** + * The field's initial value. This field, which may be null if the + * field does not have an initial value, must be an {@link java.lang.Integer + * Integer}, a {@link java.lang.Float Float}, a {@link java.lang.Long Long}, + * a {@link java.lang.Double Double} or a {@link String String}. + */ + + public Object value; + + /** + * The non standard attributes of the field. + */ + + public Attribute attrs; + + /** + * Constructs a new {@link FieldNode FieldNode} object. + * + * @param access the field's access flags (see {@link + * org.objectweb.asm.Constants}). This parameter also indicates if the + * field is synthetic and/or deprecated. + * @param name the field's name. + * @param desc the field's descriptor (see {@link org.objectweb.asm.Type + * Type}). + * @param value the field's initial value. This parameter, which may be + * null if the field does not have an initial value, must be an + * {@link java.lang.Integer Integer}, a {@link java.lang.Float Float}, a + * {@link java.lang.Long Long}, a {@link java.lang.Double Double} or a + * {@link String String}. + * @param attrs the non standard attributes of the field. + */ + + public FieldNode ( + final int access, + final String name, + final String desc, + final Object value, + final Attribute attrs) + { + this.access = access; + this.name = name; + this.desc = desc; + this.value = value; + this.attrs = attrs; + } + + /** + * Makes the given class visitor visit this field. + * + * @param cv a class visitor. + */ + + public void accept (final ClassVisitor cv) { + cv.visitField(access, name, desc, value, attrs); + } +} diff --git a/org/objectweb/asm/tree/IincInsnNode.java b/org/objectweb/asm/tree/IincInsnNode.java new file mode 100644 index 0000000000..b1c8fc958b --- /dev/null +++ b/org/objectweb/asm/tree/IincInsnNode.java @@ -0,0 +1,74 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.CodeVisitor; +import org.objectweb.asm.Constants; + +/** + * A node that represents an IINC instruction. + */ + +public class IincInsnNode extends AbstractInsnNode { + + /** + * Index of the local variable to be incremented. + */ + + public int var; + + /** + * Amount to increment the local variable by. + */ + + public int incr; + + /** + * Constructs a new {@link IincInsnNode IincInsnNode} node. + * + * @param var index of the local variable to be incremented. + * @param incr increment amount to increment the local variable by. + */ + + public IincInsnNode (final int var, final int incr) { + super(Constants.IINC); + this.var = var; + this.incr = incr; + } + + public void accept (final CodeVisitor cv) { + cv.visitIincInsn(var, incr); + } +} diff --git a/org/objectweb/asm/tree/InnerClassNode.java b/org/objectweb/asm/tree/InnerClassNode.java new file mode 100644 index 0000000000..49e413ace3 --- /dev/null +++ b/org/objectweb/asm/tree/InnerClassNode.java @@ -0,0 +1,109 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.ClassVisitor; + +/** + * A node that represents an inner class. + */ + +public class InnerClassNode { + + /** + * The internal name of an inner class (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). + */ + + public String name; + + /** + * The internal name of the class to which the inner class belongs (see + * {@link org.objectweb.asm.Type#getInternalName getInternalName}). May be + * null. + */ + + public String outerName; + + /** + * The (simple) name of the inner class inside its enclosing class. May be + * null for anonymous inner classes. + */ + + public String innerName; + + /** + * The access flags of the inner class as originally declared in the enclosing + * class. + */ + + public int access; + + /** + * Constructs a new {@link InnerClassNode InnerClassNode} object. + * + * @param name the internal name of an inner class (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). + * @param outerName the internal name of the class to which the inner class + * belongs (see {@link org.objectweb.asm.Type#getInternalName + * getInternalName}). May be null. + * @param innerName the (simple) name of the inner class inside its enclosing + * class. May be null for anonymous inner classes. + * @param access the access flags of the inner class as originally declared + * in the enclosing class. + */ + + public InnerClassNode ( + final String name, + final String outerName, + final String innerName, + final int access) + { + this.name = name; + this.outerName = outerName; + this.innerName = innerName; + this.access = access; + } + + /** + * Makes the given class visitor visit this inner class. + * + * @param cv a class visitor. + */ + + public void accept (final ClassVisitor cv) { + cv.visitInnerClass(name, outerName, innerName, access); + } +} diff --git a/org/objectweb/asm/tree/InsnNode.java b/org/objectweb/asm/tree/InsnNode.java new file mode 100644 index 0000000000..800c53108d --- /dev/null +++ b/org/objectweb/asm/tree/InsnNode.java @@ -0,0 +1,126 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.CodeVisitor; + +/** + * A node that represents a zero operand instruction. + */ + +public class InsnNode extends AbstractInsnNode { + + /** + * Constructs a new {@link InsnNode InsnNode} object. + * + * @param opcode the opcode of the instruction to be constructed. This opcode + * must be NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, + * ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, + * FCONST_2, DCONST_0, DCONST_1, + * + * IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, + * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, + * SASTORE, + * + * POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, + * + * IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, + * DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, + * FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, + * LOR, IXOR, LXOR, + * + * I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, + * I2S, + * + * LCMP, FCMPL, FCMPG, DCMPL, DCMPG, + * + * IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, RETURN, + * + * ARRAYLENGTH, + * + * ATHROW, + * + * MONITORENTER, or MONITOREXIT. + */ + + public InsnNode (final int opcode) { + super(opcode); + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be NOP, + * ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, + * ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, + * FCONST_2, DCONST_0, DCONST_1, + * + * IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, + * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, + * SASTORE, + * + * POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, + * + * IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, + * DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, + * FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, + * LOR, IXOR, LXOR, + * + * I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, + * I2S, + * + * LCMP, FCMPL, FCMPG, DCMPL, DCMPG, + * + * IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, RETURN, + * + * ARRAYLENGTH, + * + * ATHROW, + * + * MONITORENTER, or MONITOREXIT. + */ + + public void setOpcode (final int opcode) { + this.opcode = opcode; + } + + /** + * Makes the given code visitor visit this instruction. + */ + + public void accept (final CodeVisitor cv) { + cv.visitInsn(opcode); + } +} diff --git a/org/objectweb/asm/tree/IntInsnNode.java b/org/objectweb/asm/tree/IntInsnNode.java new file mode 100644 index 0000000000..5da32ebec2 --- /dev/null +++ b/org/objectweb/asm/tree/IntInsnNode.java @@ -0,0 +1,78 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.CodeVisitor; + +/** + * A node that represents an instruction with a single int operand. + */ + +public class IntInsnNode extends AbstractInsnNode { + + /** + * The operand of this instruction. + */ + + public int operand; + + /** + * Constructs a new {@link IntInsnNode IntInsnNode} object. + * + * @param opcode the opcode of the instruction to be constructed. This opcode + * must be BIPUSH, SIPUSH or NEWARRAY. + * @param operand the operand of the instruction to be constructed. + */ + + public IntInsnNode (final int opcode, final int operand) { + super(opcode); + this.operand = operand; + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be BIPUSH, + * SIPUSH or NEWARRAY. + */ + + public void setOpcode (final int opcode) { + this.opcode = opcode; + } + + public void accept (final CodeVisitor cv) { + cv.visitIntInsn(opcode, operand); + } +} diff --git a/org/objectweb/asm/tree/JumpInsnNode.java b/org/objectweb/asm/tree/JumpInsnNode.java new file mode 100644 index 0000000000..dc1a25fdcf --- /dev/null +++ b/org/objectweb/asm/tree/JumpInsnNode.java @@ -0,0 +1,87 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.Label; +import org.objectweb.asm.CodeVisitor; + +/** + * A node that represents a jump instruction. A jump instruction is an + * instruction that may jump to another instruction. + */ + +public class JumpInsnNode extends AbstractInsnNode { + + /** + * The operand of this instruction. This operand is a label that designates + * the instruction to which this instruction may jump. + */ + + public Label label; + + /** + * Constructs a new {@link JumpInsnNode JumpInsnNode} object. + * + * @param opcode the opcode of the type instruction to be constructed. This + * opcode must be IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, + * IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, + * IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL. + * @param label the operand of the instruction to be constructed. This operand + * is a label that designates the instruction to which the jump + * instruction may jump. + */ + + public JumpInsnNode (final int opcode, final Label label) { + super(opcode); + this.label = label; + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be + * IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, + * IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, + * GOTO, JSR, IFNULL or IFNONNULL. + */ + + public void setOpcode (final int opcode) { + this.opcode = opcode; + } + + public void accept (final CodeVisitor cv) { + cv.visitJumpInsn(opcode, label); + } +} diff --git a/org/objectweb/asm/tree/LdcInsnNode.java b/org/objectweb/asm/tree/LdcInsnNode.java new file mode 100644 index 0000000000..34aaae7fa2 --- /dev/null +++ b/org/objectweb/asm/tree/LdcInsnNode.java @@ -0,0 +1,72 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.CodeVisitor; +import org.objectweb.asm.Constants; + +/** + * A node that represents an LDC instruction. + */ + +public class LdcInsnNode extends AbstractInsnNode { + + /** + * The constant to be loaded on the stack. This parameter must be a non null + * {@link java.lang.Integer Integer}, a {@link java.lang.Float Float}, a + * {@link java.lang.Long Long}, a {@link java.lang.Double Double} or a {@link + * String String}. + */ + + public Object cst; + + /** + * Constructs a new {@link LdcInsnNode LdcInsnNode} object. + * + * @param cst the constant to be loaded on the stack. This parameter must be + * a non null {@link java.lang.Integer Integer}, a {@link java.lang.Float + * Float}, a {@link java.lang.Long Long}, a {@link java.lang.Double + * Double} or a {@link String String}. + */ + + public LdcInsnNode (final Object cst) { + super(Constants.LDC); + this.cst = cst; + } + + public void accept (final CodeVisitor cv) { + cv.visitLdcInsn(cst); + } +} diff --git a/org/objectweb/asm/tree/LineNumberNode.java b/org/objectweb/asm/tree/LineNumberNode.java new file mode 100644 index 0000000000..55837b4880 --- /dev/null +++ b/org/objectweb/asm/tree/LineNumberNode.java @@ -0,0 +1,81 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.Label; +import org.objectweb.asm.CodeVisitor; + +/** + * A node that represents a line number declaration. + */ + +public class LineNumberNode { + + /** + * A line number. This number refers to the source file from which the class + * was compiled. + */ + + public int line; + + /** + * The first instruction corresponding to this line number. + */ + + public Label start; + + /** + * Constructs a new {@link LineNumberNode LineNumberNode} object. + * + * @param line a line number. This number refers to the source file + * from which the class was compiled. + * @param start the first instruction corresponding to this line number. + */ + + public LineNumberNode (final int line, final Label start) { + this.line = line; + this.start = start; + } + + /** + * Makes the given code visitor visit this line number declaration. + * + * @param cv a code visitor. + */ + + public void accept (final CodeVisitor cv) { + cv.visitLineNumber(line, start); + } +} diff --git a/org/objectweb/asm/tree/LocalVariableNode.java b/org/objectweb/asm/tree/LocalVariableNode.java new file mode 100644 index 0000000000..8f2a6d5dfc --- /dev/null +++ b/org/objectweb/asm/tree/LocalVariableNode.java @@ -0,0 +1,113 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.CodeVisitor; +import org.objectweb.asm.Label; + +/** + * A node that represents a local variable declaration. + */ + +public class LocalVariableNode { + + /** + * The name of a local variable. + */ + + public String name; + + /** + * The type descriptor of this local variable. + */ + + public String desc; + + /** + * The first instruction corresponding to the scope of this local variable + * (inclusive). + */ + + public Label start; + + /** + * The last instruction corresponding to the scope of this local variable + * (exclusive). + */ + + public Label end; + + /** + * The local variable's index. + */ + + public int index; + + /** + * Constructs a new {@link LocalVariableNode LocalVariableNode} object. + * + * @param name the name of a local variable. + * @param desc the type descriptor of this local variable. + * @param start the first instruction corresponding to the scope of this + * local variable (inclusive). + * @param end the last instruction corresponding to the scope of this + * local variable (exclusive). + * @param index the local variable's index. + */ + + public LocalVariableNode ( + final String name, + final String desc, + final Label start, + final Label end, + final int index) + { + this.name = name; + this.desc = desc; + this.start = start; + this.end = end; + this.index = index; + } + + /** + * Makes the given code visitor visit this local variable declaration. + * + * @param cv a code visitor. + */ + + public void accept (final CodeVisitor cv) { + cv.visitLocalVariable(name, desc, start, end, index); + } +} diff --git a/org/objectweb/asm/tree/LookupSwitchInsnNode.java b/org/objectweb/asm/tree/LookupSwitchInsnNode.java new file mode 100644 index 0000000000..fafd1d6437 --- /dev/null +++ b/org/objectweb/asm/tree/LookupSwitchInsnNode.java @@ -0,0 +1,108 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.Label; +import org.objectweb.asm.Constants; +import org.objectweb.asm.CodeVisitor; + +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * A node that represents a LOOKUPSWITCH instruction. + */ + +public class LookupSwitchInsnNode extends AbstractInsnNode { + + /** + * Beginning of the default handler block. + */ + + public Label dflt; + + /** + * The values of the keys. This list is a list a {@link java.lang.Integer + * Integer} objects. + */ + + public final List keys; + + /** + * Beginnings of the handler blocks. This list is a list of {@link Label + * Label} objects. + */ + + public final List labels; + + /** + * Constructs a new {@link LookupSwitchInsnNode LookupSwitchInsnNode} object. + * + * @param dflt beginning of the default handler block. + * @param keys the values of the keys. + * @param labels beginnings of the handler blocks. labels[i] is the + * beginning of the handler block for the keys[i] key. + */ + + public LookupSwitchInsnNode ( + final Label dflt, + final int[] keys, + final Label[] labels) + { + super(Constants.LOOKUPSWITCH); + this.dflt = dflt; + this.keys = new ArrayList(); + this.labels = new ArrayList(); + if (keys != null) { + for (int i = 0; i < keys.length; ++i) { + this.keys.add(new Integer(keys[i])); + } + } + if (labels != null) { + this.labels.addAll(Arrays.asList(labels)); + } + } + + public void accept (final CodeVisitor cv) { + int[] keys = new int[this.keys.size()]; + for (int i = 0; i < keys.length; ++i) { + keys[i] = ((Integer)this.keys.get(i)).intValue(); + } + Label[] labels = new Label[this.labels.size()]; + this.labels.toArray(labels); + cv.visitLookupSwitchInsn(dflt, keys, labels); + } +} diff --git a/org/objectweb/asm/tree/MethodInsnNode.java b/org/objectweb/asm/tree/MethodInsnNode.java new file mode 100644 index 0000000000..14d82ce375 --- /dev/null +++ b/org/objectweb/asm/tree/MethodInsnNode.java @@ -0,0 +1,104 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.CodeVisitor; + +/** + * A node that represents a method instruction. A method instruction is an + * instruction that invokes a method. + */ + +public class MethodInsnNode extends AbstractInsnNode { + + /** + * The internal name of the method's owner class (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). + */ + + public String owner; + + /** + * The method's name. + */ + + public String name; + + /** + * The method's descriptor (see {@link org.objectweb.asm.Type Type}). + */ + + public String desc; + + /** + * Constructs a new {@link MethodInsnNode MethodInsnNode} object. + * + * @param opcode the opcode of the type instruction to be constructed. This + * opcode must be INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner the internal name of the method's owner class (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link org.objectweb.asm.Type + * Type}). + */ + + public MethodInsnNode ( + final int opcode, + final String owner, + final String name, + final String desc) + { + super(opcode); + this.owner = owner; + this.name = name; + this.desc = desc; + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be + * INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE. + */ + + public void setOpcode (final int opcode) { + this.opcode = opcode; + } + + public void accept (final CodeVisitor cv) { + cv.visitMethodInsn(opcode, owner, name, desc); + } +} diff --git a/org/objectweb/asm/tree/MethodNode.java b/org/objectweb/asm/tree/MethodNode.java new file mode 100644 index 0000000000..903bb12910 --- /dev/null +++ b/org/objectweb/asm/tree/MethodNode.java @@ -0,0 +1,210 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.CodeVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.Attribute; + +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * A node that represents a method. + */ + +public class MethodNode { + + /** + * The method's access flags (see {@link org.objectweb.asm.Constants}). This + * field also indicates if the method is synthetic and/or deprecated. + */ + + public int access; + + /** + * The method's name. + */ + + public String name; + + /** + * The method's descriptor (see {@link org.objectweb.asm.Type Type}). + */ + + public String desc; + + /** + * The internal names of the method's exception classes (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). This list is a + * list of {@link String} objects. + */ + + public final List exceptions; + + /** + * The non standard attributes of the method. + */ + + public Attribute attrs; + + /** + * The instructions of this method. This list is a list of {@link + * AbstractInsnNode AbstractInsnNode} and {@link Label Label} objects. + */ + + public final List instructions; + + /** + * The try catch blocks of this method. This list is a list of {@link + * TryCatchBlockNode TryCatchBlockNode} objects. + */ + + public final List tryCatchBlocks; + + /** + * The maximum stack size of this method. + */ + + public int maxStack; + + /** + * The maximum number of local variables of this method. + */ + + public int maxLocals; + + /** + * The local variables of this method. This list is a list of {@link + * LocalVariableNode LocalVariableNode} objects. + */ + + public final List localVariables; + + /** + * The line numbers of this method. This list is a list of {@link + * LineNumberNode LineNumberNode} objects. + */ + + public final List lineNumbers; + + /** + * The non standard attributes of the method's code. + */ + + public Attribute codeAttrs; + + /** + * Constructs a new {@link MethodNode MethodNode} object. + * + * @param access the method's access flags (see {@link + * org.objectweb.asm.Constants}). This parameter also indicates if the + * method is synthetic and/or deprecated. + * @param name the method's name. + * @param desc the method's descriptor (see {@link org.objectweb.asm.Type + * Type}). + * @param exceptions the internal names of the method's exception + * classes (see {@link org.objectweb.asm.Type#getInternalName + * getInternalName}). May be null. + * @param attrs the non standard attributes of the method. + */ + + public MethodNode ( + final int access, + final String name, + final String desc, + final String[] exceptions, + final Attribute attrs) + { + this.access = access; + this.name = name; + this.desc = desc; + this.exceptions = new ArrayList(); + this.instructions = new ArrayList(); + this.tryCatchBlocks = new ArrayList(); + this.localVariables = new ArrayList(); + this.lineNumbers = new ArrayList(); + if (exceptions != null) { + this.exceptions.addAll(Arrays.asList(exceptions)); + } + this.attrs = attrs; + } + + /** + * Makes the given class visitor visit this method. + * + * @param cv a class visitor. + */ + + public void accept (final ClassVisitor cv) { + String[] exceptions = new String[this.exceptions.size()]; + this.exceptions.toArray(exceptions); + CodeVisitor mv = cv.visitMethod(access, name, desc, exceptions, attrs); + if (mv != null && instructions.size() > 0) { + int i; + // visits instructions + for (i = 0; i < instructions.size(); ++i) { + Object insn = instructions.get(i); + if (insn instanceof Label) { + mv.visitLabel((Label)insn); + } else { + ((AbstractInsnNode)insn).accept(mv); + } + } + // visits try catch blocks + for (i = 0; i < tryCatchBlocks.size(); ++i) { + ((TryCatchBlockNode)tryCatchBlocks.get(i)).accept(mv); + } + // visits maxs + mv.visitMaxs(maxStack, maxLocals); + // visits local variables + for (i = 0; i < localVariables.size(); ++i) { + ((LocalVariableNode)localVariables.get(i)).accept(mv); + } + // visits line numbers + for (i = 0; i < lineNumbers.size(); ++i) { + ((LineNumberNode)lineNumbers.get(i)).accept(mv); + } + // visits the code attributes + Attribute attrs = codeAttrs; + while (attrs != null) { + mv.visitAttribute(attrs); + attrs = attrs.next; + } + } + } +} diff --git a/org/objectweb/asm/tree/MultiANewArrayInsnNode.java b/org/objectweb/asm/tree/MultiANewArrayInsnNode.java new file mode 100644 index 0000000000..dd33df3ecd --- /dev/null +++ b/org/objectweb/asm/tree/MultiANewArrayInsnNode.java @@ -0,0 +1,76 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.Constants; +import org.objectweb.asm.CodeVisitor; + +/** + * A node that represents a MULTIANEWARRAY instruction. + */ + +public class MultiANewArrayInsnNode extends AbstractInsnNode { + + /** + * An array type descriptor (see {@link org.objectweb.asm.Type Type}). + */ + + public String desc; + + /** + * Number of dimensions of the array to allocate. + */ + + public int dims; + + /** + * Constructs a new {@link MultiANewArrayInsnNode MultiANewArrayInsnNode} + * object. + * + * @param desc an array type descriptor (see {@link org.objectweb.asm.Type + * Type}). + * @param dims number of dimensions of the array to allocate. + */ + + public MultiANewArrayInsnNode (final String desc, final int dims) { + super(Constants.MULTIANEWARRAY); + this.desc = desc; + this.dims = dims; + } + + public void accept (final CodeVisitor cv) { + cv.visitMultiANewArrayInsn(desc, dims); + } +} diff --git a/org/objectweb/asm/tree/TableSwitchInsnNode.java b/org/objectweb/asm/tree/TableSwitchInsnNode.java new file mode 100644 index 0000000000..f15bba43c6 --- /dev/null +++ b/org/objectweb/asm/tree/TableSwitchInsnNode.java @@ -0,0 +1,107 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.Label; +import org.objectweb.asm.Constants; +import org.objectweb.asm.CodeVisitor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A node that represents a TABLESWITCH instruction. + */ + +public class TableSwitchInsnNode extends AbstractInsnNode { + + /** + * The minimum key value. + */ + + public int min; + + /** + * The maximum key value. + */ + + public int max; + + /** + * Beginning of the default handler block. + */ + + public Label dflt; + + /** + * Beginnings of the handler blocks. This list is a list of {@link Label + * Label} objects. + */ + + public final List labels; + + /** + * Constructs a new {@link TableSwitchInsnNode TableSwitchInsnNode}. + * + * @param min the minimum key value. + * @param max the maximum key value. + * @param dflt beginning of the default handler block. + * @param labels beginnings of the handler blocks. labels[i] is the + * beginning of the handler block for the min + i key. + */ + + public TableSwitchInsnNode ( + final int min, + final int max, + final Label dflt, + final Label[] labels) + { + super(Constants.TABLESWITCH); + this.min = min; + this.max = max; + this.dflt = dflt; + this.labels = new ArrayList(); + if (labels != null) { + this.labels.addAll(Arrays.asList(labels)); + } + } + + public void accept (final CodeVisitor cv) { + Label[] labels = new Label[this.labels.size()]; + this.labels.toArray(labels); + cv.visitTableSwitchInsn(min, max, dflt, labels); + } +} diff --git a/org/objectweb/asm/tree/TreeClassAdapter.java b/org/objectweb/asm/tree/TreeClassAdapter.java new file mode 100644 index 0000000000..e8a525c161 --- /dev/null +++ b/org/objectweb/asm/tree/TreeClassAdapter.java @@ -0,0 +1,133 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.ClassAdapter; +import org.objectweb.asm.CodeVisitor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Attribute; + +/** + * A {@link ClassAdapter ClassAdapter} that constructs a tree representation of + * the classes it vists. Each visitXXX method of this class + * constructs an XXXNode and adds it to the {@link #classNode + * classNode} node (except the {@link #visitEnd visitEnd} method, which just + * makes the {@link #cv cv} class visitor visit the tree that has just been + * constructed). + *

+ * In order to implement a usefull class adapter based on a tree representation + * of classes, one just need to override the {@link #visitEnd visitEnd} method + * with a method of the following form: + *

+ * public void visitEnd () {
+ *   // ...
+ *   // code to modify the classNode tree, can be arbitrary complex
+ *   // ...
+ *   // makes the cv visitor visit this modified class:
+ *   classNode.accept(cv);
+ * }
+ * 
+ */ + +public class TreeClassAdapter extends ClassAdapter { + + /** + * A tree representation of the class that is being visited by this visitor. + */ + + public ClassNode classNode; + + /** + * Constructs a new {@link TreeClassAdapter TreeClassAdapter} object. + * + * @param cv the class visitor to which this adapter must delegate calls. + */ + + public TreeClassAdapter (final ClassVisitor cv) { + super(cv); + } + + public void visit ( + final int access, + final String name, + final String superName, + final String[] interfaces, + final String sourceFile) + { + classNode = new ClassNode(access, name, superName, interfaces, sourceFile); + } + + public void visitInnerClass ( + final String name, + final String outerName, + final String innerName, + final int access) + { + InnerClassNode icn = new InnerClassNode(name, outerName, innerName, access); + classNode.innerClasses.add(icn); + } + + public void visitField ( + final int access, + final String name, + final String desc, + final Object value, + final Attribute attrs) + { + FieldNode fn = new FieldNode(access, name, desc, value, attrs); + classNode.fields.add(fn); + } + + public CodeVisitor visitMethod ( + final int access, + final String name, + final String desc, + final String[] exceptions, + final Attribute attrs) + { + MethodNode mn = new MethodNode(access, name, desc, exceptions, attrs); + classNode.methods.add(mn); + return new TreeCodeAdapter(mn); + } + + public void visitAttribute (final Attribute attr) { + attr.next = classNode.attrs; + classNode.attrs = attr; + } + + public void visitEnd () { + classNode.accept(cv); + } +} diff --git a/org/objectweb/asm/tree/TreeCodeAdapter.java b/org/objectweb/asm/tree/TreeCodeAdapter.java new file mode 100644 index 0000000000..2681de23b6 --- /dev/null +++ b/org/objectweb/asm/tree/TreeCodeAdapter.java @@ -0,0 +1,186 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.CodeAdapter; +import org.objectweb.asm.Label; +import org.objectweb.asm.Attribute; + +/** + * A {@link CodeAdapter CodeAdapter} that constructs a tree representation of + * the methods it vists. Each visitXXX method of this class + * constructs an XXXNode and adds it to the {@link #methodNode + * methodNode} node. + */ + +public class TreeCodeAdapter extends CodeAdapter { + + /** + * A tree representation of the method that is being visited by this visitor. + */ + + public MethodNode methodNode; + + /** + * Constructs a new {@link TreeCodeAdapter TreeCodeAdapter} object. + * + * @param methodNode the method node to be used to store the tree + * representation constructed by this code visitor. + */ + + public TreeCodeAdapter (final MethodNode methodNode) { + super(null); + this.methodNode = methodNode; + } + + public void visitInsn (final int opcode) { + AbstractInsnNode n = new InsnNode(opcode); + methodNode.instructions.add(n); + } + + public void visitIntInsn (final int opcode, final int operand) { + AbstractInsnNode n = new IntInsnNode(opcode, operand); + methodNode.instructions.add(n); + } + + public void visitVarInsn (final int opcode, final int var) { + AbstractInsnNode n = new VarInsnNode(opcode, var); + methodNode.instructions.add(n); + } + + public void visitTypeInsn (final int opcode, final String desc) { + AbstractInsnNode n = new TypeInsnNode(opcode, desc); + methodNode.instructions.add(n); + } + + public void visitFieldInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + AbstractInsnNode n = new FieldInsnNode(opcode, owner, name, desc); + methodNode.instructions.add(n); + } + + public void visitMethodInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + AbstractInsnNode n = new MethodInsnNode(opcode, owner, name, desc); + methodNode.instructions.add(n); + } + + public void visitJumpInsn (final int opcode, final Label label) { + AbstractInsnNode n = new JumpInsnNode(opcode, label); + methodNode.instructions.add(n); + } + + public void visitLabel (final Label label) { + methodNode.instructions.add(label); + } + + public void visitLdcInsn (final Object cst) { + AbstractInsnNode n = new LdcInsnNode(cst); + methodNode.instructions.add(n); + } + + public void visitIincInsn (final int var, final int increment) { + AbstractInsnNode n = new IincInsnNode(var, increment); + methodNode.instructions.add(n); + } + + public void visitTableSwitchInsn ( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + AbstractInsnNode n = new TableSwitchInsnNode(min, max, dflt, labels); + methodNode.instructions.add(n); + } + + public void visitLookupSwitchInsn ( + final Label dflt, + final int keys[], + final Label labels[]) + { + AbstractInsnNode n = new LookupSwitchInsnNode(dflt, keys, labels); + methodNode.instructions.add(n); + } + + public void visitMultiANewArrayInsn (final String desc, final int dims) { + AbstractInsnNode n = new MultiANewArrayInsnNode(desc, dims); + methodNode.instructions.add(n); + } + + public void visitTryCatchBlock ( + final Label start, + final Label end, + final Label handler, + final String type) + { + TryCatchBlockNode n = new TryCatchBlockNode(start, end, handler, type); + methodNode.tryCatchBlocks.add(n); + } + + public void visitMaxs (final int maxStack, final int maxLocals) { + methodNode.maxStack = maxStack; + methodNode.maxLocals = maxLocals; + } + + public void visitLocalVariable ( + final String name, + final String desc, + final Label start, + final Label end, + final int index) + { + LocalVariableNode n = new LocalVariableNode(name, desc, start, end, index); + methodNode.localVariables.add(n); + } + + public void visitLineNumber (final int line, final Label start) { + LineNumberNode n = new LineNumberNode(line, start); + methodNode.lineNumbers.add(n); + } + + public void visitAttribute (final Attribute attr) { + attr.next = methodNode.attrs; + methodNode.attrs = attr; + } +} diff --git a/org/objectweb/asm/tree/TryCatchBlockNode.java b/org/objectweb/asm/tree/TryCatchBlockNode.java new file mode 100644 index 0000000000..57a5539d3f --- /dev/null +++ b/org/objectweb/asm/tree/TryCatchBlockNode.java @@ -0,0 +1,102 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.Label; +import org.objectweb.asm.CodeVisitor; + +/** + * A node that represents a try catch block. + */ + +public class TryCatchBlockNode { + + /** + * Beginning of the exception handler's scope (inclusive). + */ + + public Label start; + + /** + * End of the exception handler's scope (exclusive). + */ + + public Label end; + + /** + * Beginning of the exception handler's code. + */ + + public Label handler; + + /** + * Internal name of the type of exceptions handled by the handler. May be + * null to catch any exceptions (for "finally" blocks). + */ + + public String type; + + /** + * Constructs a new {@link TryCatchBlockNode TryCatchBlockNode} object. + * + * @param start beginning of the exception handler's scope (inclusive). + * @param end end of the exception handler's scope (exclusive). + * @param handler beginning of the exception handler's code. + * @param type internal name of the type of exceptions handled by the handler, + * or null to catch any exceptions (for "finally" blocks). + */ + + public TryCatchBlockNode ( + final Label start, + final Label end, + final Label handler, + final String type) + { + this.start = start; + this.end = end; + this.handler = handler; + this.type = type; + } + + /** + * Makes the given code visitor visit this try catch block. + * + * @param cv a code visitor. + */ + + public void accept (final CodeVisitor cv) { + cv.visitTryCatchBlock(start, end, handler, type); + } +} diff --git a/org/objectweb/asm/tree/TypeInsnNode.java b/org/objectweb/asm/tree/TypeInsnNode.java new file mode 100644 index 0000000000..7028957e1e --- /dev/null +++ b/org/objectweb/asm/tree/TypeInsnNode.java @@ -0,0 +1,81 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.CodeVisitor; + +/** + * A node that represents a type instruction. A type instruction is an + * instruction that takes a type descriptor as parameter. + */ + +public class TypeInsnNode extends AbstractInsnNode { + + /** + * The operand of this instruction. This operand is a type descriptor (see + * {@link org.objectweb.asm.Type Type}). + */ + + public String desc; + + /** + * Constructs a new {@link TypeInsnNode TypeInsnNode} object. + * + * @param opcode the opcode of the type instruction to be constructed. This + * opcode must be NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. + * @param desc the operand of the instruction to be constructed. This operand + * is a type descriptor (see {@link org.objectweb.asm.Type Type}). + */ + + public TypeInsnNode (final int opcode, final String desc) { + super(opcode); + this.desc = desc; + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be + * NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. + */ + + public void setOpcode (final int opcode) { + this.opcode = opcode; + } + + public void accept (final CodeVisitor cv) { + cv.visitTypeInsn(opcode, desc); + } +} diff --git a/org/objectweb/asm/tree/VarInsnNode.java b/org/objectweb/asm/tree/VarInsnNode.java new file mode 100644 index 0000000000..d21e23dad0 --- /dev/null +++ b/org/objectweb/asm/tree/VarInsnNode.java @@ -0,0 +1,84 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.tree; + +import org.objectweb.asm.CodeVisitor; + +/** + * A node that represents a local variable instruction. A local variable + * instruction is an instruction that loads or stores the value of a local + * variable. + */ + +public class VarInsnNode extends AbstractInsnNode { + + /** + * The operand of this instruction. This operand is the index of a local + * variable. + */ + + public int var; + + /** + * Visits a local variable instruction. A local variable instruction is an + * instruction that loads or stores the value of a local variable. + * + * @param opcode the opcode of the local variable instruction to be + * constructed. This opcode must be ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, + * ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET. + * @param var the operand of the instruction to be constructed. This operand + * is the index of a local variable. + */ + + public VarInsnNode (final int opcode, final int var) { + super(opcode); + this.var = var; + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be ILOAD, LLOAD, + * FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET. + */ + + public void setOpcode (final int opcode) { + this.opcode = opcode; + } + + public void accept (final CodeVisitor cv) { + cv.visitVarInsn(opcode, var); + } +} diff --git a/org/objectweb/asm/util/CheckClassAdapter.java b/org/objectweb/asm/util/CheckClassAdapter.java new file mode 100644 index 0000000000..2da81e20d0 --- /dev/null +++ b/org/objectweb/asm/util/CheckClassAdapter.java @@ -0,0 +1,230 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.util; + +import org.objectweb.asm.ClassAdapter; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.CodeVisitor; +import org.objectweb.asm.Constants; +import org.objectweb.asm.Attribute; + +/** + * A {@link ClassAdapter ClssAdapter} that checks that its methods are properly + * used. More precisely this class adapter checks each method call individually, + * based only on its arguments, but does not check the + * sequence of method calls. For example, the invalid sequence + * visitField(ACC_PUBLIC, "i", "I", null) visitField(ACC_PUBLIC, + * "i", "D", null) will not be detected by this class adapter. + */ + +public class CheckClassAdapter extends ClassAdapter { + + /** + * true if the visit method has been called. + */ + + private boolean start; + + /** + * true if the visitEnd method has been called. + */ + + private boolean end; + + /** + * Constructs a new {@link CheckClassAdapter CheckClassAdapter} object. + * + * @param cv the class visitor to which this adapter must delegate calls. + */ + + public CheckClassAdapter (final ClassVisitor cv) { + super(cv); + } + + public void visit ( + final int access, + final String name, + final String superName, + final String[] interfaces, + final String sourceFile) + { + if (start) { + throw new IllegalStateException("visit must be called only once"); + } else { + start = true; + } + checkState(); + checkAccess(access, 1 + 2 + 4 + 16 + 512 + 1024 + 32 + 65536 + 131072); + CheckCodeAdapter.checkInternalName(name, "class name"); + if (name.equals("java/lang/Object")) { + if (superName != null) { + throw new IllegalArgumentException( + "The super class name of the Object class must be 'null'"); + } + } else { + CheckCodeAdapter.checkInternalName(superName, "super class name"); + } + if ((access & Constants.ACC_INTERFACE) != 0) { + if (!superName.equals("java/lang/Object")) { + throw new IllegalArgumentException( + "The super class name of interfaces must be 'java/lang/Object'"); + } + } + if (interfaces != null) { + for (int i = 0; i < interfaces.length; ++i) { + CheckCodeAdapter.checkInternalName( + interfaces[i], "interface name at index " + i); + } + } + cv.visit(access, name, superName, interfaces, sourceFile); + } + + public void visitInnerClass ( + final String name, + final String outerName, + final String innerName, + final int access) + { + checkState(); + CheckCodeAdapter.checkInternalName(name, "class name"); + if (outerName != null) { + CheckCodeAdapter.checkInternalName(outerName, "outer class name"); + } + if (innerName != null) { + CheckCodeAdapter.checkIdentifier(innerName, "inner class name"); + } + checkAccess(access, 1 + 2 + 4 + 8 + 16 + 512 + 1024 + 32); + cv.visitInnerClass(name, outerName, innerName, access); + } + + public void visitField ( + final int access, + final String name, + final String desc, + final Object value, + final Attribute attrs) + { + checkState(); + checkAccess(access, 1 + 2 + 4 + 8 + 16 + 64 + 128 + 65536 + 131072); + CheckCodeAdapter.checkIdentifier(name, "field name"); + CheckCodeAdapter.checkDesc(desc, false); + if (value != null) { + CheckCodeAdapter.checkConstant(value); + } + cv.visitField(access, name, desc, value, attrs); + } + + public CodeVisitor visitMethod ( + final int access, + final String name, + final String desc, + final String[] exceptions, + final Attribute attrs) + { + checkState(); + checkAccess( + access, 1 + 2 + 4 + 8 + 16 + 32 + 256 + 1024 + 2048 + 65536 + 131072); + CheckCodeAdapter.checkMethodIdentifier(name, "method name"); + CheckCodeAdapter.checkMethodDesc(desc); + if (exceptions != null) { + for (int i = 0; i < exceptions.length; ++i) { + CheckCodeAdapter.checkInternalName( + exceptions[i], "exception name at index " + i); + } + } + return new CheckCodeAdapter( + cv.visitMethod(access, name, desc, exceptions, attrs)); + } + + public void visitAttribute (final Attribute attr) { + checkState(); + if (attr == null) { + throw new IllegalArgumentException( + "Invalid attribute (must not be null)"); + } + } + + public void visitEnd () { + checkState(); + end = true; + cv.visitEnd(); + } + + // --------------------------------------------------------------------------- + + /** + * Checks that the visit method has been called and that visitEnd has not been + * called. + */ + + private void checkState () { + if (!start) { + throw new IllegalStateException( + "Cannot visit member before visit has been called."); + } + if (end) { + throw new IllegalStateException( + "Cannot visit member after visitEnd has been called."); + } + } + + /** + * Checks that the given access flags do not contain invalid flags. This + * method also checks that mutually incompatible flags are not set + * simultaneously. + * + * @param access the access flags to be checked + * @param possibleAccess the valid access flags. + */ + + static void checkAccess (final int access, final int possibleAccess) { + if ((access & ~possibleAccess) != 0) { + throw new IllegalArgumentException("Invalid access flags: " + access); + } + int pub = ((access & Constants.ACC_PUBLIC) != 0 ? 1 : 0); + int pri = ((access & Constants.ACC_PRIVATE) != 0 ? 1 : 0); + int pro = ((access & Constants.ACC_PROTECTED) != 0 ? 1 : 0); + if (pub + pri + pro > 1) { + throw new IllegalArgumentException( + "public private and protected are mutually exclusive: " + access); + } + int fin = ((access & Constants.ACC_FINAL) != 0 ? 1 : 0); + int abs = ((access & Constants.ACC_ABSTRACT) != 0 ? 1 : 0); + if (fin + abs > 1) { + throw new IllegalArgumentException( + "final and abstract are mutually exclusive: " + access); + } + } +} diff --git a/org/objectweb/asm/util/CheckCodeAdapter.java b/org/objectweb/asm/util/CheckCodeAdapter.java new file mode 100644 index 0000000000..d246387f71 --- /dev/null +++ b/org/objectweb/asm/util/CheckCodeAdapter.java @@ -0,0 +1,859 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.util; + +import org.objectweb.asm.Label; +import org.objectweb.asm.CodeAdapter; +import org.objectweb.asm.CodeVisitor; +import org.objectweb.asm.Constants; +import org.objectweb.asm.Attribute; + +import java.util.HashMap; + +/** + * A {@link CodeAdapter CodeAdapter} that checks that its methods are properly + * used. More precisely this code adapter checks each instruction individually + * (i.e., each visit method checks some preconditions based only on its + * arguments - such as the fact that the given opcode is correct for a given + * visit method), but does not check the sequence of instructions. + * For example, in a method whose signature is void m (), the invalid + * instruction IRETURN, or the invalid sequence IADD L2I will not be + * detected by this code adapter. + */ + +public class CheckCodeAdapter extends CodeAdapter { + + /** + * true if the visitMaxs method has been called. + */ + + private boolean end; + + /** + * The already visited labels. This map associate Integer values to Label + * keys. + */ + + private HashMap labels; + + /** + * Code of the visit method to be used for each opcode. + */ + + private final static int[] TYPE = new int[] { + 0, //NOP + 0, //ACONST_NULL + 0, //ICONST_M1 + 0, //ICONST_0 + 0, //ICONST_1 + 0, //ICONST_2 + 0, //ICONST_3 + 0, //ICONST_4 + 0, //ICONST_5 + 0, //LCONST_0 + 0, //LCONST_1 + 0, //FCONST_0 + 0, //FCONST_1 + 0, //FCONST_2 + 0, //DCONST_0 + 0, //DCONST_1 + 1, //BIPUSH + 1, //SIPUSH + 7, //LDC + -1, //LDC_W + -1, //LDC2_W + 2, //ILOAD + 2, //LLOAD + 2, //FLOAD + 2, //DLOAD + 2, //ALOAD + -1, //ILOAD_0 + -1, //ILOAD_1 + -1, //ILOAD_2 + -1, //ILOAD_3 + -1, //LLOAD_0 + -1, //LLOAD_1 + -1, //LLOAD_2 + -1, //LLOAD_3 + -1, //FLOAD_0 + -1, //FLOAD_1 + -1, //FLOAD_2 + -1, //FLOAD_3 + -1, //DLOAD_0 + -1, //DLOAD_1 + -1, //DLOAD_2 + -1, //DLOAD_3 + -1, //ALOAD_0 + -1, //ALOAD_1 + -1, //ALOAD_2 + -1, //ALOAD_3 + 0, //IALOAD + 0, //LALOAD + 0, //FALOAD + 0, //DALOAD + 0, //AALOAD + 0, //BALOAD + 0, //CALOAD + 0, //SALOAD + 2, //ISTORE + 2, //LSTORE + 2, //FSTORE + 2, //DSTORE + 2, //ASTORE + -1, //ISTORE_0 + -1, //ISTORE_1 + -1, //ISTORE_2 + -1, //ISTORE_3 + -1, //LSTORE_0 + -1, //LSTORE_1 + -1, //LSTORE_2 + -1, //LSTORE_3 + -1, //FSTORE_0 + -1, //FSTORE_1 + -1, //FSTORE_2 + -1, //FSTORE_3 + -1, //DSTORE_0 + -1, //DSTORE_1 + -1, //DSTORE_2 + -1, //DSTORE_3 + -1, //ASTORE_0 + -1, //ASTORE_1 + -1, //ASTORE_2 + -1, //ASTORE_3 + 0, //IASTORE + 0, //LASTORE + 0, //FASTORE + 0, //DASTORE + 0, //AASTORE + 0, //BASTORE + 0, //CASTORE + 0, //SASTORE + 0, //POP + 0, //POP2 + 0, //DUP + 0, //DUP_X1 + 0, //DUP_X2 + 0, //DUP2 + 0, //DUP2_X1 + 0, //DUP2_X2 + 0, //SWAP + 0, //IADD + 0, //LADD + 0, //FADD + 0, //DADD + 0, //ISUB + 0, //LSUB + 0, //FSUB + 0, //DSUB + 0, //IMUL + 0, //LMUL + 0, //FMUL + 0, //DMUL + 0, //IDIV + 0, //LDIV + 0, //FDIV + 0, //DDIV + 0, //IREM + 0, //LREM + 0, //FREM + 0, //DREM + 0, //INEG + 0, //LNEG + 0, //FNEG + 0, //DNEG + 0, //ISHL + 0, //LSHL + 0, //ISHR + 0, //LSHR + 0, //IUSHR + 0, //LUSHR + 0, //IAND + 0, //LAND + 0, //IOR + 0, //LOR + 0, //IXOR + 0, //LXOR + 8, //IINC + 0, //I2L + 0, //I2F + 0, //I2D + 0, //L2I + 0, //L2F + 0, //L2D + 0, //F2I + 0, //F2L + 0, //F2D + 0, //D2I + 0, //D2L + 0, //D2F + 0, //I2B + 0, //I2C + 0, //I2S + 0, //LCMP + 0, //FCMPL + 0, //FCMPG + 0, //DCMPL + 0, //DCMPG + 6, //IFEQ + 6, //IFNE + 6, //IFLT + 6, //IFGE + 6, //IFGT + 6, //IFLE + 6, //IF_ICMPEQ + 6, //IF_ICMPNE + 6, //IF_ICMPLT + 6, //IF_ICMPGE + 6, //IF_ICMPGT + 6, //IF_ICMPLE + 6, //IF_ACMPEQ + 6, //IF_ACMPNE + 6, //GOTO + 6, //JSR + 2, //RET + 9, //TABLESWITCH + 10, //LOOKUPSWITCH + 0, //IRETURN + 0, //LRETURN + 0, //FRETURN + 0, //DRETURN + 0, //ARETURN + 0, //RETURN + 4, //GETSTATIC + 4, //PUTSTATIC + 4, //GETFIELD + 4, //PUTFIELD + 5, //INVOKEVIRTUAL + 5, //INVOKESPECIAL + 5, //INVOKESTATIC + 5, //INVOKEINTERFACE + -1, //UNUSED + 3, //NEW + 1, //NEWARRAY + 3, //ANEWARRAY + 0, //ARRAYLENGTH + 0, //ATHROW + 3, //CHECKCAST + 3, //INSTANCEOF + 0, //MONITORENTER + 0, //MONITOREXIT + -1, //WIDE + 11, //MULTIANEWARRAY + 6, //IFNULL + 6, //IFNONNULL + -1, //GOTO_W + -1 //JSR_W + }; + + /** + * Constructs a new {@link CheckCodeAdapter CheckCodeAdapter} object. + * + * @param cv the code visitor to which this adapter must delegate calls. + */ + + public CheckCodeAdapter (final CodeVisitor cv) { + super(cv); + this.labels = new HashMap(); + } + + public void visitInsn (final int opcode) { + checkEnd(); + checkOpcode(opcode, 0); + cv.visitInsn(opcode); + } + + public void visitIntInsn (final int opcode, final int operand) { + checkEnd(); + checkOpcode(opcode, 1); + switch (opcode) { + case Constants.BIPUSH: + checkSignedByte(operand, "Invalid operand"); + break; + case Constants.SIPUSH: + checkSignedShort(operand, "Invalid operand"); + break; + //case Constants.NEWARRAY: + default: + if (operand < Constants.T_BOOLEAN || operand > Constants.T_LONG) { + throw new IllegalArgumentException( + "Invalid operand (must be an array type code T_...): " + operand); + } + } + cv.visitIntInsn(opcode, operand); + } + + public void visitVarInsn (final int opcode, final int var) { + checkEnd(); + checkOpcode(opcode, 2); + checkUnsignedShort(var, "Invalid variable index"); + cv.visitVarInsn(opcode, var); + } + + public void visitTypeInsn (final int opcode, final String desc) { + checkEnd(); + checkOpcode(opcode, 3); + if (desc != null && desc.length() > 0 && desc.charAt(0) == '[') { + checkDesc(desc, false); + } else { + checkInternalName(desc, "type"); + } + if (opcode == Constants.NEW && desc.charAt(0) == '[') { + throw new IllegalArgumentException( + "NEW cannot be used to create arrays: " + desc); + } + cv.visitTypeInsn(opcode, desc); + } + + public void visitFieldInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + checkEnd(); + checkOpcode(opcode, 4); + checkInternalName(owner, "owner"); + checkIdentifier(name, "name"); + checkDesc(desc, false); + cv.visitFieldInsn(opcode, owner, name, desc); + } + + public void visitMethodInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + checkEnd(); + checkOpcode(opcode, 5); + checkInternalName(owner, "owner"); + checkMethodIdentifier(name, "name"); + checkMethodDesc(desc); + cv.visitMethodInsn(opcode, owner, name, desc); + } + + public void visitJumpInsn (final int opcode, final Label label) { + checkEnd(); + checkOpcode(opcode, 6); + checkLabel(label, false, "label"); + cv.visitJumpInsn(opcode, label); + } + + public void visitLabel (final Label label) { + checkEnd(); + checkLabel(label, false, "label"); + if (labels.get(label) != null) { + throw new IllegalArgumentException("Already visited label"); + } else { + labels.put(label, new Integer(labels.size())); + } + cv.visitLabel(label); + } + + public void visitLdcInsn (final Object cst) { + checkEnd(); + checkConstant(cst); + cv.visitLdcInsn(cst); + } + + public void visitIincInsn (final int var, final int increment) { + checkEnd(); + checkUnsignedShort(var, "Invalid variable index"); + checkSignedShort(increment, "Invalid increment"); + cv.visitIincInsn(var, increment); + } + + public void visitTableSwitchInsn ( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + checkEnd(); + if (max < min) { + throw new IllegalArgumentException( + "Max = " + max + " must be greater than or equal to min = " + min); + } + checkLabel(dflt, false, "default label"); + if (labels == null || labels.length != max - min + 1) { + throw new IllegalArgumentException( + "There must be max - min + 1 labels"); + } + for (int i = 0; i < labels.length; ++i) { + checkLabel(labels[i], false, "label at index " + i); + } + cv.visitTableSwitchInsn(min, max, dflt, labels); + } + + public void visitLookupSwitchInsn ( + final Label dflt, + final int keys[], + final Label labels[]) + { + checkEnd(); + checkLabel(dflt, false, "default label"); + if (keys == null || labels == null || keys.length != labels.length) { + throw new IllegalArgumentException( + "There must be the same number of keys and labels"); + } + for (int i = 0; i < labels.length; ++i) { + checkLabel(labels[i], false, "label at index " + i); + } + cv.visitLookupSwitchInsn(dflt, keys, labels); + } + + public void visitMultiANewArrayInsn (final String desc, final int dims) { + checkEnd(); + checkDesc(desc, false); + if (desc.charAt(0) != '[') { + throw new IllegalArgumentException( + "Invalid descriptor (must be an array type descriptor): " + desc); + } + if (dims < 1) { + throw new IllegalArgumentException( + "Invalid dimensions (must be greater than 0): " + dims); + } + if (dims > desc.lastIndexOf('[') + 1) { + throw new IllegalArgumentException( + "Invalid dimensions (must not be greater than dims(desc)): " + dims); + } + cv.visitMultiANewArrayInsn(desc, dims); + } + + public void visitTryCatchBlock ( + final Label start, + final Label end, + final Label handler, + final String type) + { + checkLabel(start, true, "start label"); + checkLabel(end, true, "end label"); + checkLabel(handler, true, "handler label"); + if (type != null) { + checkInternalName(type, "type"); + } + int s = ((Integer)labels.get(start)).intValue(); + int e = ((Integer)labels.get(end)).intValue(); + if (e <= s) { + throw new IllegalArgumentException( + "Invalid start and end labels (end must be greater than start)"); + } + cv.visitTryCatchBlock(start, end, handler, type); + } + + public void visitMaxs (final int maxStack, final int maxLocals) { + checkEnd(); + end = true; + checkUnsignedShort(maxStack, "Invalid max stack"); + checkUnsignedShort(maxLocals, "Invalid max locals"); + cv.visitMaxs(maxStack, maxLocals); + } + + public void visitLocalVariable ( + final String name, + final String desc, + final Label start, + final Label end, + final int index) + { + checkIdentifier(name, "name"); + checkDesc(desc, false); + checkLabel(start, true, "start label"); + checkLabel(end, true, "end label"); + checkUnsignedShort(index, "Invalid variable index"); + int s = ((Integer)labels.get(start)).intValue(); + int e = ((Integer)labels.get(end)).intValue(); + if (e <= s) { + throw new IllegalArgumentException( + "Invalid start and end labels (end must be greater than start)"); + } + cv.visitLocalVariable(name, desc, start, end, index); + } + + public void visitLineNumber (final int line, final Label start) { + checkUnsignedShort(line, "Invalid line number"); + checkLabel(start, true, "start label"); + cv.visitLineNumber(line, start); + } + + public void visitAttribute (Attribute attr) { + if (attr == null) { + throw new IllegalArgumentException( + "Invalid attribute (must not be null)"); + } + } + + // --------------------------------------------------------------------------- + + /** + * Checks that the visitMaxs method has not been called. + */ + + void checkEnd () { + if (end) { + throw new IllegalStateException( + "Cannot visit instructions after visitMaxs has been called."); + } + } + + /** + * Checks that the type of the given opcode is equal to the given type. + * + * @param opcode the opcode to be checked. + * @param type the expected opcode type. + */ + + static void checkOpcode (final int opcode, final int type) { + if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) { + throw new IllegalArgumentException("Invalid opcode: " + opcode); + } + } + + /** + * Checks that the given value is a signed byte. + * + * @param value the value to be checked. + * @param msg an message to be used in case of error. + */ + + static void checkSignedByte (final int value, final String msg) { + if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { + throw new IllegalArgumentException( + msg + " (must be a signed byte): " + value); + } + } + + /** + * Checks that the given value is a signed short. + * + * @param value the value to be checked. + * @param msg an message to be used in case of error. + */ + + static void checkSignedShort (final int value, final String msg) { + if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { + throw new IllegalArgumentException( + msg + " (must be a signed short): " + value); + } + } + + /** + * Checks that the given value is an unsigned short. + * + * @param value the value to be checked. + * @param msg an message to be used in case of error. + */ + + static void checkUnsignedShort (final int value, final String msg) { + if (value < 0 || value > 65535) { + throw new IllegalArgumentException( + msg + " (must be an unsigned short): " + value); + } + } + + /** + * Checks that the given value is an {@link java.lang.Integer Integer}, a + * {@link java.lang.Float Float}, a {@link java.lang.Long Long}, a {@link + * java.lang.Double Double} or a {@link String String}. + * + * @param cst the value to be checked. + */ + + static void checkConstant (final Object cst) { + if (!(cst instanceof Integer) && + !(cst instanceof Float) && + !(cst instanceof Long) && + !(cst instanceof Double) && + !(cst instanceof String)) + { + throw new IllegalArgumentException("Invalid constant: " + cst); + } + } + + /** + * Checks that the given string is a valid Java identifier. + * + * @param name the string to be checked. + * @param msg a message to be used in case of error. + */ + + static void checkIdentifier (final String name, final String msg) { + checkIdentifier(name, 0, -1, msg); + } + + /** + * Checks that the given substring is a valid Java identifier. + * + * @param name the string to be checked. + * @param start index of the first character of the identifier (inclusive). + * @param end index of the last character of the identifier (exclusive). -1 is + * equivalent to name.length() if name is not null. + * @param msg a message to be used in case of error. + */ + + static void checkIdentifier ( + final String name, + final int start, + final int end, + final String msg) + { + if (name == null || (end == -1 ? name.length() <= start : end <= start)) { + throw new IllegalArgumentException( + "Invalid " + msg + " (must not be null or empty)"); + } + if (!Character.isJavaIdentifierStart(name.charAt(start))) { + throw new IllegalArgumentException( + "Invalid " + msg + " (must be a valid Java identifier): " + name); + } + int max = (end == -1 ? name.length() : end); + for (int i = start + 1; i < max; ++i) { + if (!Character.isJavaIdentifierPart(name.charAt(i))) { + throw new IllegalArgumentException( + "Invalid " + msg + " (must be a valid Java identifier): " + name); + } + } + } + + /** + * Checks that the given string is a valid Java identifier or is equal to + * '<init>' or '<clinit>'. + * + * @param name the string to be checked. + * @param msg a message to be used in case of error. + */ + + static void checkMethodIdentifier (final String name, final String msg) { + if (name == null || name.length() == 0) { + throw new IllegalArgumentException( + "Invalid " + msg + " (must not be null or empty)"); + } + if (name.equals("") || name.equals("")) { + return; + } + if (!Character.isJavaIdentifierStart(name.charAt(0))) { + throw new IllegalArgumentException( + "Invalid " + msg + + " (must be a '', '' or a valid Java identifier): " + + name); + } + for (int i = 1; i < name.length(); ++i) { + if (!Character.isJavaIdentifierPart(name.charAt(i))) { + throw new IllegalArgumentException( + "Invalid " + msg + + " (must be '' or '' or a valid Java identifier): " + + name); + } + } + } + + /** + * Checks that the given string is a valid internal class name. + * + * @param name the string to be checked. + * @param msg a message to be used in case of error. + */ + + static void checkInternalName (final String name, final String msg) { + checkInternalName(name, 0, -1, msg); + } + + /** + * Checks that the given substring is a valid internal class name. + * + * @param name the string to be checked. + * @param start index of the first character of the identifier (inclusive). + * @param end index of the last character of the identifier (exclusive). -1 is + * equivalent to name.length() if name is not null. + * @param msg a message to be used in case of error. + */ + + static void checkInternalName ( + final String name, + final int start, + final int end, + final String msg) + { + if (name == null || name.length() == 0) { + throw new IllegalArgumentException( + "Invalid " + msg + " (must not be null or empty)"); + } + int max = (end == -1 ? name.length() : end); + try { + int begin = start; + int slash; + do { + slash = name.indexOf('/', begin + 1); + if (slash == -1 || slash > max) { + slash = max; + } + checkIdentifier(name, begin, slash, null); + begin = slash + 1; + } while (slash != max); + } catch (IllegalArgumentException _) { + throw new IllegalArgumentException( + "Invalid " + msg + + " (must be a fully qualified class name in internal form): " + + name); + } + } + + /** + * Checks that the given string is a valid type descriptor. + * + * @param desc the string to be checked. + * @param canBeVoid true if V can be considered valid. + */ + + static void checkDesc (final String desc, final boolean canBeVoid) { + int end = checkDesc(desc, 0, canBeVoid); + if (end != desc.length()) { + throw new IllegalArgumentException("Invalid descriptor: " + desc); + } + } + + /** + * Checks that a the given substring is a valid type descriptor. + * + * @param desc the string to be checked. + * @param start index of the first character of the identifier (inclusive). + * @param canBeVoid true if V can be considered valid. + * @return the index of the last character of the type decriptor, plus one. + */ + + static int checkDesc ( + final String desc, + final int start, + final boolean canBeVoid) + { + if (desc == null || start >= desc.length()) { + throw new IllegalArgumentException( + "Invalid type descriptor (must not be null or empty)"); + } + int index; + switch (desc.charAt(start)) { + case 'V': + if (canBeVoid) { + return start + 1; + } else { + throw new IllegalArgumentException("Invalid descriptor: " + desc); + } + case 'Z': + case 'C': + case 'B': + case 'S': + case 'I': + case 'F': + case 'J': + case 'D': + return start + 1; + case '[': + index = start + 1; + while (index < desc.length() && desc.charAt(index) == '[') { + ++index; + } + if (index < desc.length()) { + return checkDesc(desc, index, false); + } else { + throw new IllegalArgumentException("Invalid descriptor: " + desc); + } + case 'L': + index = desc.indexOf(';', start); + if (index == -1 || index - start < 2) { + throw new IllegalArgumentException("Invalid descriptor: " + desc); + } + try { + checkInternalName(desc, start + 1, index, null); + } catch (IllegalArgumentException _) { + throw new IllegalArgumentException("Invalid descriptor: " + desc); + } + return index + 1; + default: + throw new IllegalArgumentException("Invalid descriptor: " + desc); + } + } + + /** + * Checks that the given string is a valid method descriptor. + * + * @param desc the string to be checked. + */ + + static void checkMethodDesc (final String desc) { + if (desc == null || desc.length() == 0) { + throw new IllegalArgumentException( + "Invalid method descriptor (must not be null or empty)"); + } + if (desc.charAt(0) != '(' || desc.length() < 3) { + throw new IllegalArgumentException("Invalid descriptor: " + desc); + } + int start = 1; + if (desc.charAt(start) != ')') { + do { + if (desc.charAt(start) == 'V') { + throw new IllegalArgumentException("Invalid descriptor: " + desc); + } + start = checkDesc(desc, start, false); + } while (start < desc.length() && desc.charAt(start) != ')'); + } + start = checkDesc(desc, start + 1, true); + if (start != desc.length()) { + throw new IllegalArgumentException("Invalid descriptor: " + desc); + } + } + + /** + * Checks that the given label is not null. This method can also check that + * the label has been visited. + * + * @param label the label to be checked. + * @param checkVisited true to check that the label has been visited. + * @param msg a message to be used in case of error. + */ + + void checkLabel ( + final Label label, + final boolean checkVisited, + final String msg) + { + if (label == null) { + throw new IllegalArgumentException( + "Invalid " + msg + " (must not be null)"); + } + if (checkVisited && labels.get(label) == null) { + throw new IllegalArgumentException( + "Invalid " + msg + " (must be visited first)"); + } + } +} diff --git a/org/objectweb/asm/util/DumpClassVisitor.java b/org/objectweb/asm/util/DumpClassVisitor.java new file mode 100644 index 0000000000..b19a022fca --- /dev/null +++ b/org/objectweb/asm/util/DumpClassVisitor.java @@ -0,0 +1,532 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.util; + +import java.io.FileInputStream; +import java.io.PrintWriter; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.CodeVisitor; +import org.objectweb.asm.Constants; +import org.objectweb.asm.attrs.Dumpable; + +/** + * A {@link PrintClassVisitor PrintClassVisitor} that prints the ASM code that + * generates the classes it visits. This class visitor can be used to quickly + * write ASM code to generate some given bytecode: + *
    + *
  • write the Java source code equivalent to the bytecode you want to + * generate;
  • + *
  • compile it with javac;
  • + *
  • make a {@link DumpClassVisitor DumpClassVisitor} visit this compiled + * class (see the {@link #main main} method);
  • + *
  • edit the generated source code, if necessary.
  • + *
+ * The source code printed when visiting the Hello class is the + * following: + *

+ *

+ *
+ * import org.objectweb.asm.*;
+ * import java.io.FileOutputStream;
+ *
+ * public class Dump implements Constants {
+ *
+ * public static void main (String[] args) throws Exception {
+ *
+ * ClassWriter cw = new ClassWriter(false);
+ * CodeVisitor cv;
+ *
+ * cw.visit(ACC_PUBLIC + ACC_SUPER, "Hello", "java/lang/Object", null, "Hello.java");
+ *
+ * {
+ * cv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+ * cv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ * cv.visitLdcInsn("hello");
+ * cv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
+ * cv.visitInsn(RETURN);
+ * cv.visitMaxs(2, 1);
+ * }
+ * {
+ * cv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ * cv.visitVarInsn(ALOAD, 0);
+ * cv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+ * cv.visitInsn(RETURN);
+ * cv.visitMaxs(1, 1);
+ * }
+ * cw.visitEnd();
+ *
+ * FileOutputStream os = new FileOutputStream("Dumped.class");
+ * os.write(cw.toByteArray());
+ * os.close();
+ * }
+ * }
+ * 
+ *
+ * where Hello is defined by: + *

+ *

+ *
+ * public class Hello {
+ *
+ *   public static void main (String[] args) {
+ *     System.out.println("hello");
+ *   }
+ * }
+ * 
+ *
+ */ + +public class DumpClassVisitor extends PrintClassVisitor { + + private static final int ACCESS_CLASS = 262144; + private static final int ACCESS_FIELD = 524288; + + /** + * Prints the ASM source code to generate the given class to the standard + * output. + *

+ * Usage: DumpClassVisitor + * <fully qualified class name or class file name> + * + * @param args the command line arguments. + * + * @throws Exception if the class cannot be found, or if an IO exception + * occurs. + */ + + public static void main (final String[] args) throws Exception { + if (args.length == 0) { + System.err.println("Prints the ASM code to generate the given class."); + System.err.println("Usage: DumpClassVisitor " + + ""); + System.exit(-1); + } + ClassReader cr; + if (args[0].endsWith(".class")) { + cr = new ClassReader(new FileInputStream(args[0])); + } else { + cr = new ClassReader(args[0]); + } + cr.accept(new DumpClassVisitor(new PrintWriter(System.out)), + PrintClassVisitor.DEFAULT_ATTRIBUTES, true); + } + + /** + * Constructs a new {@link DumpClassVisitor DumpClassVisitor} object. + * + * @param pw the print writer to be used to print the class. + */ + + public DumpClassVisitor (final PrintWriter pw) { + super(pw); + } + + public void visit ( + final int access, + final String name, + final String superName, + final String[] interfaces, + final String sourceFile) + { + text.add("import org.objectweb.asm.*;\n"); + text.add("import org.objectweb.asm.attrs.*;\n"); + text.add("import java.io.FileOutputStream;\n\n"); + text.add("public class Dump implements Constants {\n\n"); + text.add("public static void main (String[] args) throws Exception {\n\n"); + text.add("ClassWriter cw = new ClassWriter(false);\n"); + text.add("CodeVisitor cv;\n\n"); + + buf.setLength(0); + buf.append("cw.visit("); + appendAccess(access | ACCESS_CLASS); + buf.append(", "); + appendConstant(buf, name); + buf.append(", "); + appendConstant(buf, superName); + buf.append(", "); + if (interfaces != null && interfaces.length > 0) { + buf.append("new String[] {"); + for (int i = 0; i < interfaces.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendConstant(buf, interfaces[i]); + } + buf.append(" }"); + } else { + buf.append("null"); + } + buf.append(", "); + appendConstant(buf, sourceFile); + buf.append(");\n\n"); + text.add(buf.toString()); + } + + public void visitInnerClass ( + final String name, + final String outerName, + final String innerName, + final int access) + { + buf.setLength(0); + buf.append("cw.visitInnerClass("); + appendConstant(buf, name); + buf.append(", "); + appendConstant(buf, outerName); + buf.append(", "); + appendConstant(buf, innerName); + buf.append(", "); + appendAccess(access); + buf.append(");\n\n"); + text.add(buf.toString()); + } + + public void visitField ( + final int access, + final String name, + final String desc, + final Object value, + final Attribute attrs) + { + buf.setLength(0); + + if (attrs != null) { + buf.append("// FIELD ATTRIBUTES\n"); + Attribute a = attrs; + int n = 1; + while (a != null) { + if (a instanceof Dumpable) { + ((Dumpable)a).dump(buf, "attrs" + n, null); + if (n > 1) { + buf.append("attrs" + (n - 1) + " = attrs" + n + ";\n"); + } + } else { + buf.append("// WARNING! skipped non standard field attribute of type "); + buf.append(a.type).append("\n"); + } + n++; + a = a.next; + } + } + + buf.append("cw.visitField("); + appendAccess(access | ACCESS_FIELD); + buf.append(", "); + appendConstant(buf, name); + buf.append(", "); + appendConstant(buf, desc); + buf.append(", "); + appendConstant(buf, value); + + if (attrs==null) { + buf.append(", null);\n\n"); + } else { + buf.append(", attrs1);\n\n"); + } + + text.add(buf.toString()); + } + + public CodeVisitor visitMethod ( + final int access, + final String name, + final String desc, + final String[] exceptions, + final Attribute attrs) + { + buf.setLength(0); + + buf.append("{\n"); + + if (attrs != null) { + buf.append("// METHOD ATTRIBUTES\n"); + Attribute a = attrs; + int n = 1; + while (a != null) { + if (a instanceof Dumpable) { + ((Dumpable)a).dump(buf, "attrs" + n, null); + if (n > 1) { + buf.append("attrs" + (n - 1) + " = attrs" + n + ";\n"); + } + } else { + buf.append("// WARNING! skipped non standard method attribute of type "); + buf.append(a.type).append("\n"); + } + n++; + a = a.next; + } + } + + buf.append("cv = cw.visitMethod("); + appendAccess(access); + buf.append(", "); + appendConstant(buf, name); + buf.append(", "); + appendConstant(buf, desc); + buf.append(", "); + if (exceptions != null && exceptions.length > 0) { + buf.append("new String[] {"); + for (int i = 0; i < exceptions.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendConstant(buf, exceptions[i]); + } + buf.append(" }"); + } else { + buf.append("null"); + } + if (attrs==null) { + buf.append(", null);\n"); + } else { + buf.append(", attrs1);\n"); + } + + text.add(buf.toString()); + PrintCodeVisitor pcv = new DumpCodeVisitor(); + text.add(pcv.getText()); + text.add("}\n"); + return pcv; + } + + public void visitAttribute (final Attribute attr) { + buf.setLength(0); + if (attr instanceof Dumpable) { + buf.append("{\n"); + buf.append("// CLASS ATRIBUTE\n"); + ((Dumpable)attr).dump(buf, "attr", null); + buf.append("cw.visitAttribute(attr);\n"); + buf.append("}\n"); + } else { + buf.append("// WARNING! skipped a non standard class attribute of type \""); + buf.append(attr.type).append("\"\n"); + } + text.add(buf.toString()); + } + + public void visitEnd () { + text.add("cw.visitEnd();\n\n"); + text.add("FileOutputStream os = new FileOutputStream(\"Dumped.class\");\n"); + text.add("os.write(cw.toByteArray());\n"); + text.add("os.close();\n"); + text.add("}\n"); + text.add("}\n"); + super.visitEnd(); + } + + /** + * Appends a string representation of the given access modifiers to {@link + * #buf buf}. + * + * @param access some access modifiers. + */ + + void appendAccess (final int access) { + boolean first = true; + if ((access & Constants.ACC_PUBLIC) != 0) { + buf.append("ACC_PUBLIC"); + first = false; + } + if ((access & Constants.ACC_PRIVATE) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_PRIVATE"); + first = false; + } + if ((access & Constants.ACC_PROTECTED) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_PROTECTED"); + first = false; + } + if ((access & Constants.ACC_FINAL) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_FINAL"); + first = false; + } + if ((access & Constants.ACC_STATIC) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_STATIC"); + first = false; + } + if ((access & Constants.ACC_SYNCHRONIZED) != 0) { + if (!first) { + buf.append(" + "); + } + if ((access & ACCESS_CLASS) != 0) { + buf.append("ACC_SUPER"); + } else { + buf.append("ACC_SYNCHRONIZED"); + } + first = false; + } + if ((access & Constants.ACC_VOLATILE) != 0 && (access & ACCESS_FIELD) != 0 ) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_VOLATILE"); + first = false; + } + if ((access & Constants.ACC_BRIDGE) != 0 && + (access & ACCESS_CLASS) == 0 && (access & ACCESS_FIELD) == 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_BRIDGE"); + first = false; + } + if ((access & Constants.ACC_VARARGS) != 0 && + (access & ACCESS_CLASS) == 0 && (access & ACCESS_FIELD) == 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_VARARGS"); + first = false; + } + if ((access & Constants.ACC_TRANSIENT) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_TRANSIENT"); + first = false; + } + if ((access & Constants.ACC_NATIVE) != 0 && + (access & ACCESS_CLASS) == 0 && + (access & ACCESS_FIELD) == 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_NATIVE"); + first = false; + } + if ((access & Constants.ACC_ENUM) != 0 && + ((access & ACCESS_CLASS) != 0 || (access & ACCESS_FIELD) != 0)) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_ENUM"); + first = false; + } + if ((access & Constants.ACC_ABSTRACT) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_ABSTRACT"); + first = false; + } + if ((access & Constants.ACC_INTERFACE) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_INTERFACE"); + first = false; + } + if ((access & Constants.ACC_STRICT) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_STRICT"); + first = false; + } + if ((access & Constants.ACC_SYNTHETIC) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_SYNTHETIC"); + first = false; + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_DEPRECATED"); + first = false; + } + if (first) { + buf.append("0"); + } + } + + /** + * Appends a string representation of the given constant to the given buffer. + * + * @param buf a string buffer. + * @param cst an {@link java.lang.Integer Integer}, {@link java.lang.Float + * Float}, {@link java.lang.Long Long}, {@link java.lang.Double Double} + * or {@link String String} object. May be null. + */ + + static void appendConstant (final StringBuffer buf, final Object cst) { + if (cst == null) { + buf.append("null"); + } else if (cst instanceof String) { + String s = (String)cst; + buf.append("\""); + for (int i = 0; i < s.length(); ++i) { + char c = s.charAt(i); + if (c == '\n') { + buf.append("\\n"); + } else if (c == '\\') { + buf.append("\\\\"); + } else if (c == '"') { + buf.append("\\\""); + } else { + buf.append(c); + } + } + buf.append("\""); + } else if (cst instanceof Integer) { + buf.append("new Integer(") + .append(cst) + .append(")"); + } else if (cst instanceof Float) { + buf.append("new Float(") + .append(cst) + .append("F)"); + } else if (cst instanceof Long) { + buf.append("new Long(") + .append(cst) + .append("L)"); + } else if (cst instanceof Double) { + buf.append("new Double(") + .append(cst) + .append(")"); + } + } +} diff --git a/org/objectweb/asm/util/DumpCodeVisitor.java b/org/objectweb/asm/util/DumpCodeVisitor.java new file mode 100644 index 0000000000..823d160bba --- /dev/null +++ b/org/objectweb/asm/util/DumpCodeVisitor.java @@ -0,0 +1,306 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.util; + +import org.objectweb.asm.Label; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.attrs.Dumpable; + +import java.util.HashMap; + +/** + * A {@link PrintCodeVisitor PrintCodeVisitor} that prints the ASM code that + * generates the code it visits. + */ + +public class DumpCodeVisitor extends PrintCodeVisitor { + + /** + * The label names. This map associate String values to Label keys. + */ + + private final HashMap labelNames; + + /** + * Constructs a new {@link DumpCodeVisitor DumpCodeVisitor} object. + */ + + public DumpCodeVisitor () { + this.labelNames = new HashMap(); + } + + public void printInsn (final int opcode) { + buf.append("cv.visitInsn("). + append(OPCODES[opcode]). + append(");\n"); + } + + public void printIntInsn (final int opcode, final int operand) { + buf.append("cv.visitIntInsn("). + append(OPCODES[opcode]). + append(", "). + append(operand). + append(");\n"); + } + + public void printVarInsn (final int opcode, final int var) { + buf.append("cv.visitVarInsn("). + append(OPCODES[opcode]). + append(", "). + append(var). + append(");\n"); + } + + public void printTypeInsn (final int opcode, final String desc) { + buf.append("cv.visitTypeInsn("). + append(OPCODES[opcode]). + append(", "); + DumpClassVisitor.appendConstant(buf, desc); + buf.append(");\n"); + } + + public void printFieldInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + buf.append("cv.visitFieldInsn(") + .append(OPCODES[opcode]) + .append(", "); + DumpClassVisitor.appendConstant(buf, owner); + buf.append(", "); + DumpClassVisitor.appendConstant(buf, name); + buf.append(", "); + DumpClassVisitor.appendConstant(buf, desc); + buf.append(");\n"); + } + + public void printMethodInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + buf.append("cv.visitMethodInsn(") + .append(OPCODES[opcode]) + .append(", "); + DumpClassVisitor.appendConstant(buf, owner); + buf.append(", "); + DumpClassVisitor.appendConstant(buf, name); + buf.append(", "); + DumpClassVisitor.appendConstant(buf, desc); + buf.append(");\n"); + } + + public void printJumpInsn (final int opcode, final Label label) { + declareLabel(label); + buf.append("cv.visitJumpInsn(") + .append(OPCODES[opcode]) + .append(", "); + appendLabel(label); + buf.append(");\n"); + } + + public void printLabel (final Label label) { + declareLabel(label); + buf.append("cv.visitLabel("); + appendLabel(label); + buf.append(");\n"); + } + + public void printLdcInsn (final Object cst) { + buf.append("cv.visitLdcInsn("); + DumpClassVisitor.appendConstant(buf, cst); + buf.append(");\n"); + } + + public void printIincInsn (final int var, final int increment) { + buf.append("cv.visitIincInsn(") + .append(var) + .append(", ") + .append(increment) + .append(");\n"); + } + + public void printTableSwitchInsn ( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + for (int i = 0; i < labels.length; ++i) { + declareLabel(labels[i]); + } + declareLabel(dflt); + + buf.append("cv.visitTableSwitchInsn(") + .append(min) + .append(", ") + .append(max) + .append(", "); + appendLabel(dflt); + buf.append(", new Label[] {"); + for (int i = 0; i < labels.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendLabel(labels[i]); + } + buf.append(" });\n"); + } + + public void printLookupSwitchInsn ( + final Label dflt, + final int keys[], + final Label labels[]) + { + for (int i = 0; i < labels.length; ++i) { + declareLabel(labels[i]); + } + declareLabel(dflt); + + buf.append("cv.visitLookupSwitchInsn("); + appendLabel(dflt); + buf.append(", new int[] {"); + for (int i = 0; i < keys.length; ++i) { + buf.append(i == 0 ? " " : ", ").append(keys[i]); + } + buf.append(" }, new Label[] {"); + for (int i = 0; i < labels.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendLabel(labels[i]); + } + buf.append(" });\n"); + } + + public void printMultiANewArrayInsn (final String desc, final int dims) { + buf.append("cv.visitMultiANewArrayInsn("); + DumpClassVisitor.appendConstant(buf, desc); + buf.append(", ") + .append(dims) + .append(");\n"); + } + + public void printTryCatchBlock ( + final Label start, + final Label end, + final Label handler, + final String type) + { + buf.append("cv.visitTryCatchBlock("); + appendLabel(start); + buf.append(", "); + appendLabel(end); + buf.append(", "); + appendLabel(handler); + buf.append(", "); + DumpClassVisitor.appendConstant(buf, type); + buf.append(");\n"); + } + + public void printMaxs (final int maxStack, final int maxLocals) { + buf.append("cv.visitMaxs(") + .append(maxStack) + .append(", ") + .append(maxLocals) + .append(");\n"); + } + + public void printLocalVariable ( + final String name, + final String desc, + final Label start, + final Label end, + final int index) + { + buf.append("cv.visitLocalVariable("); + DumpClassVisitor.appendConstant(buf, name); + buf.append(", "); + DumpClassVisitor.appendConstant(buf, desc); + buf.append(", "); + appendLabel(start); + buf.append(", "); + appendLabel(end); + buf.append(", ").append(index).append(");\n"); + } + + public void printLineNumber (final int line, final Label start) { + buf.append("cv.visitLineNumber(") + .append(line) + .append(", "); + appendLabel(start); + buf.append(");\n"); + } + + public void printAttribute (final Attribute attr) { + if (attr instanceof Dumpable) { + buf.append("// CODE ATTRIBUTE\n"); + ((Dumpable)attr).dump(buf, "cv", labelNames); + } else { + buf.append("// WARNING! skipped a non standard code attribute of type \""); + buf.append(attr.type).append("\"\n"); + } + } + + /** + * Appends a declaration of the given label to {@link #buf buf}. This + * declaration is of the form "Label lXXX = new Label();". Does nothing + * if the given label has already been declared. + * + * @param l a label. + */ + + private void declareLabel (final Label l) { + String name = (String)labelNames.get(l); + if (name == null) { + name = "l" + labelNames.size(); + labelNames.put(l, name); + buf.append("Label ") + .append(name) + .append(" = new Label();\n"); + } + } + + /** + * Appends the name of the given label to {@link #buf buf}. The given label + * must already have a name. One way to ensure this is to always call + * {@link #declareLabel declared} before calling this method. + * + * @param l a label. + */ + + private void appendLabel (final Label l) { + buf.append((String)labelNames.get(l)); + } +} diff --git a/org/objectweb/asm/util/PrintClassVisitor.java b/org/objectweb/asm/util/PrintClassVisitor.java new file mode 100644 index 0000000000..8d44c07b1e --- /dev/null +++ b/org/objectweb/asm/util/PrintClassVisitor.java @@ -0,0 +1,132 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.util; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.attrs.AnnotationDefaultAttribute; +import org.objectweb.asm.attrs.EnclosingMethodAttribute; +import org.objectweb.asm.attrs.RuntimeInvisibleAnnotations; +import org.objectweb.asm.attrs.RuntimeInvisibleParameterAnnotations; +import org.objectweb.asm.attrs.RuntimeVisibleAnnotations; +import org.objectweb.asm.attrs.RuntimeVisibleParameterAnnotations; +import org.objectweb.asm.attrs.SignatureAttribute; +import org.objectweb.asm.attrs.SourceDebugExtensionAttribute; +import org.objectweb.asm.attrs.StackMapAttribute; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * An abstract class visitor that prints the classes it visits. + */ + +public abstract class PrintClassVisitor implements ClassVisitor { + + public static final Attribute[] DEFAULT_ATTRIBUTES = new Attribute[] { + new AnnotationDefaultAttribute(), + new RuntimeInvisibleAnnotations(), + new RuntimeInvisibleParameterAnnotations(), + new RuntimeVisibleAnnotations(), + new RuntimeVisibleParameterAnnotations(), + new StackMapAttribute(), + new SourceDebugExtensionAttribute(), + new SignatureAttribute(), + new EnclosingMethodAttribute() + }; + + /** + * The text to be printed. Since the code of methods is not necessarily + * visited in sequential order, one method after the other, but can be + * interlaced (some instructions from method one, then some instructions from + * method two, then some instructions from method one again...), it is not + * possible to print the visited instructions directly to a sequential + * stream. A class is therefore printed in a two steps process: a string tree + * is constructed during the visit, and printed to a sequential stream at the + * end of the visit. This string tree is stored in this field, as a string + * list that can contain other string lists, which can themselves contain + * other string lists, and so on. + */ + + protected final List text; + + /** + * A buffer that can be used to create strings. + */ + + protected final StringBuffer buf; + + /** + * The print writer to be used to print the class. + */ + + protected final PrintWriter pw; + + /** + * Constructs a new {@link PrintClassVisitor PrintClassVisitor} object. + * + * @param pw the print writer to be used to print the class. + */ + + public PrintClassVisitor (final PrintWriter pw) { + this.text = new ArrayList(); + this.buf = new StringBuffer(); + this.pw = pw; + } + + public void visitEnd () { + printList(text); + pw.flush(); + } + + /** + * Prints the given string tree to {@link #pw pw}. + * + * @param l a string tree, i.e., a string list that can contain other string + * lists, and so on recursively. + */ + + private void printList (final List l) { + for (int i = 0; i < l.size(); ++i) { + Object o = l.get(i); + if (o instanceof List) { + printList((List)o); + } else { + pw.print(o.toString()); + } + } + } +} diff --git a/org/objectweb/asm/util/PrintCodeVisitor.java b/org/objectweb/asm/util/PrintCodeVisitor.java new file mode 100644 index 0000000000..9577125b9f --- /dev/null +++ b/org/objectweb/asm/util/PrintCodeVisitor.java @@ -0,0 +1,693 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.util; + +import org.objectweb.asm.CodeVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.Attribute; + +import java.util.ArrayList; +import java.util.List; + +/** + * An abstract code visitor that prints the code it visits. Each + * visitXXX method clears the {@link #buf buf} buffer, calls the + * corresponding printXXX method, and then adds the buffer's + * content to the {@link #text text} list. In order to provide a concrete + * print code visitor, one must implement the printXXX methods + * in a sub class of this class. Each method should print the instructions it + * visits in {@link #buf buf}. + */ + +public abstract class PrintCodeVisitor implements CodeVisitor { + + /** + * The text to be printed. See {@link PrintClassVisitor#text text}. + */ + + protected final List text; + + /** + * A buffer used to convert instructions to strings. + */ + + protected final StringBuffer buf; + + /** + * The names of the Java Virtual Machine opcodes. + */ + + public final static String[] OPCODES = { + "NOP", + "ACONST_NULL", + "ICONST_M1", + "ICONST_0", + "ICONST_1", + "ICONST_2", + "ICONST_3", + "ICONST_4", + "ICONST_5", + "LCONST_0", + "LCONST_1", + "FCONST_0", + "FCONST_1", + "FCONST_2", + "DCONST_0", + "DCONST_1", + "BIPUSH", + "SIPUSH", + "LDC", + null, + null, + "ILOAD", + "LLOAD", + "FLOAD", + "DLOAD", + "ALOAD", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "IALOAD", + "LALOAD", + "FALOAD", + "DALOAD", + "AALOAD", + "BALOAD", + "CALOAD", + "SALOAD", + "ISTORE", + "LSTORE", + "FSTORE", + "DSTORE", + "ASTORE", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "IASTORE", + "LASTORE", + "FASTORE", + "DASTORE", + "AASTORE", + "BASTORE", + "CASTORE", + "SASTORE", + "POP", + "POP2", + "DUP", + "DUP_X1", + "DUP_X2", + "DUP2", + "DUP2_X1", + "DUP2_X2", + "SWAP", + "IADD", + "LADD", + "FADD", + "DADD", + "ISUB", + "LSUB", + "FSUB", + "DSUB", + "IMUL", + "LMUL", + "FMUL", + "DMUL", + "IDIV", + "LDIV", + "FDIV", + "DDIV", + "IREM", + "LREM", + "FREM", + "DREM", + "INEG", + "LNEG", + "FNEG", + "DNEG", + "ISHL", + "LSHL", + "ISHR", + "LSHR", + "IUSHR", + "LUSHR", + "IAND", + "LAND", + "IOR", + "LOR", + "IXOR", + "LXOR", + "IINC", + "I2L", + "I2F", + "I2D", + "L2I", + "L2F", + "L2D", + "F2I", + "F2L", + "F2D", + "D2I", + "D2L", + "D2F", + "I2B", + "I2C", + "I2S", + "LCMP", + "FCMPL", + "FCMPG", + "DCMPL", + "DCMPG", + "IFEQ", + "IFNE", + "IFLT", + "IFGE", + "IFGT", + "IFLE", + "IF_ICMPEQ", + "IF_ICMPNE", + "IF_ICMPLT", + "IF_ICMPGE", + "IF_ICMPGT", + "IF_ICMPLE", + "IF_ACMPEQ", + "IF_ACMPNE", + "GOTO", + "JSR", + "RET", + "TABLESWITCH", + "LOOKUPSWITCH", + "IRETURN", + "LRETURN", + "FRETURN", + "DRETURN", + "ARETURN", + "RETURN", + "GETSTATIC", + "PUTSTATIC", + "GETFIELD", + "PUTFIELD", + "INVOKEVIRTUAL", + "INVOKESPECIAL", + "INVOKESTATIC", + "INVOKEINTERFACE", + null, + "NEW", + "NEWARRAY", + "ANEWARRAY", + "ARRAYLENGTH", + "ATHROW", + "CHECKCAST", + "INSTANCEOF", + "MONITORENTER", + "MONITOREXIT", + null, + "MULTIANEWARRAY", + "IFNULL", + "IFNONNULL", + null, + null + }; + + /** + * Constructs a new {@link PrintCodeVisitor PrintCodeVisitor} object. + */ + + public PrintCodeVisitor () { + this.buf = new StringBuffer(); + this.text = new ArrayList(); + } + + public void visitInsn (final int opcode) { + buf.setLength(0); + printInsn(opcode); + text.add(buf.toString()); + } + + public void visitIntInsn (final int opcode, final int operand) { + buf.setLength(0); + printIntInsn(opcode, operand); + text.add(buf.toString()); + } + + public void visitVarInsn (final int opcode, final int var) { + buf.setLength(0); + printVarInsn(opcode, var); + text.add(buf.toString()); + } + + public void visitTypeInsn (final int opcode, final String desc) { + buf.setLength(0); + printTypeInsn(opcode, desc); + text.add(buf.toString()); + } + + public void visitFieldInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + buf.setLength(0); + printFieldInsn(opcode, owner, name, desc); + text.add(buf.toString()); + } + + public void visitMethodInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + buf.setLength(0); + printMethodInsn(opcode, owner, name, desc); + text.add(buf.toString()); + } + + public void visitJumpInsn (final int opcode, final Label label) { + buf.setLength(0); + printJumpInsn(opcode, label); + text.add(buf.toString()); + } + + public void visitLabel (final Label label) { + buf.setLength(0); + printLabel(label); + text.add(buf.toString()); + } + + public void visitLdcInsn (final Object cst) { + buf.setLength(0); + printLdcInsn(cst); + text.add(buf.toString()); + } + + public void visitIincInsn (final int var, final int increment) { + buf.setLength(0); + printIincInsn(var, increment); + text.add(buf.toString()); + } + + public void visitTableSwitchInsn ( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + buf.setLength(0); + printTableSwitchInsn(min, max, dflt, labels); + text.add(buf.toString()); + } + + public void visitLookupSwitchInsn ( + final Label dflt, + final int keys[], + final Label labels[]) + { + buf.setLength(0); + printLookupSwitchInsn(dflt, keys, labels); + text.add(buf.toString()); + } + + public void visitMultiANewArrayInsn (final String desc, final int dims) { + buf.setLength(0); + printMultiANewArrayInsn(desc, dims); + text.add(buf.toString()); + } + + public void visitTryCatchBlock ( + final Label start, + final Label end, + final Label handler, + final String type) + { + buf.setLength(0); + printTryCatchBlock(start, end, handler, type); + text.add(buf.toString()); + } + + public void visitMaxs (final int maxStack, final int maxLocals) { + buf.setLength(0); + printMaxs(maxStack, maxLocals); + text.add(buf.toString()); + } + + public void visitLocalVariable ( + final String name, + final String desc, + final Label start, + final Label end, + final int index) + { + buf.setLength(0); + printLocalVariable(name, desc, start, end, index); + text.add(buf.toString()); + } + + public void visitLineNumber (final int line, final Label start) { + buf.setLength(0); + printLineNumber(line, start); + text.add(buf.toString()); + } + + public void visitAttribute (final Attribute attr) { + buf.setLength(0); + printAttribute(attr); + text.add(buf.toString()); + } + + /** + * Returns the code printed by this code visitor. + * + * @return the code printed by this code visitor. See {@link + * PrintClassVisitor#text text}. + */ + + public List getText () { + return text; + } + + /** + * Prints a zero operand instruction. + * + * @param opcode the opcode of the instruction to be printed. This opcode is + * either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, + * ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, + * FCONST_2, DCONST_0, DCONST_1, + * + * IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, + * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, + * SASTORE, + * + * POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, + * + * IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, + * DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, + * FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, + * LOR, IXOR, LXOR, + * + * I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, + * I2S, + * + * LCMP, FCMPL, FCMPG, DCMPL, DCMPG, + * + * IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, RETURN, + * + * ARRAYLENGTH, + * + * ATHROW, + * + * MONITORENTER, or MONITOREXIT. + */ + + public abstract void printInsn (final int opcode); + + /** + * Prints an instruction with a single int operand. + * + * @param opcode the opcode of the instruction to be printed. This opcode is + * either BIPUSH, SIPUSH or NEWARRAY. + * @param operand the operand of the instruction to be printed. + */ + + public abstract void printIntInsn (final int opcode, final int operand); + + /** + * Prints a local variable instruction. A local variable instruction is an + * instruction that loads or stores the value of a local variable. + * + * @param opcode the opcode of the local variable instruction to be printed. + * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, + * LSTORE, FSTORE, DSTORE, ASTORE or RET. + * @param var the operand of the instruction to be printed. This operand is + * the index of a local variable. + */ + + public abstract void printVarInsn (final int opcode, final int var); + + /** + * Prints a type instruction. A type instruction is an instruction that + * takes a type descriptor as parameter. + * + * @param opcode the opcode of the type instruction to be printed. This opcode + * is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. + * @param desc the operand of the instruction to be printed. This operand is + * must be a fully qualified class name in internal form, or a the type + * descriptor of an array type (see {@link org.objectweb.asm.Type Type}). + */ + + public abstract void printTypeInsn (final int opcode, final String desc); + + /** + * Prints a field instruction. A field instruction is an instruction that + * loads or stores the value of a field of an object. + * + * @param opcode the opcode of the type instruction to be printed. This opcode + * is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. + * @param owner the internal name of the field's owner class (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). + * @param name the field's name. + * @param desc the field's descriptor (see {@link org.objectweb.asm.Type + * Type}). + */ + + public abstract void printFieldInsn ( + final int opcode, + final String owner, + final String name, + final String desc); + + /** + * Prints a method instruction. A method instruction is an instruction that + * invokes a method. + * + * @param opcode the opcode of the type instruction to be printed. This opcode + * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner the internal name of the method's owner class (see {@link + * org.objectweb.asm.Type#getInternalName getInternalName}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link org.objectweb.asm.Type + * Type}). + */ + + public abstract void printMethodInsn ( + final int opcode, + final String owner, + final String name, + final String desc); + + /** + * Prints a jump instruction. A jump instruction is an instruction that may + * jump to another instruction. + * + * @param opcode the opcode of the type instruction to be printed. This opcode + * is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, + * IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, + * GOTO, JSR, IFNULL or IFNONNULL. + * @param label the operand of the instruction to be printed. This operand is + * a label that designates the instruction to which the jump instruction + * may jump. + */ + + public abstract void printJumpInsn (final int opcode, final Label label); + + /** + * Prints a label. A label designates the instruction that will be visited + * just after it. + * + * @param label a {@link Label Label} object. + */ + + public abstract void printLabel (final Label label); + + /** + * Prints a LDC instruction. + * + * @param cst the constant to be loaded on the stack. This parameter must be + * a non null {@link java.lang.Integer Integer}, a {@link java.lang.Float + * Float}, a {@link java.lang.Long Long}, a {@link java.lang.Double + * Double} or a {@link String String}. + */ + + public abstract void printLdcInsn (final Object cst); + + /** + * Prints an IINC instruction. + * + * @param var index of the local variable to be incremented. + * @param increment amount to increment the local variable by. + */ + + public abstract void printIincInsn (final int var, final int increment); + + /** + * Prints a TABLESWITCH instruction. + * + * @param min the minimum key value. + * @param max the maximum key value. + * @param dflt beginning of the default handler block. + * @param labels beginnings of the handler blocks. labels[i] is the + * beginning of the handler block for the min + i key. + */ + + public abstract void printTableSwitchInsn ( + final int min, + final int max, + final Label dflt, + final Label labels[]); + + /** + * Prints a LOOKUPSWITCH instruction. + * + * @param dflt beginning of the default handler block. + * @param keys the values of the keys. + * @param labels beginnings of the handler blocks. labels[i] is the + * beginning of the handler block for the keys[i] key. + */ + + public abstract void printLookupSwitchInsn ( + final Label dflt, + final int keys[], + final Label labels[]); + + /** + * Prints a MULTIANEWARRAY instruction. + * + * @param desc an array type descriptor (see {@link org.objectweb.asm.Type + * Type}). + * @param dims number of dimensions of the array to allocate. + */ + + public abstract void printMultiANewArrayInsn ( + final String desc, + final int dims); + + /** + * Prints a try catch block. + * + * @param start beginning of the exception handler's scope (inclusive). + * @param end end of the exception handler's scope (exclusive). + * @param handler beginning of the exception handler's code. + * @param type internal name of the type of exceptions handled by the handler, + * or null to catch any exceptions (for "finally" blocks). + */ + + public abstract void printTryCatchBlock ( + final Label start, + final Label end, + final Label handler, + final String type); + + /** + * Prints the maximum stack size and the maximum number of local variables of + * the method. + * + * @param maxStack maximum stack size of the method. + * @param maxLocals maximum number of local variables for the method. + */ + + public abstract void printMaxs (final int maxStack, final int maxLocals); + + /** + * Prints a local variable declaration. + * + * @param name the name of a local variable. + * @param desc the type descriptor of this local variable. + * @param start the first instruction corresponding to the scope of this + * local variable (inclusive). + * @param end the last instruction corresponding to the scope of this + * local variable (exclusive). + * @param index the local variable's index. + */ + + public abstract void printLocalVariable ( + final String name, + final String desc, + final Label start, + final Label end, + final int index); + + /** + * Prints a line number declaration. + * + * @param line a line number. This number refers to the source file + * from which the class was compiled. + * @param start the first instruction corresponding to this line number. + */ + + public abstract void printLineNumber (final int line, final Label start); + + /** + * Prints a non standard code attribute. + * + * @param attr a non standard code attribute. + */ + + public abstract void printAttribute (final Attribute attr); +} \ No newline at end of file diff --git a/org/objectweb/asm/util/TraceClassVisitor.java b/org/objectweb/asm/util/TraceClassVisitor.java new file mode 100644 index 0000000000..ec4fc51881 --- /dev/null +++ b/org/objectweb/asm/util/TraceClassVisitor.java @@ -0,0 +1,366 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.util; + +import java.io.FileInputStream; +import java.io.PrintWriter; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.CodeVisitor; +import org.objectweb.asm.Constants; + +/** + * A {@link PrintClassVisitor PrintClassVisitor} that prints a disassembled + * view of the classes it visits. This class visitor can be used alone (see the + * {@link #main main} method) to disassemble a class. It can also be used in + * the middle of class visitor chain to trace the class that is visited at a + * given point in this chain. This may be uselful for debugging purposes. + *

+ * The trace printed when visiting the Hello class is the following: + *

+ *

+ *
+ * // compiled from Hello.java
+ * public class Hello {
+ *
+ *   public static main ([Ljava/lang/String;)V
+ *     GETSTATIC java/lang/System out Ljava/io/PrintStream;
+ *     LDC "hello"
+ *     INVOKEVIRTUAL java/io/PrintStream println (Ljava/lang/String;)V
+ *     RETURN
+ *     MAXSTACK = 2
+ *     MAXLOCALS = 1
+ *
+ *   public <init> ()V
+ *     ALOAD 0
+ *     INVOKESPECIAL java/lang/Object <init> ()V
+ *     RETURN
+ *     MAXSTACK = 1
+ *     MAXLOCALS = 1
+ *
+ * }
+ * 
+ *
+ * where Hello is defined by: + *

+ *

+ *
+ * public class Hello {
+ *
+ *   public static void main (String[] args) {
+ *     System.out.println("hello");
+ *   }
+ * }
+ * 
+ *
+ */ + +public class TraceClassVisitor extends PrintClassVisitor { + + /** + * The {@link ClassVisitor ClassVisitor} to which this visitor delegates + * calls. May be null. + */ + + protected final ClassVisitor cv; + + /** + * Prints a disassembled view of the given class to the standard output. + *

+ * Usage: TraceClassVisitor + * <fully qualified class name or class file name > + * + * @param args the command line arguments. + * + * @throws Exception if the class cannot be found, or if an IO exception + * occurs. + */ + + public static void main (final String[] args) throws Exception { + if (args.length == 0) { + System.err.println("Prints a disassembled view of the given class."); + System.err.println("Usage: TraceClassVisitor " + + ""); + System.exit(-1); + } + ClassReader cr; + if (args[0].endsWith(".class")) { + cr = new ClassReader(new FileInputStream(args[0])); + } else { + cr = new ClassReader(args[0]); + } + cr.accept(new TraceClassVisitor(null, new PrintWriter(System.out)), + PrintClassVisitor.DEFAULT_ATTRIBUTES, true); + } + + /** + * Constructs a new {@link TraceClassVisitor TraceClassVisitor} object. + * + * @param cv the class visitor to which this adapter must delegate calls. May + * be null. + * @param pw the print writer to be used to print the class. + */ + + public TraceClassVisitor (final ClassVisitor cv, final PrintWriter pw) { + super(pw); + this.cv = cv; + } + + public void visit ( + final int access, + final String name, + final String superName, + final String[] interfaces, + final String sourceFile) + { + buf.setLength(0); + if ((access & Constants.ACC_DEPRECATED) != 0) { + buf.append("// DEPRECATED\n"); + } + if (sourceFile != null) { + buf.append("// compiled from ").append(sourceFile).append("\n"); + } + buf.append("// access flags ").append(access).append("\n"); + appendAccess(access & ~Constants.ACC_SUPER); + if ((access & Constants.ACC_INTERFACE) != 0) { + buf.append("interface "); + } else if ((access & Constants.ACC_ENUM) != 0) { + buf.append("enum "); + } else { + buf.append("class "); + } + buf.append(name).append(" "); + if (superName != null && !superName.equals("java/lang/Object")) { + buf.append("extends ").append(superName).append(" "); + } + if (interfaces != null && interfaces.length > 0) { + buf.append("implements "); + for (int i = 0; i < interfaces.length; ++i) { + buf.append(interfaces[i]).append(" "); + } + } + buf.append("{\n\n"); + text.add(buf.toString()); + + if (cv != null) { + cv.visit(access, name, superName, interfaces, sourceFile); + } + } + + public void visitInnerClass ( + final String name, + final String outerName, + final String innerName, + final int access) + { + buf.setLength(0); + buf.append(" INNERCLASS ") + .append(name) + .append(" ") + .append(outerName) + .append(" ") + .append(innerName) + .append(" ") + .append(access) + .append("\n"); + text.add(buf.toString()); + + if (cv != null) { + cv.visitInnerClass(name, outerName, innerName, access); + } + } + + public void visitField ( + final int access, + final String name, + final String desc, + final Object value, + final Attribute attrs) + { + buf.setLength(0); + if ((access & Constants.ACC_DEPRECATED) != 0) { + buf.append(" // DEPRECATED\n"); + } + buf.append(" // access flags ").append(access).append("\n"); + buf.append(" "); + appendAccess(access); + if ((access & Constants.ACC_ENUM) != 0) { + buf.append("enum "); + } + buf.append(desc) + .append(" ") + .append(name); + if (value != null) { + buf.append(" = "); + if (value instanceof String) { + buf.append("\"").append(value).append("\""); + } else { + buf.append(value); + } + } + Attribute attr = attrs; + while (attr != null) { + buf.append(" FIELD ATTRIBUTE ").append(attr.type).append(" : ") + .append(attr.toString()).append("\n"); + attr = attr.next; + } + buf.append("\n"); + text.add(buf.toString()); + + if (cv != null) { + cv.visitField(access, name, desc, value, attrs); + } + } + + public CodeVisitor visitMethod ( + final int access, + final String name, + final String desc, + final String[] exceptions, + final Attribute attrs) + { + buf.setLength(0); + if ((access & Constants.ACC_DEPRECATED) != 0) { + buf.append(" // DEPRECATED\n"); + } + buf.append(" // access flags ").append(access).append("\n"); + buf.append(" "); + appendAccess(access); + if ((access & Constants.ACC_NATIVE) != 0) { + buf.append("native "); + } + if ((access & Constants.ACC_VARARGS) != 0) { + buf.append("varargs "); + } + if ((access & Constants.ACC_BRIDGE) != 0) { + buf.append("bridge "); + } + buf.append(name). + append(" "). + append(desc); + if (exceptions != null && exceptions.length > 0) { + buf.append(" throws "); + for (int i = 0; i < exceptions.length; ++i) { + buf.append(exceptions[i]).append(" "); + } + } + buf.append("\n"); + text.add(buf.toString()); + Attribute attr = attrs; + while (attr != null) { + buf.setLength(0); + buf.append(" METHOD ATTRIBUTE ").append(attr.type).append(" : ") + .append(attr.toString()).append("\n"); + text.add(buf.toString()); + attr = attr.next; + } + + CodeVisitor cv; + if (this.cv != null) { + cv = this.cv.visitMethod(access, name, desc, exceptions, attrs); + } else { + cv = null; + } + PrintCodeVisitor pcv = new TraceCodeVisitor(cv); + text.add(pcv.getText()); + return pcv; + } + + public void visitAttribute (final Attribute attr) { + buf.setLength(0); + buf.append(" CLASS ATTRIBUTE ").append(attr.type).append(" : ") + .append(attr.toString()).append("\n"); + text.add(buf.toString()); + + if (cv != null) { + cv.visitAttribute(attr); + } + } + + public void visitEnd () { + text.add("}\n"); + + if (cv != null) { + cv.visitEnd(); + } + + super.visitEnd(); + } + + /** + * Appends a string representation of the given access modifiers to {@link + * #buf buf}. + * + * @param access some access modifiers. + */ + + private void appendAccess (final int access) { + if ((access & Constants.ACC_PUBLIC) != 0) { + buf.append("public "); + } + if ((access & Constants.ACC_PRIVATE) != 0) { + buf.append("private "); + } + if ((access & Constants.ACC_PROTECTED) != 0) { + buf.append("protected "); + } + if ((access & Constants.ACC_FINAL) != 0) { + buf.append("final "); + } + if ((access & Constants.ACC_STATIC) != 0) { + buf.append("static "); + } + if ((access & Constants.ACC_SYNCHRONIZED) != 0) { + buf.append("synchronized "); + } + if ((access & Constants.ACC_VOLATILE) != 0) { + buf.append("volatile "); + } + if ((access & Constants.ACC_TRANSIENT) != 0) { + buf.append("transient "); + } + // if ((access & Constants.ACC_NATIVE) != 0) { + // buf.append("native "); + // } + if ((access & Constants.ACC_ABSTRACT) != 0) { + buf.append("abstract "); + } + if ((access & Constants.ACC_STRICT) != 0) { + buf.append("strictfp "); + } + } +} diff --git a/org/objectweb/asm/util/TraceCodeVisitor.java b/org/objectweb/asm/util/TraceCodeVisitor.java new file mode 100644 index 0000000000..cf954e0c4e --- /dev/null +++ b/org/objectweb/asm/util/TraceCodeVisitor.java @@ -0,0 +1,360 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000,2002,2003 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package org.objectweb.asm.util; + +import org.objectweb.asm.CodeVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.Attribute; + +import java.util.HashMap; + +/** + * A {@link PrintCodeVisitor PrintCodeVisitor} that prints a disassembled view + * of the code it visits. + */ + +public class TraceCodeVisitor extends PrintCodeVisitor { + + /** + * The {@link CodeVisitor CodeVisitor} to which this visitor delegates calls. + * May be null. + */ + + protected final CodeVisitor cv; + + /** + * The label names. This map associate String values to Label keys. + */ + + private final HashMap labelNames; + + /** + * Constructs a new {@link TraceCodeVisitor TraceCodeVisitor} object. + * + * @param cv the code visitor to which this adapter must delegate calls. May + * be null. + */ + + public TraceCodeVisitor (final CodeVisitor cv) { + this.cv = cv; + this.labelNames = new HashMap(); + } + + public void printInsn (final int opcode) { + buf.append(" ") + .append(OPCODES[opcode]) + .append("\n"); + + if (cv != null) { + cv.visitInsn(opcode); + } + } + + public void printIntInsn (final int opcode, final int operand) { + buf.append(" ") + .append(OPCODES[opcode]) + .append(" ").append(operand) + .append("\n"); + + if (cv != null) { + cv.visitIntInsn(opcode, operand); + } + } + + public void printVarInsn (final int opcode, final int var) { + buf.append(" ") + .append(OPCODES[opcode]) + .append(" ") + .append(var) + .append("\n"); + + if (cv != null) { + cv.visitVarInsn(opcode, var); + } + } + + public void printTypeInsn (final int opcode, final String desc) { + buf.append(" ") + .append(OPCODES[opcode]) + .append(" ") + .append(desc) + .append("\n"); + + if (cv != null) { + cv.visitTypeInsn(opcode, desc); + } + } + + public void printFieldInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + buf.append(" ") + .append(OPCODES[opcode]) + .append(" ") + .append(owner) + .append(" ") + .append(name) + .append(" ") + .append(desc) + .append("\n"); + + if (cv != null) { + cv.visitFieldInsn(opcode, owner, name, desc); + } + } + + public void printMethodInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + buf.append(" ") + .append(OPCODES[opcode]) + .append(" ") + .append(owner) + .append(" ") + .append(name) + .append(" ") + .append(desc) + .append("\n"); + + if (cv != null) { + cv.visitMethodInsn(opcode, owner, name, desc); + } + } + + public void printJumpInsn (final int opcode, final Label label) { + buf.append(" ") + .append(OPCODES[opcode]). + append(" "); + appendLabel(label); + buf.append("\n"); + + if (cv != null) { + cv.visitJumpInsn(opcode, label); + } + } + + public void printLabel (final Label label) { + buf.append(" "); + appendLabel(label); + buf.append(": // ").append(label).append("\n"); + + if (cv != null) { + cv.visitLabel(label); + } + } + + public void printLdcInsn (final Object cst) { + buf.append(" LDC "); + if (cst instanceof String) { + buf.append("\"").append(cst).append("\""); + } else { + buf.append(cst); + } + buf.append("\n"); + + if (cv != null) { + cv.visitLdcInsn(cst); + } + } + + public void printIincInsn (final int var, final int increment) { + buf.append(" IINC ") + .append(var) + .append(" ") + .append(increment) + .append("\n"); + + if (cv != null) { + cv.visitIincInsn(var, increment); + } + } + + public void printTableSwitchInsn ( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + buf.append(" TABLESWITCH\n"); + for (int i = 0; i < labels.length; ++i) { + buf.append(" ") + .append(min + i) + .append(": "); + appendLabel(labels[i]); + buf.append("\n"); + } + buf.append(" default: "); + appendLabel(dflt); + buf.append("\n"); + + if (cv != null) { + cv.visitTableSwitchInsn(min, max, dflt, labels); + } + } + + public void printLookupSwitchInsn ( + final Label dflt, + final int keys[], + final Label labels[]) + { + buf.append(" LOOKUPSWITCH\n"); + for (int i = 0; i < labels.length; ++i) { + buf.append(" ") + .append(keys[i]) + .append(": "); + appendLabel(labels[i]); + buf.append("\n"); + } + buf.append(" default: "); + appendLabel(dflt); + buf.append("\n"); + + if (cv != null) { + cv.visitLookupSwitchInsn(dflt, keys, labels); + } + } + + public void printMultiANewArrayInsn (final String desc, final int dims) { + buf.append(" MULTIANEWARRAY ") + .append(desc) + .append(" ") + .append(dims) + .append("\n"); + + if (cv != null) { + cv.visitMultiANewArrayInsn(desc, dims); + } + } + + public void printTryCatchBlock ( + final Label start, + final Label end, + final Label handler, + final String type) + { + buf.append(" TRYCATCHBLOCK "); + appendLabel(start); + buf.append(" "); + appendLabel(end); + buf.append(" "); + appendLabel(handler); + buf.append(" ") + .append(type) + .append("\n"); + + if (cv != null) { + cv.visitTryCatchBlock(start, end, handler, type); + } + } + + public void printMaxs (final int maxStack, final int maxLocals) { + buf.append(" MAXSTACK = ") + .append(maxStack) + .append("\n MAXLOCALS = ") + .append(maxLocals) + .append("\n\n"); + + if (cv != null) { + cv.visitMaxs(maxStack, maxLocals); + } + } + + public void printLocalVariable ( + final String name, + final String desc, + final Label start, + final Label end, + final int index) + { + buf.append(" LOCALVARIABLE ") + .append(name) + .append(" ") + .append(desc) + .append(" "); + appendLabel(start); + buf.append(" "); + appendLabel(end); + buf.append(" ") + .append(index) + .append("\n"); + + if (cv != null) { + cv.visitLocalVariable(name, desc, start, end, index); + } + } + + public void printLineNumber (final int line, final Label start) { + buf.append(" LINENUMBER ") + .append(line) + .append(" "); + appendLabel(start); + buf.append("\n"); + + if (cv != null) { + cv.visitLineNumber(line, start); + } + } + + public void printAttribute (final Attribute attr) { + buf.append(" CODE ATTRIBUTE ").append(attr.type).append(" : ") + .append(attr.toString()).append("\n"); + + if (cv != null) { + cv.visitAttribute(attr); + } + } + + /** + * Appends the name of the given label to {@link #buf buf}. Creates a new + * label name if the given label does not yet have one. + * + * @param l a label. + */ + + private void appendLabel (final Label l) { + String name = (String)labelNames.get(l); + if (name == null) { + name = "L" + labelNames.size(); + labelNames.put(l, name); + } + buf.append(name); + } +} diff --git a/version.factor b/version.factor new file mode 100644 index 0000000000..aa23256b50 --- /dev/null +++ b/version.factor @@ -0,0 +1 @@ +"0.29" @version